【问题标题】:GNU GAS: Label is not relatively referencedGNU GAS:标签没有被相对引用
【发布时间】:2015-02-26 11:01:34
【问题描述】:

我正在写一个小引导程序用于学习目的。

这是开机。S

.code16
.text
    movw    $0xB800, %ax    /* 0xB000 is the text screen video memory */
    movw    %ax, %es       /* set it as the %es segment */

    movb    label, %al
    movb    %al, %es:0x0    /* screen[0] = 'A' */
    movb    $0x07, %es:0x1  /* white on black */
    jmp .

label:  
    .byte 'A

    .=510
    .byte 0x55
    .byte 0xAA

这是我用来将其编译为原始二进制文件的 Makefile

hdd.img: boot.S
    as $< -o boot.o
    ld --oformat binary -Ttext 0x7c00 boot.o -o hdd.img

我面临标签没有相对引用的问题:加载字节'A'时,它使用绝对地址,比如0x7c14)。

所以我不能在运行时重新定位这个引导扇区(例如通过在内存中进一步复制它)。如果标签只是通过当前指令的偏移量来引用会更好。

这样可以吗?

【问题讨论】:

  • 您可以将-Ttext 设置为0,并确保从远处跳转到0x7c0:0。然后你可以将你的代码重新定位到任何段。

标签: assembly linker nasm boot gnu-assembler


【解决方案1】:

当然可以重定位字符串。

首先,您的-Ttext 0x7C00 是正确的。不要改变它。 在引导加载程序的开头,您应该将段寄存器归零(%ds%es%fs%gs%ss):

mov $0, %ax         // xor %ax, %ax would be more space efficient
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss

接下来,您应该设置一个堆栈。阅读:osdev wiki about stacks

现在回答您的问题: 通常使用segmentation%ds:%si 的形式对字符串进行寻址。重新定位引导加载程序时,只需正确更改%ds即可。
假设你调用你的字符串label:

mov $label, %si     // Always prefix a label address with a $

您可以使用lodsb 指令从字符串中获取字符(LOaD String Byte,它会自动为您增加 %si

lodsb               // Character is stored in `%al` now

另一个建议是不要手动寻址视频内存。使用BIOS Interrupt 0x10

祝你的引导加载程序好运!

【讨论】:

  • 如果我保留-Ttext 0x7C00,那么%si 将包含一个绝对地址,例如0x7C14。如果我在0x7C00(比如0x6000)之前重新定位引导加载程序,无论%ds 是什么,这都会是一个问题,不是吗?如果重定位是在0x7C00 之后,那么我将不得不以某种方式计算%si=0x7C14 和我的新重定位之间的差异,并将这个差异设置为%ds
  • 是的,%si 将包含一个绝对地址,但应该可以更改%ds,使其仍指向正确的点。 %si 相对于 %ds
  • 似乎无法在0x7C00 之前重新定位引导加载程序,因为%si 包含已经更大的地址
  • 在这种情况下,您必须将 %ds 设置为包含您的引导扇区的段(开始时为 0x7C00 >> 8 = 0x7C0,重定位后为 YOUR_ADDRESS >> 8)。像往常一样将标签的地址复制到%si,但减去引导加载程序开始的地址(见上文)。然后你可以再次使用段偏移寻址。
  • 我明白了。与-Ttext=0 一起工作似乎更少?有什么缺点吗?
猜你喜欢
  • 2018-09-13
  • 2018-09-16
  • 1970-01-01
  • 2011-07-11
  • 2011-02-28
  • 2011-02-01
  • 2020-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多