Reputation: 615
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
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