Call Printf System Subroutine to Output a Integer Number Error in Assembly Code

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:

Sample Image

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 to array resd 10.

  • (Pointed out by Sep Roland) In _loop you have an off-by-one bug; since the inc is done before the mov you will access the dwords at [array+4], [array+8], ... [array+40], where the last one is out of range. This is like doing int 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 do mov [array + ecx * 4 - 4], ecx instead.

  • After _loop2 you have jmp print, which will transfer control to print and never come back. Since you apparently want to call print as a subroutine and continue executing with add ecx, 1 ; cmp ecx, 10, etc, you need to call print instead of jmp. And also uncomment the ret at the end of print so that it will actually return. Subroutines in assembly language don't automatically return unless you actually execute ret; otherwise the CPU will just continue executing whatever garbage happens to be next in memory.

  • You have a push ecx to save the value of ecx before the call to printf, which is good since printf will overwrite that register, but you need to pop ecx afterwards to get that value back and put the stack back to where it was.

    Specifically, the pop ecx should follow the add esp, 8; a stack is a last-in-first-out structure, and the push ecx was before the pushing of the printf arguments, so you need to pop ecx after removing those arguments from the stack.

  • The mov eax, 0 as a return value at the end of print is unnecessary since you never use it anywhere else.

With these changes the code works as it should.



Related Topics



Leave a reply



Submit