nasm/ld "relocation truncated to fit: R_386_16"
nasm/ld "relocation truncated to fit: R_386_16"
程序集:
[BITS 16]
global _start
_start:
mov ax, 0x07C0
mov ds, ax
mov si, hw
call print_string
jmp $
print_string:
mov ah, 0x0E
.char:
lodsb
cmp al, 0
je .exit
int 0x10
jmp .char
.exit: ret
times 0x100-($-$$) db 0
hw: db "Hello, World!", 0
times 510-($-$$) db 0
dw 0xAA55
将其组装为:
$ nasm file.asm -felf -o file.o
然后链接到:
$ ld -melf_i386 -o file.bin file.o --oformat binary
出现以下错误:
file.asm:(.text+0x6): relocation truncated to fit: R_386_16 against `.text'
稍微修改一下代码后,我发现将 mov si, hw
更改为 mov si, 0x100
效果很好。但是标签的意义何在?
我的猜测是 ld 不能生成 16 位二进制文件,因此它将 hw
替换为 32 位地址而不是 16 位地址。然后它抱怨,因为我的程序试图将 32 位值放入 16 位寄存器。
是否有一些参数我可以传递给 nasm/ld 以使其工作?
编辑:
elf 不支持 16 位,唯一的输出格式 nasm 支持,实际上它在 nasm -hf
中声明它支持 16 位是 .obj,但我找不到它的链接器.
NASM 手册:
The ELF32 specification doesn't provide relocations for 8- and 16-bit values, but the GNU ld linker adds these as an extension. NASM can generate GNU-compatible relocations, to allow 16-bit code to be linked as ELF using GNU ld. If NASM is used with the -w+gnu-elf-extensions option, a warning is issued when one of these relocations is generated.
添加-w+gnu-elf-extensions
确实显示警告,但ld仍然给出相同的错误。
我在这里找到了解决方案:Looking for 16-bit x86 compiler
something I learned the hard way;
-Ttext 0x0
is critical, otherwise the .text segment is pushed outside of 16bit addressing range (don't ask me why)
首先,我建议您考虑使用 i686 ELF Cross compiler 来避免一些以后在您开发内核时可能会咬到您的陷阱。
使用 NASM 和 -f bin 输出选项
没有什么可以阻止您使用 ELF 作为带有 NASM 的目标文件类型,但它通常更易于使用-f bin
选项生成完全解析的平面二进制文件,不需要修正。它可以用作引导扇区而无需任何链接步骤。不利的一面是所有代码都必须相同。外部 assembler 语句可以包含在 %include
指令中,类似于 C 的 include
指令。
为此,您必须将原点放在 assembler 文件中,以便 NASM 知道基准偏移量(原点) 需要生成绝对地址(用于标签等)。您将修改您的汇编代码并将其添加到顶部:
[ORG 0x0000]
这仅适用于使用 -f bin
输出选项时,此指令将抛出其他输出类型(如 -f elf
)的错误。在这种情况下,我们使用 0x0000,因为您的代码假定的段是 0x07c0,它已移入 DS。 0x07c0:0x0000 映射到物理地址 (0x07c0<<4)+0x0000 = 0x07c00,这是我们的引导加载程序将加载到内存中的位置。
如果您不指定 [org 0x0000]
,那么 org = 0x0000 是使用 -f bin
输出选项时的默认值,因此实际上没有必要指定它。它只是通过显式使用它使 reader 更清晰。
为了 assemble 把它变成一个二进制文件,你可以这样做:
nasm file.asm -fbin -o file.bin
这将从 file.asm
输出一个名为 file.bin
assembled 的平面二进制文件。不需要链接步骤。
使用 NASM 和 -f elf 输出选项
在您的示例中,您使用的是 ELF。这样做可能有几个原因。您生成的二进制文件可能是多个对象 (.o
) 文件的组合,或者您可能希望生成调试符号以与 GDB 等调试器一起使用。无论您出于何种原因,都可以使用以下命令完成此操作:
nasm file.asm -felf -o file.o
ld -melf_i386 -Ttext 0x0 -o file.bin file.o --oformat binary
-Ttext 0x0
将是与您的代码匹配的原点。在这种情况下,0x0000 与 ORG
指令使用的值相同,如果您将 NASM 与 -f bin
输出选项一起使用。如果您编写的代码假定偏移量为 0x7c00,代码如下:
xor ax, ax ; AX = 0
mov ds, ax ; DS = 0
然后 TEXT 段必须指定为:
ld -melf_i386 -Ttext 0x7c00 -o file.bin file.o --oformat binary
您的问题可能是:为什么我们需要明确地为TEXT段的基数设置一个值?原因是 LD 的默认值取决于您定位的 OS(通常针对您当前 运行 所在的平台)。如果您在 Linux,默认情况下 LD 将尝试为 Linux 创建输出。在 Linux 上,当指定 -m elf_i386
时,TEXT 段的默认开头通常是 0x08048000
。这当然是一个32位的值。
任何需要绝对地址的地方,它都会尝试向其添加 0x08048000
(或可能是其他一些大地址)。所以像这样的指令:
mov si, hw
会尝试将 hw
的地址移动到 16 位寄存器 SI 中。在创建平面二进制输出文件时,链接器会尝试将其解析为 0x08048000 + hw
的偏移量。因为您在一条仅采用 16 位值的指令中使用了 32 位值,所以您将得到 warning/error。 LD 会将 32 位值截断为 16 位,不幸的是,这可能会产生不正确的 16 位地址。
程序集:
[BITS 16]
global _start
_start:
mov ax, 0x07C0
mov ds, ax
mov si, hw
call print_string
jmp $
print_string:
mov ah, 0x0E
.char:
lodsb
cmp al, 0
je .exit
int 0x10
jmp .char
.exit: ret
times 0x100-($-$$) db 0
hw: db "Hello, World!", 0
times 510-($-$$) db 0
dw 0xAA55
将其组装为:
$ nasm file.asm -felf -o file.o
然后链接到:
$ ld -melf_i386 -o file.bin file.o --oformat binary
出现以下错误:
file.asm:(.text+0x6): relocation truncated to fit: R_386_16 against `.text'
稍微修改一下代码后,我发现将 mov si, hw
更改为 mov si, 0x100
效果很好。但是标签的意义何在?
我的猜测是 ld 不能生成 16 位二进制文件,因此它将 hw
替换为 32 位地址而不是 16 位地址。然后它抱怨,因为我的程序试图将 32 位值放入 16 位寄存器。
是否有一些参数我可以传递给 nasm/ld 以使其工作?
编辑:
elf 不支持 16 位,唯一的输出格式 nasm 支持,实际上它在 nasm -hf
中声明它支持 16 位是 .obj,但我找不到它的链接器.
NASM 手册:
The ELF32 specification doesn't provide relocations for 8- and 16-bit values, but the GNU ld linker adds these as an extension. NASM can generate GNU-compatible relocations, to allow 16-bit code to be linked as ELF using GNU ld. If NASM is used with the -w+gnu-elf-extensions option, a warning is issued when one of these relocations is generated.
添加-w+gnu-elf-extensions
确实显示警告,但ld仍然给出相同的错误。
我在这里找到了解决方案:Looking for 16-bit x86 compiler
something I learned the hard way; -Ttext 0x0 is critical, otherwise the .text segment is pushed outside of 16bit addressing range (don't ask me why)
首先,我建议您考虑使用 i686 ELF Cross compiler 来避免一些以后在您开发内核时可能会咬到您的陷阱。
使用 NASM 和 -f bin 输出选项
没有什么可以阻止您使用 ELF 作为带有 NASM 的目标文件类型,但它通常更易于使用-f bin
选项生成完全解析的平面二进制文件,不需要修正。它可以用作引导扇区而无需任何链接步骤。不利的一面是所有代码都必须相同。外部 assembler 语句可以包含在 %include
指令中,类似于 C 的 include
指令。
为此,您必须将原点放在 assembler 文件中,以便 NASM 知道基准偏移量(原点) 需要生成绝对地址(用于标签等)。您将修改您的汇编代码并将其添加到顶部:
[ORG 0x0000]
这仅适用于使用 -f bin
输出选项时,此指令将抛出其他输出类型(如 -f elf
)的错误。在这种情况下,我们使用 0x0000,因为您的代码假定的段是 0x07c0,它已移入 DS。 0x07c0:0x0000 映射到物理地址 (0x07c0<<4)+0x0000 = 0x07c00,这是我们的引导加载程序将加载到内存中的位置。
如果您不指定 [org 0x0000]
,那么 org = 0x0000 是使用 -f bin
输出选项时的默认值,因此实际上没有必要指定它。它只是通过显式使用它使 reader 更清晰。
为了 assemble 把它变成一个二进制文件,你可以这样做:
nasm file.asm -fbin -o file.bin
这将从 file.asm
输出一个名为 file.bin
assembled 的平面二进制文件。不需要链接步骤。
使用 NASM 和 -f elf 输出选项
在您的示例中,您使用的是 ELF。这样做可能有几个原因。您生成的二进制文件可能是多个对象 (.o
) 文件的组合,或者您可能希望生成调试符号以与 GDB 等调试器一起使用。无论您出于何种原因,都可以使用以下命令完成此操作:
nasm file.asm -felf -o file.o
ld -melf_i386 -Ttext 0x0 -o file.bin file.o --oformat binary
-Ttext 0x0
将是与您的代码匹配的原点。在这种情况下,0x0000 与 ORG
指令使用的值相同,如果您将 NASM 与 -f bin
输出选项一起使用。如果您编写的代码假定偏移量为 0x7c00,代码如下:
xor ax, ax ; AX = 0
mov ds, ax ; DS = 0
然后 TEXT 段必须指定为:
ld -melf_i386 -Ttext 0x7c00 -o file.bin file.o --oformat binary
您的问题可能是:为什么我们需要明确地为TEXT段的基数设置一个值?原因是 LD 的默认值取决于您定位的 OS(通常针对您当前 运行 所在的平台)。如果您在 Linux,默认情况下 LD 将尝试为 Linux 创建输出。在 Linux 上,当指定 -m elf_i386
时,TEXT 段的默认开头通常是 0x08048000
。这当然是一个32位的值。
任何需要绝对地址的地方,它都会尝试向其添加 0x08048000
(或可能是其他一些大地址)。所以像这样的指令:
mov si, hw
会尝试将 hw
的地址移动到 16 位寄存器 SI 中。在创建平面二进制输出文件时,链接器会尝试将其解析为 0x08048000 + hw
的偏移量。因为您在一条仅采用 16 位值的指令中使用了 32 位值,所以您将得到 warning/error。 LD 会将 32 位值截断为 16 位,不幸的是,这可能会产生不正确的 16 位地址。