Calling Printf from Assembly Language on 64Bit and 32Bit Architecture Using Nasm

How to link 32-bit Nasm assembly object code on a 64-bit windows computer

i386 architecture of input file `hello32.o' is incompatible with i386:x86-64 output

NASM has created a 32 bit object file, but you are trying to link a 64 bit executable. You could try to use the -m32 switch to create a 32 bit executable, but you already found out that this causes another bunch of errors. I do not have a solution for that either.

To link your executable, use a 32 bit MingW environment. I tried MinGW4.6.2 32 bit which worked well.
Alternatively, you can use the linker (link.exe) from a Microsoft Visual Studio installation.

https://github.com/afester/CodeSamples/tree/master/Asm/nasm_win32 shows a hello world example together with a Makefile which uses the Visual Studio linker. Alternatively, using gcc helloworld.obj -o hello32.exe from a MingW32 installation works also.

Issue with Linking a 32-bit NASM file on a 64-bit machine

I fixed this by installing libgcc32-4.8-dev. I had already installed gcc-multilib so perhaps the combination of the two was what I needed.

NASM printf print 64 bit double segfaults

On x86_64, arguments are passed in registers, and not on the stack (the stack is used only if the size of the arguments to too large to fit in the registers). All the gory details1 are laid out in the SYSV ABI for x86_64

The basics of that is that the first 6 integer/pointer arguments are passed in RDI/RSI/RDX/RCX/R8/R9, while the first 8 float/double arguments are passed in XMM0..XMM7. In addition, you need to specify the number of XMM registers used for arguments in AL2. So in your case, you want the format in RDI, the double value in XMM0 and 1 in AL

The wikipedia page also has lots of good (concise) info about this.


1For non-Microsoft systems -- MS being MS they do things in their own incompatible way

2You only actually need to set this for varargs functions that use at least one XMM register. For non-varargs functions it will be ignored, and if it is set too large for a varargs function, the result will be a few wasted cycles (saving uneeded XMM regs), but wont actually break anything.

Mac OS X 32-bit nasm assembly program using main and scanf/printf?

You're asking a lot of questions about your code, and you really don't understand the assembly code that's there.

Firstly, because of the way you're writing your code, the main routine is going to be the entry point of a C style program. Because of the way that mac os x linkages work; you're going to have to name it _main to match the name of the symbol being looked for by the linker as the default program entry point when it pulls in /usr/lib/crt1.o when producing the executable (if you do an nm of the file you'll see an entry like: U _main. Similarly, all the library routines start with a leading underscore so you have to use that prefix if you want to use them.

Secondly, the MAC OS calling convention requires a 16 byte alignment of the stack for all calls which means that you have to ensure that the stack pointer is aligned relevantly at each point. At the entry point of the main routine you already know that you're misaligned due to the return address being stored in the stack for returning from main. This means that if you want to make even a single call you're going to have to move the stack down by at least 12 bytes to make the call.

Armed with that piece of information, we're going to omit futzing around with the ebp, and just use esp exclusively for the purposes of the code.

This is assuming a prolog of:

bits 32
extern _printf
global _main

section .data
message db "Hello world!", 10, 0

section .text
_main:

On entry into _main, realign the stack:

sub esp, 12

Next we store the address of the message into the address pointed to by esp:

mov dword[esp], message

Then we call printf:

call _printf

Then we restore the stack:

add esp, 12

Set the return code for main, and return:

mov eax, 0
ret

The ABI for MAC OS X uses eax as the return code for the routine as long as it fits in a register. Once you've compiled and linked the code:

nasm -f macho -o test.o test.asm 
ld -o test -arch i386 test.o -macosx_version_min 10.7 -lc /usr/lib/crt1.o

It runs and prints the message, exiting with a 0.

Next we're going to play around with your scanning and printing example.

Firstly, scanf only scans, you can't have a prompt in there; it's simply not going to work, so you have to split the prompt from the scanning. We've already shown you how to do the print, and now what we need to show is the scanf.

Set up the variables in the data section:

scan_string     db  "%d", 0
limit dd 0

First store the address of scan_string in esp, and then store the address of limit in esp + 4, then call scanf:

mov dword[esp], scan_string
mov dword[esp + 4], limit
call _scanf

We now should have the value that we scanned stored in the limit memory location.

Next to print this message:

output_string   db  "Value %d", 10, 0

Next we put the address of output_string on the stack:

mov dword[esp], output_string

Read the value of the limit address into the eax register and put it into esp + 4 - i.e. the second parameter for printf:

mov eax, [limit]
mov dword[esp + 4], eax
call _printf

Next, we're calling exit, so we have to store the exit code in the stack and invoke the _exit function - this is different from the simple print variant as we're actually invoking exit, rather than simply returning.

mov dword[esp], 0
call _exit

As for some of the questions:

Why the alignment?

Because that's how Mac OS X does it

Why isn't push good enough?

It is, but we aligned the stack at the start of the routine and an aligned stack is a functioning stack, by pushing and popping you're messing with the alignment. This is one of the purposes behind using the ebp register rather than the esp register.

If we were to use the ebp register, the function prolog would look like:

push ebp
mov ebp, esp
sub esp, 8 ; in this case to obtain alignment

and the function epilog would look like:

add esp, 8
pop ebp

You can put in symmetric pusha/popa calls in there as well, but if you're not using the registers, why complicate the stack.

A better overview of the 32bit function calling mechanism is on the OS X Developer guide, The ABI function call guide gives the far more detail on the hows of parameter passing and returning. It's based on the AT&T System V ABI for the i386, with a few listed differences:

  • Different rules for returning structures
  • The stack is 16-byte aligned at the point of function calls
  • Large data types (larger than 4 bytes) are kept at their natural alignment
  • Most floating-point operations are carried out using the SSE unit instead of the x87 FPU, except when operating on long double values. (The IA-32 environment defaults to 64-bit internal precision for the x87 FPU.)

How to print a number in assembly NASM?

If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:

;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf

main:

mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret

message db "Register = %08X", 10, 0

Note that printf uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.



Related Topics



Leave a reply



Submit