nasm/ld“重新定位被截断以适应:R_386_16”

Dut*_*n18 6 assembly nasm ld bootloader x86-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
Run Code Online (Sandbox Code Playgroud)

组装这个:

$ nasm file.asm -felf -o file.o
Run Code Online (Sandbox Code Playgroud)

然后将其与:

$ ld -melf_i386 -o file.bin file.o --oformat binary
Run Code Online (Sandbox Code Playgroud)

给出以下错误:

file.asm:(.text+0x6): relocation truncated to fit: R_386_16 against `.text'
Run Code Online (Sandbox Code Playgroud)

在稍微摆弄代码后,我发现更改mov si, hwmov si, 0x100工作正常。但是标签有什么意义呢?

我的猜测是 ld 无法生成 16 位二进制文​​件,因此它替换hw为 32 位地址而不是 16 位地址。然后它会抱怨,因为我的程序试图将 32 位值放入 16 位寄存器中。

是否有一些参数我可以传递给 nasm/ld 来完成这项工作?


编辑:

elf 不支持 16 位,nasm 支持的唯一输出格式实际上声明它支持 16 位nasm -hf是 .obj,但我找不到它的链接器。


NASM 手册:

ELF32 规范不提供 8 位和 16 位值的重定位,但 GNU ld 链接器将这些作为扩展添加。NASM 可以生成与 GNU 兼容的重定位,以允许使用 GNU ld 将 16 位代码链接为 ELF。如果 NASM 与 -w+gnu-elf-extensions 选项一起使用,则在生成这些重定位之一时会发出警告。

添加-w+gnu-elf-extensions确实显示警告,但 ld 仍然给出相同的错误。

Mic*_*tch 6

首先,我建议您考虑使用i686 ELF 交叉编译器,以避免在您开发内核时会出现一些问题。


将 NASM 与 -f bin 输出选项一起使用

没有什么可以阻止您使用ELF作为NASM的目标文件类型,但使用-f bin生成完全解析的平面二进制文件的选项通常更简单,不需要修复。它可以用作引导扇区,无需任何链接步骤。不利的一面是所有代码都必须相同。外部汇编语句可以包含在%include指令中,类似于Cinclude指令。

为此,您必须将原点放置在汇编程序文件中,以便NASM知道生成绝对地址(用于标签等)所需的基本偏移量(原点)。您将修改您的汇编代码并将其添加到顶部:

[ORG 0x0000]
Run Code Online (Sandbox Code Playgroud)

这仅适用于使用-f bin输出选项时,此指令将引发其他输出类型的错误,例如-f elf. 在这种情况下,我们使用 0x0000 ,因为您的代码假定的段是 0x07c0 ,它被移动到DS 中。0x07c0:0x0000 映射到物理地址 (0x07c0<<4)+0x0000 = 0x07c00,这是我们的引导加载程序将被加载到内存的位置。

如果您不指定[org 0x0000],则 org = 0x0000 是使用-f bin输出选项时的默认值,因此实际上没有必要指定它。通过明确地使用它,它只是让读者更清楚。

为了将其组装成二进制文件,您可以执行以下操作:

nasm file.asm -fbin -o file.bin
Run Code Online (Sandbox Code Playgroud)

这将输出一个名为file.binassembled from的平面二进制文件file.asm。不需要链接步骤。


将 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
Run Code Online (Sandbox Code Playgroud)

-Ttext 0x0将是与您的代码匹配的原点。在这种情况下,0x0000 与您在ORG指令中使用的值相同,如果您将NASM-f bin输出选项一起使用。如果您编写的代码假设偏移量为 0x7c00,代码如下:

xor ax, ax     ; AX = 0
mov ds, ax     ; DS = 0
Run Code Online (Sandbox Code Playgroud)

然后必须指定TEXT段:

ld -melf_i386 -Ttext 0x7c00 -o file.bin file.o --oformat binary
Run Code Online (Sandbox Code Playgroud)

您的问题可能是:为什么我们需要为TEXT段的基数显式设置一个值?原因是LD的默认值取决于您的目标操作系统(通常是您当前运行的平台)。如果您使用的是 Linux,默认情况下LD将尝试为 Linux 创建输出。在 Linux 上,TEXT段开头的默认值通常是0x08048000在指定-m elf_i386. 这当然是一个 32 位值。

任何需要绝对地址的地方都会尝试添加0x08048000(或可能是其他一些大地址)。所以像这样的指令:

mov si, hw
Run Code Online (Sandbox Code Playgroud)

将尝试将 的地址移动hw到 16 位寄存器SI 中hw创建平面二进制输出文件时,链接器会尝试将其解析为 0x08048000 + 偏移量。因为您在仅采用 16 位值的指令中使用了 32 位值,所以您将收到警告/错误。LD会将 32 位值截断为 16 位,不幸的是,这可能会产生不正确的 16 位地址。