Recently I was helping someone really junior to write and run simple
I had some difficulties, as the last time I used Assembly to write something was around 16 years ago. Time to refresh the memory.
lldb for Mac)
Let’s create a simple
C wrapper program that will invoke external function, written with NASM:
Here we have defined an external function
asm_compute that will accept 3 numeric arguments.
Now we have to create a simple Assembly file in NASM to provide this
Here we defined a global function that we will export, so our C-wrapper will link properly.
Beware of the underscore before the function name
_asm_compute. As described in this comment on Stackoverflow, this behavior can be changed, but it is a good thing to follow conventions ;)
Nasm documentation states following:
Most 32-bit C compilers share the convention used by 16-bit compilers, that the names of all global symbols (functions or data) they define are formed by prefixing an underscore to the name as it appears in the C program. However, not all of them do: the ELF specification states that C symbols do not have a leading underscore on their assembly-language names.
Before we will dive into writing ASM code, let’s have a look at the Calling Conventions
This is important to understand, in order to make external calls possible. Caller and Callee should respect the same calling convention, in order to send arguments and receive return values.
We will use for our example x86-64 calling convention. Please note, that different platforms will implement this differently. For example, *nix will send integer arguments via
RCX and so on, while Windows will use
R9, and so on.
So in our case, the parameters would be passed through those three:
One other trick to understand how parameters were passed, is to disassemble
C code with
gcc -S, which will produce
file.s assembly, which format is not NASM, btw (so the operands are in reverse order!)
gcc -S num_calc.asm produces
Ok, so now we are pretty sure that the values go where expected, and the result is taken from
RAX register. Our codes should look like this:
Back to calculations
Let’s do some useful calculations here, like
(n1 * 8 + n2 / 4) * (n3 - 5)
One should always remember two things: size of the operand (8/16/32/64/.. bits) and if it is signed or unsigned. Proper Asm command should be used, in order to avoid unpredicted numbers.
Let’s break down our formula into easy calculations:
Ok, let’s just collect everything into one piece of code:
Time to compile everything back into one lovely executable:
If we were lucky enough, everything should be compiled into
num_calc executable, let’s run it:
Awesome, it runs. Just let’s remember that we used Integers, so we are only talking so far about whole numbers. No floating point calculations.
Continue reading here: Running NASM inside C inside GDB. Part 2. More arguments
Source code on github: nasm-c-gdb