重定位被截断以适合:16 against `.text'
relocated truncation to fit: 16 against `.text'
希望你们今天过得愉快。我有一个关于将程序集编译成 .bin
的问题。我正在尝试使用 来修复它,但即使这样我也会得到 `relocated truncation to fit: 16 against '.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 [=10=]x0e, %ah
int [=10=]x10
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
<- 这个抛出错误
如有任何帮助,我们将不胜感激!
正如@jester 建议的那样,您需要设置一个虚拟内存地址 (VMA) 起点。您在 Windows 上使用的链接器使用了一个设置 VMA >= 0x10000 的内部链接器脚本。因此,任何对不适合 16 位的绝对地址的引用都会产生重定位错误。您无法将 >= 0x10000 的地址放入 16 位寄存器,因此链接器会中止并出现类似于以下内容的错误:
(.text+0x1f): relocation truncated to fit: R_386_16 against `.text'
如果您使用的是 64 位工具链,错误可能会略有不同,但它会类似于 R_???????_16 against '.text'
要解决此问题,您可以创建自己的链接描述文件或在 LD 命令行上将基本 VMA 设置为适当的值。我建议使用 -Ttext=0x7c00
并在引导加载程序中将 DS 设置为 0x0000。
我有 tips that discuss many of the problems writing a bootloader. You shouldn't assume that when your bootloader is running that the segment registers have any particular value. If you use -Ttext=0x7c00
as a VMA (ORG) then you need to set DS to zero. The segment:offset 对 0x0000:0x7c00 = 物理地址 0x07c00 (0x0000<<4+0x7c00)。 0x07c00 是传统 BIOS 将您的引导扇区加载到内存中的位置。
如果您遇到重定位错误,例如:
(.text+0x1f): relocation truncated to fit: R_386_16 against `.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 [=10=]x20,%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 [=10=]xe,%ah
18: cd 10 int [=10=]x10
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.
的引导加载程序下方
- 为了防止您的引导加载程序在最后一条指令后 运行宁半 运行dom 代码,您需要将处理器置于某种无限循环中。一个简单的方法是
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 [=12=]x0e, %ah
int [=12=]x10
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 [=12=]x7c00, %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
对于 assemble 和 运行 你可以使用类似的东西:
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 和 qemu-system-i386 -fda boot.bin
输出看起来类似于:
希望你们今天过得愉快。我有一个关于将程序集编译成 .bin
的问题。我正在尝试使用
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 [=10=]x0e, %ah
int [=10=]x10
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
<- 这个抛出错误
如有任何帮助,我们将不胜感激!
正如@jester 建议的那样,您需要设置一个虚拟内存地址 (VMA) 起点。您在 Windows 上使用的链接器使用了一个设置 VMA >= 0x10000 的内部链接器脚本。因此,任何对不适合 16 位的绝对地址的引用都会产生重定位错误。您无法将 >= 0x10000 的地址放入 16 位寄存器,因此链接器会中止并出现类似于以下内容的错误:
(.text+0x1f): relocation truncated to fit: R_386_16 against `.text'
如果您使用的是 64 位工具链,错误可能会略有不同,但它会类似于 R_???????_16 against '.text'
要解决此问题,您可以创建自己的链接描述文件或在 LD 命令行上将基本 VMA 设置为适当的值。我建议使用 -Ttext=0x7c00
并在引导加载程序中将 DS 设置为 0x0000。
我有 -Ttext=0x7c00
as a VMA (ORG) then you need to set DS to zero. The segment:offset 对 0x0000:0x7c00 = 物理地址 0x07c00 (0x0000<<4+0x7c00)。 0x07c00 是传统 BIOS 将您的引导扇区加载到内存中的位置。
如果您遇到重定位错误,例如:
(.text+0x1f): relocation truncated to fit: R_386_16 against `.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 [=10=]x20,%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 [=10=]xe,%ah 18: cd 10 int [=10=]x10 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. 的引导加载程序下方
- 为了防止您的引导加载程序在最后一条指令后 运行宁半 运行dom 代码,您需要将处理器置于某种无限循环中。一个简单的方法是
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 [=12=]x0e, %ah
int [=12=]x10
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 [=12=]x7c00, %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
对于 assemble 和 运行 你可以使用类似的东西:
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 和 qemu-system-i386 -fda boot.bin
输出看起来类似于: