views:

130

answers:

3

I am working with a legacy C library that can be extended by writing user defined function(s) and then recompiling the source. I want to avoid the compilation requirement, and instead extend it ONCE with a function (see pseudocode below):

This function will be implemented like this:

VARIANT_TYPE CallSharedLibFunction(const char* library_name, const char* funcname, const char *params, const char* return_type){
// parse arguments and get data types and count
// initiate variable of data type indicated by return_type, to hold returned variable

/* this is the part I need help with */
// lptr = LoadSharedLibrary(library_name);
// funcptr = GetFunctionAddress(lptr, funcname);

// call function and pass it arguments
retvalue = funcptr(param1, param2, param3);

// wrap up returned value in the VARIANT_TYPE
VARIANT_TYPE ret;
setVariantValue(ret, retvalue, return_type);

return ret;

}

Note: despite the "Windows sounding" names (VARIANT_TYPE, LoadSharedLibrary and GetFunctionAddress), I am developing on Linux (Ubuntu 9.10). Ideally, I would like the library loading implementation to be cross platform (since I am using ANSI C code). But if I have to choose one platform, it will have to be the Linux platform.

I would be very grateful if anyone could shed some light on how I can invoke functions in arbitrary shared libraries (ideally, in a cross platform way - failing that, on Linux), so that I can implement the above function.

+2  A: 

For Linux/POSIX, you use the dlopen() family of functions to load shared libraries at runtime, look up symbol addresses, and so on.

If you want to add a library dependency to make working with loadable code a bit easier (and more portable), look into glib's module API.

unwind
+3  A: 

You might want to have a look at the dlopen, dlsym and similar functions. These work on POSIX (linux, OSX, win32+cygwin etc...).

With dlopen(), you can open a shared library. Your LoadSharedLibrary can be a wrapper around dlopen(). You GetFuncPtr() function can be a wrapper around dlsym(). What you can do is write code around the dl*() functions to make it robust - like do some error checking. You also might want to define an interface in the shared library, i.e. a struct that 'exports' supported functions. This way you can get a list of methods without the need to resorting to reading elf files.

There is also a nice page about function pointers in C and C++.

Here's a quick example on the usage:

void* LoadSharedLibrary(const char* name)
{
   return dlopen(name, RTLD_LOCAL | RTLD_LAZY);
}    

void* GetFunctionAddress(void* h, const char* name)
{
   return dlsym(h, name);
}

const char** GetFunctionList(void* h)
{
   return (char**)dlsym(h, "ExportedFunctions");
}

// Declare a variable to hold the function pointer we are going to retrieve.
// This function returns nothing (first void) and takes no parameters (second void).
// The * means we want a pointer to a function.
void (*doStuff)(void);

// Here we retrieve the function pointer from the dl.
doStuff = GetFunctionAddress(h, "doStuff");

// And this how we call it. It is a convention to call function pointers like this.
// But you can read it as 'take contents of the doStuff var and call that function'.
(*doStuff)();

[1]: http://manpages.ubuntu.com/manpages/hardy/man3/dlopen.3.html GetFunctionAddress

Johan
Hi Johan, this looks like what I'm looking for (shame I cant bump up your answer!).However, could you explain this, (its a bit cryptic to me):*(void**)((*doStuff)();
Stick it to THE MAN
I updated the answer which should make it all more clear.
Johan
A: 

use dlopen/dlsym as well as compile it with -fPIC / FPIC code

Kumar Vishal