'relocation truncated to fit r_386_8 against .bss'

When I try to build my source into a 32-bit static executable for Linux with

 nasm -f elf -F dwarf -g loop.asm
 ld -m elf_i386 -o loop loop.o

I get this R_386_8 error, any idea what causes it?

foo.o: in function `loop1':
foo.asm:(.text+0x12): relocation truncated to fit: R_386_8 against `.bss'
foo.o: in function `endend':
foo.asm:(.text+0x2f): relocation truncated to fit: R_386_8 against `.bss'

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           ; note: DOS system calls won't work in Linux

(Editor's note: this code has multiple bugs; this Q&A is primarily about the one preventing it from linking. But fixing that won't make a working Linux program.)



Solution 1:[1]

In NASM, $numA is the same as numA. A leading $ stops the assembler from considering it as a register name. Thus you can write mov eax, [$eax] to load the eax register from a symbol called eax. (So you could link with C which used int eax = 123;)

So mov [$numA], al looks weird, but it's really just mov [numA], al and isn't the source of the error.


You're getting the error from mov dl,$numA which does a mov dl, imm8 of the low byte of the address.

Most times, that's a case of Basic use of immediates vs. square brackets in YASM/NASM x86 assembly - where you meant to load from memory at that address, like movzx edx, byte [numA] or mov dl, [numA].


The linker warns you because the address of numA doesn't fit in 1 byte, so the r_386_8 relocation would have had to truncate the address.

The _8 tells you it's a relocation that asks the linker to fill in 8 bits (1 byte) as an absolute address. (8-bit relative branch displacements have a different relocation type, although normally you'd use a 32-bit displacement for jumping to a symbol in another file.)

The r_386 tells you it's an i386 relocation as opposed to some type of r_x86_64 relocation (which could be absolute or RIP-relative), or a MIPS jump-target relocation (which would need to right-shift the offset by 2). Possibly related: Relocations in the System V gABI (generic ABI, for which the i386 SysV psABI is a "processor supplement").

Solution 2:[2]

The fixed code with comments starting ;* about what did I modify:

;* 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

After running this:

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

The output is expected:

255

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Ped7g