Calling printf in x86_64 using GNU assembler
There are a number of issues with this code. The AMD64 System V ABI calling convention used by Linux requires a few things. It requires that just before a CALL that the stack be at least 16-byte (or 32-byte) aligned:
The end of the input argument area shall be aligned on a 16 (32, if __m256 is
passed on stack) byte boundary.
After the C runtime calls your main
function the stack is misaligned by 8 because the return pointer was placed on the stack by CALL. To realign to 16-byte boundary you can simply PUSH any general purpose register onto the stack and POP it off at the end.
The calling convention also requires that AL contain the number of vector registers used for a variable argument function:
%al is used to indicate the number of vector arguments passed to a function requiring a variable number of arguments
printf
is a variable argument function, so AL needs to be set. In this case you don't pass any parameters in a vector register so you can set AL to 0.
You also dereference the $format pointer when it is already an address. So this is wrong:
mov $format, %rbx
mov (%rbx), %rdi
This takes the address of format and places it in RBX. Then you take the 8 bytes at that address in RBX and place them in RDI. RDI needs to be a pointer to a string of characters, not the characters themselves. The two lines could be replaced with:
lea format(%rip), %rdi
This uses RIP Relative Addressing.
You should also NUL terminate your strings. Rather than use .ascii
you can use .asciz
on the x86 platform.
A working version of your program could look like:
# global data #
.data
format: .asciz "%d\n"
.text
.global main
main:
push %rbx
lea format(%rip), %rdi
mov $1, %esi # Writing to ESI zero extends to RSI.
xor %eax, %eax # Zeroing EAX is efficient way to clear AL.
call printf
pop %rbx
ret
Other Recommendations/Suggestions
You should also be aware from the 64-bit Linux ABI, that the calling convention also requires functions you write to honor the preservation of certain registers. The list of registers and whether they should preserved is as follows:
Any register that says Yes
in the Preserved across
function calls column are ones you must ensure are preserved across your function. Function main
is like any other C function.
If you have strings/data that you know will be read only you can place them in the .rodata
section with .section .rodata
rather than .data
In 64-bit mode: if you have a destination operand that is a 32-bit register, the CPU will zero extend the register across the entire 64-bit register. This can save bytes on the instruction encoding.
It is possible your executable is being compiled as position independent code. You may receive an error similar to:
relocation R_X86_64_PC32 against symbol `printf@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
To fix this you'll have to call the external function printf
this way:
call printf@plt
This calls the external library function via the Procedure Linkage Table (PLT)
assembly subroutines get called twice without even being called from main
int 80H
invokes the 32-bit system call interface, which a) uses the 32-bit system call numbers and b) is intended for use by 32-bit code, not 64-bit code. Your code is actually performing a umask
system call with random parameters.
For a 64-bit system call, use the syscall
instruction instead:
...
os_return:
mov rax, EXIT ; Linux system call 60 i.e. exit ()
mov rdi, 0 ; Error code 0 i.e. no errors
syscall ; Interrupt Linux kernel
...
Linux x86 NASM - Subroutine: Print a dword from EAX
I'm very rusty at assembler, but it looks like ECX contains the value 48, when it should contain the address of the buffer to be written.
I presume what you intended in print_eax_val
is to take the binary value of EAX, add the ASCII offset to digit 0
(which should have been 48, not 47) and then print that single character. To do this, add 48 before storing the value in to_print
, put the address of to_print
in ECX, and set the length (EDX) to 1, because you're writing only one character.
Now remember that this will work for EAX values between 0x0000 and 0x0009 only. When you go past 9 you will get other ASCII characters.
Explaining how to take an arbitrary binary value of EAX and convert it to a decimal string that can be printed is far beyond the scope of SO.
Problem with recursive factorial in x86_64 Assembly
You're returning the result of your factorial in rax
, but your caller is assuming that it is in rsi
. The caller should move the result from rax
to where it is needed (rsi
in this case) right after the call to factorial
returns.
Loop with printf in NASM
Several issues as mentioned in comments:
array resb 10
will reserve space for 10 bytes, but you want to store 10 dwords there (40 bytes). Change toarray resd 10
.(Pointed out by Sep Roland) In
_loop
you have an off-by-one bug; since theinc
is done before themov
you will access the dwords at[array+4], [array+8], ... [array+40]
, where the last one is out of range. This is like doingint array[10]; for (i=1; i <= 10; i++) array[i]=i;
in C, and is incorrect for exactly the same reason. One fix would be to domov [array + ecx * 4 - 4], ecx
instead.After
_loop2
you havejmp print
, which will transfer control toprint
and never come back. Since you apparently want to callprint
as a subroutine and continue executing withadd ecx, 1 ; cmp ecx, 10
, etc, you need tocall print
instead ofjmp
. And also uncomment theret
at the end ofprint
so that it will actually return. Subroutines in assembly language don't automatically return unless you actually executeret
; otherwise the CPU will just continue executing whatever garbage happens to be next in memory.You have a
push ecx
to save the value ofecx
before the call toprintf
, which is good sinceprintf
will overwrite that register, but you need topop ecx
afterwards to get that value back and put the stack back to where it was.Specifically, the
pop ecx
should follow theadd esp, 8
; a stack is a last-in-first-out structure, and thepush ecx
was before the pushing of theprintf
arguments, so you need topop ecx
after removing those arguments from the stack.The
mov eax, 0
as a return value at the end ofprint
is unnecessary since you never use it anywhere else.
With these changes the code works as it should.
Related Topics
Compile Git for 32-Bit Linux on Shared Hosting
Why Do I Get /Etc/Cups Conflicts Between Attempted Installs in Yocto
Asp Net Core Linux Err_Connection_Refused
Knowing If a Remote Port Forward Was Successful
Export_Symbol in Kernel Module | Undefined Symbol During Insmod
Using Gzip to Compress Files to Transfer with Aws Command
How to Ask Bash for the Current Options
How to Offload the 1Hz Tick in Dyntick Mode
How Come _Exit(0) (Exiting by Syscall) Prevents Me from Receiving Any Stdout Content
Reading Gnu-Screen Logs with Vim
Linux Zip and Exclude Dir via Bash/Shell Script
Can Someone Explain How This "Shellshock" Code Works in Shell