【问题标题】:relocation truncated to fit r_386_8 against .bss'重定位被截断以适应 r_386_8 反对 .bss'
【发布时间】:2025-12-26 14:25:07
【问题描述】:

当我输入 ld -m elf_i386 -o loop loop.asm 时,我得到标题中所述的错误,知道是什么原因造成的吗?对不起,如果代码看起来很糟糕,对汇编来说还很新。

cr equ 13 
lf equ 10 

section .bss
numA resb 1

section .text

global _start:

mov [numA],byte 0
call loop1
jmp endend
loop1:
xor cx,cx
mov al, $numA
cmp cx, 0x0A
jle else 
inc al
jmp end
else:
dec al
jmp end
end:
mov [$numA], al
inc cx
cmp cx,20
jle loop1

endend:
mov dl,$numA
mov ah,2
int 21h

【问题讨论】:

  • 您确定将.asm 文件输入到ld 中吗?我认为这行不通,您是说.o 吗?然后还显示nasm 命令,如何将.asm 组装成.o。您可能正在将 asm 编译为 64b 目标文件。为了获得我在 *buntu 上使用的完整 32b nasm->ld->二进制链:nasm -w+all test.asm -l test.lst -f elf32ld -m elf_i386 test.o -o test(对于test.asm)(如果是这种情况,这是许多“如何编译器”之一的副本64b linux 中的 32b asm”问题)
  • 错字,意思是loop.o,nasm命令:nasm -f elf -F dwarf -g loop.asm
  • 我试图编译你的源代码,它有实际的语义/语法问题,有些东西在 x86-32 汇编中没有多大意义。我将尝试猜测您想要实现的目标,并发布固定来源。 ...我有点特别困惑$numA 的意思,为什么那里有美元符号?我还没有在 NASM 源代码中看到,独立美元是指令的当前地址,所以像 $-numA 这样的东西对我来说意味着什么,但 $numA 对我来说是未知的。
  • “标识符也可以以 $ 为前缀,表示它旨在作为标识符而不是保留字来读取” 来自docs ...可惜我会忘记这一点,因为我从不使用保留字作为标识符,整洁的功能。
  • 我在网上找到了一些关于如何获取循环等的代码,试图学习汇编。程序只是运行一个循环,如果循环 10 时减少它。

标签: assembly nasm ld


【解决方案1】:

在 NASM 中,$numAnumA 相同。 A leading $ stops the assembler from considering it as a register name。因此,您可以编写mov eax, [$eax] 以从名为eax 的符号加载eax 寄存器。 (所以你可以链接到使用int eax = 123;的C)

所以mov [$numA], al 看起来很奇怪,但实际上它只是mov [numA], al,并不是错误的根源。


您收到来自mov dl,$numA 的错误,它对地址的低字节执行mov dl, imm8

链接器会警告您,因为 numA 的地址不适合 1 个字节,因此 r_386_8 重定位将不得不截断地址。

_8 告诉您这是一个重定位,它要求链接器填写 8 位(1 个字节)作为绝对地址。 (8 位相对分支位移具有不同的重定位类型,尽管通常您会使用 32 位位移来跳转到另一个文件中的符号。)

r_386 告诉您它是 i386 重定位,而不是某种类型的 r_x86_64 重定位(可以是绝对的或 RIP 相对的),或 MIPS 跳转目标重定位(需要右移偏移 2)。可能相关:Relocations in the System V gABI(通用 ABI,i386 SysV psABI 是其“处理器补充”)。

【讨论】:

  • 谢谢,已修复!快速跟进问题,endend 用于打印 numa,我是否需要将其转换为 ascii/反之亦然?
  • @malikaasen 是的,没有 DOS 或 linux system 调用来输出整数值。在 DOS 中,您至少可以输出单个 8b 扩展 ASCII 字符,在 linux 中,据我所知只有 sys_write 可用,它仅将存储在内存中的字节块作为输入。
  • @malikaasen:您可以链接 libc 并让 printf 为您完成。或者您可以自己将数字转换为字符串并打印出来。 (请参阅*.com/tags/x86/info 中的多位数字常见问题解答条目)。顺便说一句,您的代码中有一个int 21h,但是您正在制作一个 ELF 可执行文件。 Linux 根本没有实现 DOS int 21h 系统调用 ABI / API。见*.com/questions/2535989/…
  • 相关:Basic use of immediates vs. square brackets in YASM/NASM x86 assembly 之间的区别 mov ax, foo(地址转换为 16 位 reg = 错误)与 mov ax, [foo] 在标签处从内存中加载 16 位地址。
【解决方案2】:

cmets 开头的固定代码;* 关于我修改了什么:

;* build commands used to test:
;* nasm -f elf32 -F dwarf -g loop.asm -l loop.lst -w+all
;* ld -m elf_i386 -o loop loop.o

cr equ 13
lf equ 10

section .bss
numA resb 1

section .text

global _start   ;* global directive takes symbol name (without colon)

_start:
;* the actual label you defined as global, and want to start from.

    ;* set memory at numA address to byte zero
    mov [numA],byte 0
    ;* try to call subroutine with loop
    call loop1
    jmp endend

loop1:
    xor cx,cx       ;* loop counter = 0
.real_loop: 
;* you don't want to loop to "loop1" as that will reset CX!
    mov al, [$numA] ; load AL with value from memory at numA address
;* in NASM you must use [] to indicate memory load/store
;* the mov al, $numA tried to put the memory address numA into AL
;* but memory address in x86-32 is 32 bit value, and AL is 8 bit only
;* and you didn't want address, but value any way.
    cmp cx, 0x0A
    jle .else 
    inc al
    jmp .end
.else:
;* I modified all subroutine labels to be "local" starting with dot
;* i.e. ".else" is full label "loop1.else". This practice will allow
;* you to use also ".else" in different subroutines, while global
;* "else:" can be used only once per source file.
    dec al
    jmp .end
.end:
    mov [$numA], al
    inc cx
    cmp cx,20
    jle .real_loop      ;* fix of loop jump (to not reset CX)
    ;* after CX will reach value 21, the CPU will continue here
    ret         ;* so added return from subroutine

endend:
    ;* call linux 32b sys_exit(numA value) to terminate
    ;* return value will be equal to zero-extended [numA] to 32bits
    ;* 8bit -1 = 0xFF -> return value is 255
    movzx   ebx,byte [$numA]
    mov     eax,1
    int     80h

运行后:

nasm -f elf32 -F dwarf -g loop.asm -l loop.lst -w+all
ld -m elf_i386 -o loop loop.o
./loop ; echo $?

预期的输出:

255

【讨论】:

  • 非常感谢!评论很有​​用,帮助我学习,而不仅仅是复制。