Running NASM inside C inside GDB. Part 1. Integers
Recently I was helping someone really junior to write and run simple C
and NASM
programm.
I had some difficulties, as the last time I used Assembly to write something was around 16 years ago. Time to refresh the memory.
Tools required: nasm
, gcc
, gdb
(lldb
for Mac)
Numeric calculations
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 asm_compute
function.
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.
Calling Conventions
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 RDI
, RSI
, RDX
, RCX
and so on, while Windows will use RCX
, RDX
, R8
, R9
, and so on.
So in our case, the parameters would be passed through those three: RDI
: n1
, RSI
: n2
, RDX
: n3
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 num_calc.s
:
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)
We have here all basic operations: *
, /
, +
, -
.
There’s plenty documentation online, you can also check those cheat sheets: 1, 2, 3.
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:
Compile
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