Assembly 使用链接器脚本中的位置计数器会影响哪些内存?
我学习组装是为了好玩,今天是我的第三天。 也许我误解了链接器脚本中的位置计数器。根据我的理解,位置计数器定义了内存中的哪个地址,这些部分必须加载到内存中(物理或虚拟) 然而,下面从中获取的链接器脚本似乎改变了结果图像(将幻数放入结果MBR图像的最后2个字节) link.ldAssembly 使用链接器脚本中的位置计数器会影响哪些内存?,assembly,ld,x86-16,bootloader,linker-scripts,Assembly,Ld,X86 16,Bootloader,Linker Scripts,我学习组装是为了好玩,今天是我的第三天。 也许我误解了链接器脚本中的位置计数器。根据我的理解,位置计数器定义了内存中的哪个地址,这些部分必须加载到内存中(物理或虚拟) 然而,下面从中获取的链接器脚本似乎改变了结果图像(将幻数放入结果MBR图像的最后2个字节) link.ld SECTIONS { /* The BIOS loads the code from the disk to this location. * We must tell that to the linker
SECTIONS
{
/* The BIOS loads the code from the disk to this location.
* We must tell that to the linker so that it can properly
* calculate the addresses of symbols we might jump to.
*/
. = 0x7c00;
.text :
{
__start = .;
*(.text)
/* Place the magic boot bytes at the end of the first 512 sector. */
. = 0x1FE;
SHORT(0xAA55)
}
}
我的代码是:
main.S
.code16
mov $msg, %si
mov $0x0e, %ah
loop:
lodsb
or %al, %al
jz halt
int $0x10
jmp loop
halt:
hlt
msg:
.asciz "hello world"
我组装并链接到:
as -g -o main.o main.S
ld --oformat binary -o main.img -T link.ld main.o
qemu-system-x86_64 -hda main.img
我很快意识到,formatbinary的选项--oformatbinary
与此有关,因为排除此选项不会创建512字节的图像。也许我应该寻找ELF和二进制格式?有人能帮我理解为什么使用二进制
格式,它是如何解释位置计数器的(因为它也应该对=0x7C00
做些什么)
生成的512字节hello world图像的Hexdump提供了以下信息:
我不理解的影响=0x7C00
在这里?信息丢失了吗?(可能不需要,因为GRUB无论如何都会在0x7C00加载它)
0x7C00您正在告诉链接器(顺便说一句,这不是汇编语言,不相关)。我希望下一件事是地址空间中的地址0x7C00(对于处理器)。它下面有.text,这意味着我们希望.text代码从地址0x7C00开始链接。因此,如果有任何具体的职位,那么它将基于该地址
__开始给我这一点的地址(在.text内)
*(.text)将所有的.text代码放在这里
=0x1FE将指针移动到.text中的0x1FE
SHORT(0xAA55)将这两个字节放在.text中的偏移量0x1Fe和0x1FF处
因此,假设代码适合,那么这将生成一个0x200字节的blob,该blob将在地址空间中的0x7C00处加载
现在,当您objcopy-O binary hello.elf hello.bin
该工具将查找第一个可加载的对象,输出文件的第一部分就是第一个可加载的对象。如果这是“二进制”中的唯一内容,则0x200字节将进入文件hello.bin
告诉您0x7C00是处理器需要查找的位置的信息以-O二进制文件格式丢失。小精灵有,其他人有,但那个没有
此外,如果在0x7C00处有这个0x200字节,而在0x8000处有另外两个字节,那么-O二进制输出的长度将是0x402字节。第一个0x200字节将来自0x7C00处的.text(可加载的最低值),然后是0x200字节的填充,这样相对于文件开头的下一个字节就位于正确的位置,如果您选择hello.bin并放置在0x7C00处,那么这两个字节将位于0x8000处
如果这些0x200位于0x7C00,并且要将另一项添加到链接器脚本中,0x02字节位于0x7000,那么hello.bin将以这两个字节开始,将有0xBFE字节的填充,然后是0x200字节的.text。因此,当bin文件在0x7000加载到内存中时,两个字节和0x200字节位于正确的位置
因此objcopy-O二进制文件本质上创建了需要加载的内容的内存映像,有时带有填充,但没有关于该加载的起始地址的信息。你必须知道
elf文件也将以某种形式包含0xAA55,我假设整个0x200字节是.text中显示的一个内容,但可能它将其分为两项。取决于创建elf的工具,以确定填充方式和填充方式。在执行符号解析时,链接过程中使用该信息。当然,它并没有显式地存储在原始二进制文件中,因为它没有头,也没有存储元数据的位置。我的理解可能是错误的,“输出部分地址”用于定义加载地址,加载地址以某种方式存储在二进制文件中,加载程序使用该二进制文件在定义的地址加载。请纠正我对“输出节地址”的理解,它适用于实际查阅二进制文件的加载程序,二进制文件中存储了此信息。原始二进制文件没有此信息,BIOS会自动将其加载到
0x7c00
,如您所说。需要它的唯一原因是执行链接,以便代码在该地址实际工作。“原始二进制文件没有此信息”,您的意思是使用格式二进制文件的生成的图像mov$msg,%si
使用绝对地址,因此它取决于org
aka=0x7c00
。您的jmp循环
指令使用了rel8
分支位移,因此它不会。其他说明不涉及地址。顺便说一句,我已经用实际使用的代码以及他用来构建的命令和工具更新了这个问题。它们恰好与他在问题中所链接的答案相同。当我跳过这一行时,=0x7c00
从链接器脚本,则文本部分应链接到地址0。使用--oformat binary
,应在输出图像中拾取第一个可加载文件。我得到了正确的512字节图像,但它没有打印“hello world”,而是打印了一些垃圾字符。我比较了删除这行之前和之后的hextump,我可以看到输出图像的前3个字节发生了变化。为什么会这样?为什么我得不到正确的“hello world”?因为你链接了0x0000,但它是在0x7C00加载的,所以代码正在抓取数据或跳转到不在正确地址的代码,请区分二进制文件以查看不同的字节,检查这两个二进制文件在这些位置的反汇编,您应该看到使用了具有特定地址的分支或数据访问,在一种情况下为0
00000000 bf 0f 7c b4 0e ac 08 c0 74 04 cd 10 eb f7 f4 68 |..|.....t......h|
00000010 65 6c 6c 6f 20 77 6f 72 6c 64 00 66 2e 0f 1f 84 |ello world.f....|
00000020 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 |.....f.........f|
00000030 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 |.........f......|
00000040 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f |...f.........f..|
00000050 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 |.......f........|
00000060 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 |.f.........f....|
00000070 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 |.....f.........f|
00000080 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 |.........f......|
00000090 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f |...f.........f..|
000000a0 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 |.......f........|
000000b0 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 |.f.........f....|
000000c0 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 |.....f.........f|
000000d0 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 |.........f......|
000000e0 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f |...f.........f..|
000000f0 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 |.......f........|
00000100 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 |.f.........f....|
00000110 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 |.....f.........f|
00000120 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 |.........f......|
00000130 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f |...f.........f..|
00000140 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 |.......f........|
00000150 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 |.f.........f....|
00000160 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 |.....f.........f|
00000170 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 |.........f......|
00000180 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f |...f.........f..|
00000190 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 |.......f........|
000001a0 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 |.f.........f....|
000001b0 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 |.....f.........f|
000001c0 2e 0f 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 |.........f......|
000001d0 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 66 2e 0f |...f.........f..|
000001e0 1f 84 00 00 00 00 00 66 2e 0f 1f 84 00 00 00 00 |.......f........|
000001f0 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 55 aa |.f............U.|
00000200
. = 0x7c00;
.text :
{
__start = .;
*(.text)
/* Place the magic boot bytes at the end of the first 512 sector. */
. = 0x1FE;
SHORT(0xAA55)
}