c++ - Why does this dynamic library loading code work with gcc? -


background:

i've found myself unenviable task of porting c++ gnu/linux application on windows. 1 of things application search shared libraries on specific paths , loads classes out of them dynamically using posix dlopen() , dlsym() calls. have reason doing loading way not go here.

the problem:

to dynamically discover symbols generated c++ compiler dlsym() or getprocaddress() must unmangled using extern "c" linkage block. example:

#include <list> #include <string>  using std::list; using std::string;  extern "c" {      list<string> get_list()     {         list<string> mylist;         mylist.push_back("list object");         return mylist;     }  } 

this code valid c++ , compiles , runs on numerous compilers on both linux , windows. it, however, not compile msvc because "the return type not valid c". workaround we've come change function return pointer list instead of list object:

#include <list> #include <string>  using std::list; using std::string;  extern "c" {      list<string>* get_list()     {         list<string>* mylist = new list<string>();         mylist->push_back("ptr list");         return mylist;     }  } 

i've been trying find optimal solution gnu/linux loader either work both new functions , old legacy function prototype or @ least detect when deprecated function encountered , issue warning. unseemly our users if code segfaulted when tried use old library. original idea set sigsegv signal handler during call get_list (i know icky - i'm open better ideas). confirm loading old library segfault thought ran library using old function prototype (returning list object) through new loading code (that expects pointer list) , surprise worked. question have why?

the below loading code works both function prototypes listed above. i've confirmed works on fedora 12, redhat 5.5, , redhawk 5.1 using gcc versions 4.1.2 , 4.4.4. compile libraries using g++ -shared , -fpic , executable needs linked against dl (-ldl).

#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <list> #include <string>  using std::list; using std::string;  int main(int argc, char **argv) {     void *handle;     list<string>* (*getlist)(void);     char *error;      handle = dlopen("library path", rtld_lazy);     if (!handle)     {         fprintf(stderr, "%s\n", dlerror());         exit(exit_failure);     }      dlerror();      *(void **) (&getlist) = dlsym(handle, "get_list");      if ((error = dlerror()) != null)     {         printf("%s\n", error);         exit(exit_failure);     }      list<string>* liblist = (*getlist)();      for(list<string>::iterator iter = liblist->begin();           iter != liblist->end(); iter++)     {         printf("\t%s\n", iter->c_str());     }      dlclose(handle);      exit(exit_success); } 

as aschepler says, because got lucky.

as turns out, abi used gcc (and other compilers) both x86 , x64 returns 'large' structs (too big fit in register) passing 'hidden' pointer arg function, uses pointer space store return value, , returns pointer itself. turns out function of form

struct foo func(...) 

is equivlant to

struct foo *func(..., struct foo *) 

where caller expected allocate space 'foo' (probably on stack) , pass in pointer it.

so happens if have function expecting called way (expecting return struct) , instead call via function pointer returns pointer, may appear work -- if garbage bits gets arg (random register contents left there caller) happen point somewhere writable, called function happily write return value there , return pointer, called code looks valid pointer struct expecting. code may superficially appear work, clobbering random bit of memory may important later.


Comments

Popular posts from this blog

Javascript line number mapping -

c# - Is it possible to remove an existing registration from Autofac container builder? -

php - Mysql PK and FK char(36) vs int(10) -