Calling code from shared libraries in C is simple with dlopen / dlsym ( LoadLibrary on Windows). I provided a comprehensive example in the article on Plugins in C; here, I'll start with a simplified example.

Here's a sample C library compiled into libsomelib.so . First, the header file somelib.h :

#ifndef SOMELIB_H #define SOMELIB_H typedef struct { int num; double dnum; } DataPoint; DataPoint add_data( const DataPoint* dps, unsigned n); #endif /* SOMELIB_H */

And the implementation, somelib.c :

#include "somelib.h" DataPoint add_data ( const DataPoint* dps, unsigned n) { DataPoint out = {.num = 0 , .dnum = 0.0 }; for ( unsigned i = 0 ; i < n; ++i) { out.num += dps[i].num; out.dnum += dps[i].dnum; } return out; }

Dynamically loading libsomelib.so at runtime and calling add_data from C code is straightforward:

#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include "somelib.h" // Prototype for a function pointer for add_data typedef DataPoint (*add_data_fn_t)( const DataPoint* dps, unsigned n); int main ( int argc, const char * argv[]) { void * libhandle = dlopen( "./libsomelib.so" , RTLD_LAZY); if (!libhandle) { fprintf(stderr, "dlopen error: %s

" , dlerror()); exit( 1 ); } printf( "dlopen success: handle %p

" , libhandle); // We know the prototype of add_data so we can directly assign it to a // function pointer of the correct type. add_data_fn_t add_data_fn = dlsym(libhandle, "add_data" ); char * err = dlerror(); if (err) { fprintf(stderr, "dlsym failed: %s

" , err); exit( 1 ); } DataPoint dp[ 4 ] = {{ 2 , 2.2 }, { 3 , 3.3 }, { 4 , 4.4 }, { 5 , 5.5 }}; printf( "Calling add_data

" ); DataPoint dout = add_data_fn(dp, sizeof (dp) / sizeof (DataPoint)); printf( "dout = {%d, %lf}

" , dout.num, dout.dnum); return 0 ; }

This works great. However, note a certain lack of flexibility. While the shared library can be discovered and loaded at runtime, the interface of the function we call from it has to be defined statically, at compile time - this is the function pointer prototype in the snippet above.

But what if we want the interface to be dynamic as well? In other words, what if we don't know until runtime what arguments the called function accepts? Alas, if standard C is all we have, we're stuck. The problem is that to call a function properly, the compiler has to know what arguments it accepts to translate the call to the proper machine code sequence according to the system's calling convention. When I disassemble both add_data and the call in main , I see this call sequence, which is in accordance with the System V AMD64 ABI :

dps is passed in %rdi

is passed in n is passed in %esi

is passed in return value is in %xmm0

So to call a function whose signature is determined at runtime, we'd have to implement the calling convention ourselves, packing the arguments into registers and stack as appropriate and unpacking the return value. Moreover, this has to be implemented for each platform the code runs on. And it goes beyond saying that such code is not portable since standard C does not provide direct access to the stack or to the registers.

Luckily, a library exists that implements all of this for us.