Link C Program with Assembly Code in Unix/Linux

In systems programming, it is often necessary to access and control the hardware, such as CPU registers and I/O port locations, etc. In these situations, assembly code becomes necessary. It is therefore important to know how to link C programs with assembly code.

1. Programming in Assembly

 C code to Assembly Code

/************* a c file ********************/

#include <stdio.h>

extern int B();

int A(int x, int y)

{

int d, e, f;

d = 4; e = 5; f = 6;

f = B(d,e);

}

Explanations of the Assembly Code

The assembly code generated by GCC consists of three parts:

  • Entry: also called the prolog, which establishes stack frame, allocates local variables and working space on stack
  • Function body, which performs the function task with return value in AX register
  • Exit: also called the epilog, which deallocates stack space and return to caller

The GCC generated assembly code are explained below, along with the stack contents

The entry code first saves FP (%bp) on stack and let FP point at the saved FP of the caller. The stack contents become

Then it shift SP downward 24 bytes to allocate space for locals variables and working area.

While inside a function, FP points at a fixed location and acts as a base register for accessing local variables, as well as parameters. As can be seen, the 3 locals d, e, f, each 4 bytes long, are at the byte offsets -20, -16, -12 from FP. After assigning values to the local variables, the stack contents become

2. Implement Functions in Assembly

Example 1: Get CPU registers. Since these functions are simple, they do not need to establish and deallocate stack frames.

#============== s.s file ===============

.global get_esp, get_ebp

get_esp:

movl    %esp, %eax

ret

get_ebp:

movl    %ebp, %eax

ret

#======================================

int main()

{

int ebp, esp;

ebp = get_ebp();

esp = get_esp();

printf(“ebp=%8x esp=%8x\n”, ebp, esp);

}

Example 2: Assume int mysum(int x, int y) returns the sum of x and y. Write mysum() function in ASSEMBLY. Since the function must use its parameters to compute the sum, we show the entry, function body and exit parts of the function code.

# ============ mysum.s file ===================

.text                    # Code section

.global mysum, printf    # globals: export mysum, import printf

mysum:

# (1) Entry:(establish stack frame)

pushl %ebp

movl %esp, %ebp

# (2): Function Body Code of mysum: compute x+y in AX register

movl 8(%ebp), %eax      # AX = x

addl 12(%ebp), %eax     # AX += y

# (3) Exit Code: (deallocate stack space and return)

movl %ebp, %esp

pop %ebp

ret

# =========== end of mysum.s file ==========================

int main() # driver program to test mysum() function

{

int a,b,c;

a = 123;

b = 456;

c = mysum(a, b);

printf(“c=%d\n”, c);    // c should be 579

}

3. Call C functions from Assembly

Example 3: Access global variables and call printf()

int a, b; int main()

{

a = 100; b = 2 0 0;

sub();

}

#========== Assembly Code file ===========

.text

.global sub, a, b, printf

sub:

pushl %ebp

movl %esp, %ebp

pushl    b

pushl    a

pushl   $fmt         # push VALUE (address) of fmt

call    printf       # printf(fmt, a, b);

addl    $12, %esp

movl    %ebp, %esp

popl    %ebp

ret

.data

 fmt:     .asciz “a=%d    b=%d\n”

#========================================

Source: Wang K.C. (2018), Systems Programming in Unix/Linux, Springer; 1st ed. 2018 edition.

Leave a Reply

Your email address will not be published. Required fields are marked *