How to Mix C and Assembly

C and Assembly can be used together to provide extra flexibility. You can create static libraries in Assembly that you call from C, and vice-versa, you can call C functions from within Assembly. Check out the code samples below that demonstrate that process.

It's actually slightly incorrect to say we are 'calling C from Assembly' or vice-versa because once the code is compiled, it is the same machine language. Whether you compile the object files from Assembly or from C they turn out the same. Once they are object files they can be called from anywhere, but they have to be defined as externals and linked. You could create static libraries as object files from other languages too. It doesn't just have to be Assembly and C.

Creating a Static Library with Assembly

The first step to calling an assembly function from C is to package it as a static library. Create a function and then compile the assembly source to an object file. We will link that later when we compile the c program. Save the assembly source here as say_hi.asm.

hello:     db 'Hello world!',10
helloLen:  equ $-hello

GLOBAL say_hi

mov eax,4            ; write()
mov ebx,1            ; STDOUT
mov ecx,hello
mov edx,helloLen
int 80h                 ; Interrupt
        ret                        ; Return control

Then compile the assembly source to an object file with

nasm -f elf64 say_hi.asm say_hi.o

Calling Assembly From C

/* example.c */
#include <stdio.h>

int main(int argc, char *argv[]) {
extern say_hi();

Next, compile and link the C program with gcc. Do that with

gcc example.c say_hi.o -o hello

You can then run the hello program that was just created.


Calling C from Assembly

You can call C functions from Assembly as well. This example calls printf(). In the main function, we push and pop the stack and put our operations in between. We move all the parameters in to the appropriate registers, and then we call the function.

; Define printf as an external function
extern printf

    msg: db "Hello world", 0 ; Zero is Null terminator
    fmt:    db "%s", 10, 0 ; printf format string follow by a newline(10) and a null terminator(0), "\n",'0'

    global main
    push rbp ; Push stack

    ; Set up parameters and call the C function
    mov rdi,fmt
    mov rsi,msg
    mov rax,0
    call printf

    pop rbp ; Pop stack

    mov rax,0 ; Exit code 0
    ret ; Return

Compile and run that with

nasm printf.asm -f elf64 -o printf.o
gcc printf.o