Foreign Function Interfaces

Example Library

The Source Files

// lib.h

#ifndef __LIB__
#define __LIB__

int add( int a, int b );

#endif
// lib.c

#include "lib.h"

int add( int a, int b )
{
  return a+b;
}
# Makefile

CC=gcc-12
CFLAGS=-Wall -Wextra -pedantic -std=c17
LDFLAGS=
LDLIBS=

DEPFILE=dep.mk
OBJ=lib.so
SRC=lib.c

.PHONY: all
all: $(OBJ)

.PHONY: clean
clean:
    $(RM) *.o
    $(RM) $(DEPFILE)
    $(RM) $(OBJ)

$(OBJ): $(SRC:%.c=%.o)
    $(CC) $(LDFLAGS) -shared -o $@ $(LDLIBS) $^

$(DEPFILE): $(SRC)
    $(CC) -o $@ -MM -MG $^

include $(DEPFILE)

Note

Most C++ compilers apply some form of name mangling. Thus, to get a shared object file exporting the add symbol as is it is important to use a strict C compiler (in this case gcc-12).

Building and Inspecting the Library

To build the shared object file we use the make tool.

$ make
gcc-12 -o dep.mk -MM -MG lib.c
gcc-12 -Wall -Wextra -pedantic -std=c17   -c -o lib.o lib.c
gcc-12  -shared -o lib.so  lib.o

To inspect the file type of the shared object file we use the file tool.

$ file lib.so
lib.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=7758d4c0a8d91c7033a6cd37bef86eb34330979c, not stripped

To list the symbols defined in the shared object file we use the nm tool. In the listing below, the -g flag filters the results so that only external symbols are listed. The -D flag displays dynamic symbols rather than normal symbols. We can see that the add symbol is indeed external.

$ nm -gD lib.so
00000000000010f9 T add
                 w __cxa_finalize
                 w __gmon_start__
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable

The readelf tool provides another way to investigate the symbols in a shared object file.

$ readelf --dyn-syms lib.so

Symbol table '.dynsym' contains 6 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_finalize
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 00000000000010f9    24 FUNC    GLOBAL DEFAULT   10 add

Foreign Functions in Racket

Reading

The Racket Foreign Interface

Inside: Racket C API

Basic Example

#lang racket/base

(require ffi/unsafe
         ffi/unsafe/define)

(define-ffi-definer define-lib (ffi-lib "lib.so"))

(define-lib add (_fun _int _int -> _int))
(add 1 2)