tags:

views:

371

answers:

3

When loaded a shared library is opened via the function dlopen(), is there a way for it to call functions in main program?

+3  A: 

Yes, If you provide your library a pointer to that function, I'm sure the library will be able to run/execute the function in the main program.

Here is an example, haven't compiled it so beware ;)

/* in main app */

/* define your function */

int do_it( char arg1, char arg2);

int do_it( char arg1, char arg2){
  /* do it! */
  return 1;
}

/* some where else in main app (init maybe?) provide the pointer */
 LIB_set_do_it(&do_it);
/** END MAIN CODE ***/

/* in LIBRARY */

int (*LIB_do_it_ptr)(char, char) = NULL;

void LIB_set_do_it( int (*do_it_ptr)(char, char) ){
    LIB_do_it_ptr = do_it_ptr;
}

int LIB_do_it(){
  char arg1, arg2;

  /* do something to the args 
  ...
  ... */

  return LIB_do_it_ptr( arg1, arg2);
}
hhafez
do_it_ptr takes a pointer to a function that expects 3 char arguments; you assign function pointers for functions that just take 2 char arguments. The extern declaration for doit() is hardly needed. The do_it_ptr is not needed; you can just pass do_it by name where you currently pass do_it_ptr. Etc!
Jonathan Leffler
that's correct :) in fact you could also get rid of the LIB_get_it() and just define a new LIB_do_it( int (*do_it_ptr)(char, char, char) ) { return do_it_ptr( arg1, arg2, arg3 ) }
hhafez
+6  A: 

Code of dlo.c (the lib):

#include <stdio.h>

// function is defined in main program
void callb(void);

void test(void) {
    printf("here, in lib\n");
    callb();
}

Compile with

gcc -shared -olibdlo.so dlo.c

Here the code of the main program (copied from dlopen manpage, and adjusted):

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void callb(void) {
    printf("here, i'm back\n");
}

int
main(int argc, char **argv)
{
    void *handle;
    void (*test)(void);
    char *error;

    handle = dlopen("libdlo.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    dlerror();    /* Clear any existing error */

    *(void **) (&test) = dlsym(handle, "test");

    if ((error = dlerror()) != NULL)  {
        fprintf(stderr, "%s\n", error);
        exit(EXIT_FAILURE);
    }

    (*test)();
    dlclose(handle);
    exit(EXIT_SUCCESS);
}

Build with

gcc -ldl -rdynamic main.c

Output:

[js@HOST2 dlopen]$ LD_LIBRARY_PATH=. ./a.out
here, in lib
here, i'm back
[js@HOST2 dlopen]$

The -rdynamic option puts all symbols in the dynamic symbol table (which is mapped into memory), not only the names of the used symbols. Read further about it here. Of course you can also provide function pointers (or a struct of function pointers) that define the interface between the library and your main program. It's actually the method what i would choose probably. I heard from other people that it's not so easy to do -rdynamic in windows, and it also would make for a cleaner communication between library and main program (you've got precise control on what can be called and not), but it also requires more house-keeping.

Johannes Schaub - litb
Awesome answer. Somebody has earned 10k rep :-)
Norman Ramsey
That's an interesting way of doing the casting. Isn't it more normal to convert the return value of dlsym() to the pointer to function, rather than pretend that the pointer you're assigning to is is the same type as the function dlsym() returns?
Jonathan Leffler
C doesn't say what happens if you cast from a void* to a function pointer. sadly i haven't found the paragraph stating that it's undefined behavior, but the manpage says it states so.
Johannes Schaub - litb
you find more information on that here: http://www.opengroup.org/onlinepubs/009695399/functions/dlsym.html . Anyway, C++ explicitly forbids casting from void* or object pointers to a function pointer type or back.
Johannes Schaub - litb
so casting the stuff to void** like i did and then dereferencing is still undefined behavior, but XSI compliant systems will support it, and C++ doesn't explicitly forbid (makes it ill-formed) it (instead, it says it's undefined behavior).
Johannes Schaub - litb
@litb - I'd like to add a more-than-300-character comment about using union to 'resolve' the problem. May I edit your answer to add that info?
Jonathan Leffler
Jonathan, well i think you want to use an union, and put the function pointer and void pointer in them. then assign the void pointer and read the function pointer member. it will compile of course, as will the cast to void** , and both are undefined behavior in C.
Johannes Schaub - litb
@litb: Yes - you got it. The only difference is that the union bypasses warnings that the cast does not.
Jonathan Leffler
+1  A: 

The dlopen() function, as discussed by @litb, is primarily provided on systems using ELF format object files. It is rather powerful and will let you control whether symbols referenced by the loaded library can be satisfied from the main program, and generally does let them be satisfied. Not all shared library loading systems are as flexible - be aware if it comes to porting your code.

The callback mechanism outlined by @hhafez works now that the kinks in that code are straightened out.

Jonathan Leffler
straightened out the kinks ;) thanks
hhafez