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

时间:2016-01-25 14:41:16

标签: assembly linker nasm ld 16-bit

大会:

[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位二进制文​​件,因此它用32位地址而不是16位地址替换hw。然后它抱怨,因为我的程序试图将32位值放入16位寄存器。

是否有一些论据可以传递给nasm / ld以使其有效?

修改

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

NASM手册:

  

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

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

2 个答案:

答案 0 :(得分:3)

首先,我建议您考虑使用i686 ELF Cross compiler来避免在开发内核时稍后会咬你的一些问题。

将NASM与-f bin输出选项

一起使用

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

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

[ORG 0x0000]

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

如果您没有指定[org 0x0000],那么在使用-f bin输出选项时,org = 0x0000是默认设置,因此实际上并不需要指定它。通过明确地使用它,它使读者更清楚。

为了将它组装成二进制文件,你可以这样做:

nasm file.asm -fbin -o file.bin

这将从file.bin输出一个名为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

-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 的默认值取决于您要定位的操作系统(通常用于您当前运行的平台)。如果您使用的是Linux,默认情况下 LD 将尝试为Linux创建输出。在Linux上,指定0x08048000时, TEXT 段开头的默认值通常为-m elf_i386。这当然是32位值。

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

mov si, hw

尝试将hw的地址移动到16位寄存器 SI 中。在创建平面二进制输出文件时,链接器会尝试将此解析为0x08048000 + hw的偏移量。因为在一个只占用16位值的指令中使用了32位值,所以会出现警告/错误。 LD 会将32位值截断为16位,遗憾的是,这可能会产生错误的16位地址。

答案 1 :(得分:0)

我在这里找到了一个解决方案:Looking for 16-bit x86 compiler

  我学到了很多东西;   -Ttext 0x0   是至关重要的,否则.text段被推到16位寻址范围之外(不要问我为什么)