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
Post a Comment