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

linux - Mailx and Gmail nss config dir -

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

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