【问题标题】:relocated truncation to fit: 16 against `.text'重定位被截断以适应:16反对`.text'
【发布时间】:2020-05-23 21:33:14
【问题描述】:

我希望你们今天过得愉快。我有一个关于将程序集编译成.bin 的问题。我正在尝试使用This article 来修复它,但即便如此,我也会得到`重新定位的截断以适应:16 对'.text'。

bootReal.s

#generate 16-bit code
.code16

#hint the assembler that here is the executable code located
.text
.globl _start;
#boot code entry
_start:
      jmp _boot                           #jump to boot code
      welcome: .asciz "Hello, World\n\r"  #here we define the string

     .macro mWriteString str              #macro which calls a function to print a string
          leaw  \str, %si
          call .writeStringIn
     .endm

     #function to print the string
     .writeStringIn:
          lodsb
          orb  %al, %al
          jz   .writeStringOut
          movb $0x0e, %ah
          int  $0x10
          jmp  .writeStringIn
     .writeStringOut:
     ret

_boot:
     mWriteString welcome

     #move to 510th byte from the start and append boot signature
     . = _start + 510
     .byte 0x55
     .byte 0xaa

我使用的命令:

as bootReal.s -o bootReal.s

ld.exe -o file.tmp bootReal.o

任何帮助将不胜感激!

【问题讨论】:

  • 使用ld -Ttext=0-Ttext=0x7c00 任您选择。确保设置 ds 以匹配。
  • @Jester ld -Ttext=0x7c00 也会抛出一个错误,其中-0x7c00 "不是文件或目录"
  • 您输入错误。可能在某个地方有额外的空间?或者尝试使用--image-base=0
  • @Jester :那是我的猜测......我假设他们写的是 -Ttext= 0x7c00 而不是 -Ttext=0x7c00
  • @fuz 错误在标题中。这是一个重定位错误。如果 LD 在 Windows 上使用的默认链接器脚本默认为 VMA (org) >=0x10000(几乎可以肯定是这种情况),几乎可以肯定是因为 leaw \str, %si

标签: assembly ld x86-16 bootloader gnu-assembler


【解决方案1】:

正如@jester 建议的那样,您需要设置一个虚拟内存地址(VMA)起点。您在 Windows 上使用的链接器使用了设置 VMA >= 0x10000 的内部链接器脚本。因此,任何对不适合 16 位的绝对地址的引用都会产生重定位错误。您不能将 >= 0x10000 的地址放入 16 位寄存器,因此链接器会中止并出现类似于以下内容的错误:

(.text+0x1f):重定位被截断以适应:R_386_16 对 `.text'

如果您使用的是 64 位工具链,错误可能会略有不同,但会类似于 R_???????_16 against '.text'

要解决此问题,您可以创建自己的链接器脚本或在 LD 命令行上将基本 VMA 设置为适当的值。我建议使用-Ttext=0x7c00 并在引导加载程序中将 DS 设置为 0x0000。

我有General Bootloader 提示,它们讨论了编写引导加载程序的许多问题。您不应该假设当您的引导加载程序运行时,段寄存器具有任何特定值。如果您使用 -Ttext=0x7c00 作为 VMA (ORG),则需要将 DS 设置为零。 segment:offset 对 0x0000:0x7c00 = 物理地址 0x07c00 (0x0000

如果您遇到如下重定位错误:

(.text+0x1f):重定位被截断以适应:R_386_16 对 `.text'

您始终可以使用 OBJDUMP 来显示目标文件中的重定位条目。在这种情况下,由于您正在编写 16 位代码,因此您需要让 OBJDUMP 转储代码 (-D);解码为 16 位指令 (-Mi8086) 并输出重定位条目 (-r)。 objdump -Mi8086 -Dr bootReal.o 的输出将与此类似(可能因使用的链接器而异):

00000000 <_start>:
   0:   eb 1b                   jmp    1d <_boot>

00000002 <welcome>:
   2:   48                      dec    %ax
   3:   65                      gs
   4:   6c                      insb   (%dx),%es:(%di)
   5:   6c                      insb   (%dx),%es:(%di)
   6:   6f                      outsw  %ds:(%si),(%dx)
   7:   2c 20                   sub    $0x20,%al
   9:   57                      push   %di
   a:   6f                      outsw  %ds:(%si),(%dx)
   b:   72 6c                   jb     79 <_boot+0x5c>
   d:   64 0a 0d                or     %fs:(%di),%cl
        ...

00000011 <.writeStringIn>:
  11:   ac                      lods   %ds:(%si),%al
  12:   08 c0                   or     %al,%al
  14:   74 06                   je     1c <.writeStringOut>
  16:   b4 0e                   mov    $0xe,%ah
  18:   cd 10                   int    $0x10
  1a:   eb f5                   jmp    11 <.writeStringIn>

0000001c <.writeStringOut>:
  1c:   c3                      ret

0000001d <_boot>:
  1d:   8d 36 02 00             lea    0x2,%si
                        1f: R_386_16    .text
  21:   e8 ed ff                call   11 <.writeStringIn>
        ...
 1fc:   00 00                   add    %al,(%bx,%si)
 1fe:   55                      push   %bp
 1ff:   aa                      stos   %al,%es:(%di)

在重定位错误.text+0x1f 被引用为问题的根源。如果您查看 OBJDUMP 输出,则有一个重定位条目:

  1d:   8d 36 02 00             lea    0x2,%si
                        1f: R_386_16    .text

此重定位与上面的指令相关联。这实际上意味着链接器尝试为LEA 指令生成偏移量,但该值无法以 16 位值表示。 SI 是 16 位寄存器,不能在其中放置值 >= 0x10000。


代码问题

  • 如果使用 0x7c00 的 VMA (ORG),您希望将 DS 正确设置为 0
  • 确保像lodsb 这样的字符串指令在字符串中向前移动。使用CLD 指令清除方向标志(DF=0)。
  • 通常最好设置自己的堆栈指针SS:SP。如果您曾经将更多数据从磁盘读取到内存中,这一点很重要。你不知道 BIOS 设置在哪里 SS:SP 并且你不想破坏它。设置堆栈的方便位置位于引导加载程序下方 0x0000:0x7c00。
  • 为了防止引导加载程序在最后一条指令之后运行半随机代码,您需要将处理器置于某种无限循环中。一个简单的方法是jmp .
  • LD 将生成一个非二进制文件格式的可执行文件,并且不能作为引导加载程序运行。您可以让链接器使用--oformat=binary 选项写入二进制文件。

修改后的代码可能如下所示:

#generate 16-bit code
.code16

#hint the assembler that here is the executable code located
.text
.globl _start;
#boot code entry
_start:
      jmp _boot                           #jump to boot code
      welcome: .asciz "Hello, World\n\r"  #here we define the string

     .macro mWriteString str              #macro which calls a function to print a string
          leaw  \str, %si
          call .writeStringIn
     .endm

     #function to print the string
     .writeStringIn:
          lodsb
          orb  %al, %al
          jz   .writeStringOut
          movb $0x0e, %ah
          int  $0x10
          jmp  .writeStringIn
     .writeStringOut:
     ret

_boot:
     xor %ax, %ax
     mov %ax, %ds      # Initialize the DS segment to zero
     mov %ax, %ss      # Set the stack pointer below bootloader at 0x0000:0x7c00
     mov $0x7c00, %sp
     cld               # Clear Direction Flag (DF=0) to set forward movement on string
                       # instructions

     mWriteString welcome
     jmp .             # Enter an infinite loop to stop executing code beyond this point

     #move to 510th byte from the start and append boot signature
     . = _start + 510
     .byte 0x55
     .byte 0xaa

要组装和运行,你可以使用类似的东西:

as bootReal.s -o bootReal.o
ld -Ttext=0x7c00 --oformat=binary -o boot.bin bootReal.o

另一种方法是使用 LD 生成可执行文件并使用 OBJCOPY 将可执行文件转换为二进制文件:

as bootReal.s -o bootReal.o
ld -Ttext=0x7c00 -o file.tmp bootReal.o
objcopy -O binary file.tmp boot.bin

无论哪种方式都应该生成一个名为 boot.bin 的 512 字节二进制文件(引导加载程序)。如果您使用带有qemu-system-i386 -fda boot.bin 的 QEMU 运行它,输出将类似于:

【讨论】:

    猜你喜欢
    • 2018-04-06
    • 2013-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-01
    • 2016-04-03
    • 2015-10-20
    相关资源
    最近更新 更多