Say hello to x86_64 Assembly [part 7]

Interaction C with Assembly


It is seventh part of Say hello to x86_64 Assembly and here we will look on how we can use C together with assembler.

Actually we have 3 ways to use it together:
  • Call assembly routines from C code
  • Call c routines from assembly code
  • Use inline assembly in C code
Let's write 3 simple Hello world programs which shows us how to use assembly and C together.

Call assembly from C


First of all let's write simple C program like this:

Here we can see C code which defines two variables: our Hello world string which we will write to stdout and length of this string. Next we call printHelloWorld assembly function with this 2 variables as parameters. As we use x86_64 Linux, we must know x86_64 linux calling convetions, so we will know how to write printHelloWorld function, how to get incoming parameters and etc... When we call function first six parameters passes through rdi, rsi, rdx, rcx, r8 and r9 general purpose registers, all another through the stack. So we can get first and second parameter from rdi and rsi registers and call write syscall and than return from function with ret instruction:

Now we can build it with:

And run it.

Inline assembly


The following method is to write assembly code directly in C code. There is special syntax for this. It has general view:
asm [volatile] ("assembly code" : output operand : input operand : clobbers);
As we can read in gcc documentation volatile keyword means:
The typical use of Extended asm statements is to manipulate input values to produce output values. However, your asm statements may also produce side effects. If so, you may need to use the volatile qualifier to disable certain optimizations
Each operand is described by constraint string followed by C expression in parentheses. There are a number of constraints:
  • r - Kept variable value in general purpose register
  • g - Any register, memory or immediate integer operand is allowed, except for registers that are not general registers.
  • f - Floating point register
  • m - A memory operand is allowed, with any kind of address that the machine supports in general.
  • and etc...
So our hello world will be:

Here we can see the same 2 variables as in previous example and inline assembly definition. First of all we put 1 to rax and rdi registers (write system call number, and stdout) as we did it in our plain assembly hello world. Next we do similar operation with rsi and rdi registers but first operands starts with % symbol instead $. It means str is the output operand referred by %1 and len second output operand referred by %2, so we put values of str and len to rsi and rdi with %n notation, where n is number of output operand. Also there is %% prefixed to the register name.
This helps GCC to distinguish between the operands and registers. operands have a single % as prefix
We can build it with:

and as previous example. Full documentation about GCC inline assembly you can find - here.

Call C from assembly


And the last method is to call C function from assembly code. For example we have following simple C code with one function which just prints Hello world:

Now we can define this function as extern in our assembly code and call it with call instruction as we do it much times in previous posts:

Build it with:

and now we can run our third hello world.

Conclusion


It was a seventh part of series 'say hello to x64 assembly'. Here we saw how to use assembly and C code together. if you will have a questions/suggestions write me a comment or ping me at twitter for discussing. If you're interesting in some additional themes about assembly and x86_64 programming write me a comment and I will try to write blog post about it in near time.

All another parts you can find - here.

All source code you can find as every time - here.

English is not my first language, so you'll find mistakes in my blog post. Please tell me in the comments or drop me an email.

Comments