2
votes

When i type ld -m elf_i386 -o loop loop.asm, i get the error stated in the title, any idea what causes it? Sorry if the code looks bad, fairly new to assembly.

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
2
are you sure you input .asm file into ld? I don't think that can work, did you meant .o? Then show also the nasm command, how you assemble the .asm into .o. You are probably compiling the asm into 64b object file. To get full 32b nasm->ld->binary chain I'm using on *buntu: nasm -w+all test.asm -l test.lst -f elf32 ld -m elf_i386 test.o -o test (for test.asm) (if that is the case, this is duplicate of one of many "how to compiler 32b asm in 64b linux" questions) - Ped7g
Typo, meant loop.o, nasm command: nasm -f elf -F dwarf -g loop.asm - malik aasen
I tried to compile your source, and it has actual semantic/syntax problems, some things don't make much sense in x86-32 assembly. I will try to guess what you did want to achieve, and post fixed source. ... I'm sort of particularly confused what you mean by $numA, why the dollar sign there? I didn't see that in NASM source yet, the stand-alone dollar is current address of instruction, so something like $-numA would mean something to me, but $numA is unknown to me. - Ped7g
"An identifier may also be prefixed with a $ to indicate that it is intended to be read as an identifier and not a reserved word" from docs ... pity I will forgot that, as I instead never use reserved words for identifiers, neat feature. - Ped7g
I found some code online on how to get loops and such, trying to learn assembly. the program just runs a loop and adds 1 to numa if loop < 10 and decreases it if loop is => 10. - malik aasen

2 Answers

5
votes

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.

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").

2
votes

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