SpilledMango
SpilledMango

Reputation: 615

Assembly number change error

I tried to make a assembly program that took one number, and then printed it. But when I wrote 1, it wrote 4171. Why, and how can I fix it? I am using 64 bit assembly, I assembled it with nasm -f macho64 scanf.asm, and I linked it with gcc -o scanf scanf.o -Wl,-no-pie. And I've tried to use square brackets around number in the scan macro (for a pointer to the varible number), but nasm mach-o didn't allow it, so I didn't do that.

Here is my code:

; lib.asm
extern _printf
extern _scanf

%macro print 2

    push rbp

    mov rdi, %1
    mov rsi, %2

    xor rax, rax

    call _printf

    pop rbp

%endmacro

%macro scan 2

    push rbp

    mov rdi, %1
    mov rsi, %2

    mov rax, 0
    call _scanf

    pop rbp

%endmacro

; scanf.asm

%INCLUDE "lib.asm"

section .bss
    number resd 1
section .text
    global _main
_main:

    print str_out, qst
    scan int_in, number

    print ans, number

exit:

    mov rax, 0x2000001
    xor rbx, rbx
    syscall

section .data
    str_out db '%s', 0xa, 0
    int_in db '%d', 0
    int_out db '%d', 0xa, 0


    qst db 'Enter a number: ', 0

    ans db 'You wrote: %d', 0xa, 0

Upvotes: 1

Views: 202

Answers (1)

Michael Petch
Michael Petch

Reputation: 47573

The macro to print the answer is incorrect.

print ans, number

When the macro is expanded you'd get:

push rbp

mov rdi, ans      ; RDI = Address of ans
mov rsi, number   ; RSI = Address of number

xor rax, rax

call _printf

pop rbp

I have commented the 2 lines above and their meaning. A format string of 'You wrote: %d' says to print an integer. You have passed it the address of number instead. To resolve this get the 32-bit value from number and place it in an available register and pass that register to your macro.

To do this you can replace:

print ans, number

With:

mov eax, [rel number] ; Get the 32-bit number from number and store in eax
                      ; Using RIP relative addressing 
print ans, rax        ; Print the 32-bit word in the bottom half of RAX.

That would expand to:

push rbp

mov rdi, ans      ; RDI = Address of ans
mov rsi, rax      ; RSI = number to print in RAX

xor rax, rax

call _printf

pop rbp

Note: We use mov eax, [rel number] with the rel directive to change the default absolute address behavior to RIP relative addressing. If you wish to use mov eax, [number] and avoid having to specify rel you can override the default addressing and place default rel at the top of your assembly file. The default is default abs.


You may ask why I suggested:

mov eax, [rel number]   ; Get the 32-bit number from number and store in eax
                        ; Using RIP relative addressing 
print ans, rax          ; Print the 32-bit word in the bottom half of RAX.

Rather than:

print ans, [rel number]

Ultimately the macro would have attempted to do this:

mov rsi, [rel number]   ; Get the 64-bit number from number and store in RDI

You defined number this way:

number resd 1

This is a 32-bit value, not 64-bit. mov rsi, [rel number] would have attempted to move 8 bytes from the memory location number into RSI. This will likely cause no issues in your sample code but it is considered bad form. I consider it a bug despite the fact that it may work.

Upvotes: 2

Related Questions