在链接描述文件中使用位置计数器会影响哪些内存?

What memory is impacted using the location counter in linker script?

我学习汇编是为了好玩,今天是我的第三天。 也许我误解了 linker 脚本中的位置计数器。根据我的理解,位置计数器定义内存中的哪个地址,这些部分必须加载到内存中(物理或虚拟)。

但是,从 this SO post 中提取的以下 linker 脚本似乎改变了生成的图像(将幻数放在生成的 MBR 图像的最后 2 个字节中)。

link.ld

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 [=11=]x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int [=11=]x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"

我 assemble 和 link 与:

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

我很快意识到选项 --oformat binary 必须对此做一些事情,因为排除它不会创建 512 字节的图像。也许我应该寻找 ELF 与二进制格式?有人可以帮我理解为什么使用 binary 格式,它如何解释位置计数器(因为它也应该用 . = 0x7C00 做一些事情)?

得到的 512 字节 hello world 图像的 Hexdump 给我这个:

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这里的影响?那个信息丢失了吗? (可能不需要,因为 GRUB 无论如何都会在 0x7C00 加载它)

. = 0x7c00;
.text :
{
    __start = .;
    *(.text)
    /* Place the magic boot bytes at the end of the first 512 sector. */
    . = 0x1FE;
    SHORT(0xAA55)
}

0x7C00 你在告诉链接器(顺便说一句,这不是汇编语言,不相关)。我希望下一件事位于地址 space 中的地址 0x7C00(对于处理器)。 .text 在它下面,这意味着我们希望 .text 代码从地址 0x7C00 开始链接。因此,如果有任何特定位置,那么它将基于该地址。

__start 给我地址(在 .text 内)

*(.text) 将所有.text 代码放在这里

。 = 0x1FE 将指针移动到 .text

中的 0x1FE

SHORT(0xAA55) 将这两个字节放在 .text 中的偏移量 0x1Fe 和 0x1FF 处

因此,假设代码合适,那么这将生成一个 0x200 字节的 blob,该 blob 将加载到地址 space 中的 0x7C00。

现在当你 objcopy -O binary hello.elf hello.bin

该工具将寻找第一个可加载的东西,输出文件的第一部分就是第一个可加载的东西。如果这是“二进制文件”中唯一的内容,那么 0x200 字节将转到文件 hello.bin.

告诉您 0x7C00 是处理器需要找到它的位置的信息在 -O 二进制文件格式中丢失了。小精灵有,别人有,那个没有。

此外,如果您在 0x7C00 处有这个 0x200 字节,并且在 0x8000 处有另外 2 个字节,那么 -O 二进制输出将为 0x402 字节长。第一个 0x200 字节将来自 .text 的 0x7C00 最低可加载的东西,然后是 0x200 字节的填充,以便相对于文件开头的下一个字节位于正确的位置,如果你要采取 hello.bin并放在 0x7c00 处,那么这两个字节将位于 0x8000 处。

如果您在 0x7C00 处有这些 0x200,并且要在 0x7000 处向链接器脚本添加另一个 0x02 字节的项目,那么 hello.bin 将从这两个字节开始,然后是 0xBFE 字节的填充,然后是 0x200 字节的.text。这样当 bin 文件加载到内存中的 0x7000 时,两个字节和 0x200 字节就在正确的位置。

所以 objcopy -O binary 本质上创建了一个需要加载的内存映像,有时带有填充,但没有关于该加载的起始地址的信息。你必须知道。

elf 文件也将包含某种形式的 0xAA55,我假设整个 0x200 字节是 .text 中显示的一个东西,但也许它把它分成了两个项目。填充方式和填充内容取决于创建小精灵的工具。