Ruby 1.9.3p327(2012-11-10revision37606)
ext/dl/handle.c
Go to the documentation of this file.
00001 /* -*- C -*-
00002  * $Id: handle.c 32983 2011-08-16 00:51:58Z drbrain $
00003  */
00004 
00005 #include <ruby.h>
00006 #include "dl.h"
00007 
00008 VALUE rb_cDLHandle;
00009 
00010 #ifdef _WIN32
00011 # ifndef _WIN32_WCE
00012 static void *
00013 w32_coredll(void)
00014 {
00015     MEMORY_BASIC_INFORMATION m;
00016     memset(&m, 0, sizeof(m));
00017     if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL;
00018     return m.AllocationBase;
00019 }
00020 # endif
00021 
00022 static int
00023 w32_dlclose(void *ptr)
00024 {
00025 # ifndef _WIN32_WCE
00026     if( ptr == w32_coredll() ) return 0;
00027 # endif
00028     if( FreeLibrary((HMODULE)ptr) ) return 0;
00029     return errno = rb_w32_map_errno(GetLastError());
00030 }
00031 #define dlclose(ptr) w32_dlclose(ptr)
00032 #endif
00033 
00034 static void
00035 dlhandle_free(void *ptr)
00036 {
00037     struct dl_handle *dlhandle = ptr;
00038     if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
00039         dlclose(dlhandle->ptr);
00040     }
00041 }
00042 
00043 static size_t
00044 dlhandle_memsize(const void *ptr)
00045 {
00046     return ptr ? sizeof(struct dl_handle) : 0;
00047 }
00048 
00049 static const rb_data_type_t dlhandle_data_type = {
00050     "dl/handle",
00051     {0, dlhandle_free, dlhandle_memsize,},
00052 };
00053 
00054 /*
00055  * call-seq: close
00056  *
00057  * Close this DL::Handle.  Calling close more than once will raise a
00058  * DL::DLError exception.
00059  */
00060 VALUE
00061 rb_dlhandle_close(VALUE self)
00062 {
00063     struct dl_handle *dlhandle;
00064 
00065     TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
00066     if(dlhandle->open) {
00067         int ret = dlclose(dlhandle->ptr);
00068         dlhandle->open = 0;
00069 
00070         /* Check dlclose for successful return value */
00071         if(ret) {
00072 #if defined(HAVE_DLERROR)
00073             rb_raise(rb_eDLError, "%s", dlerror());
00074 #else
00075             rb_raise(rb_eDLError, "could not close handle");
00076 #endif
00077         }
00078         return INT2NUM(ret);
00079     }
00080     rb_raise(rb_eDLError, "dlclose() called too many times");
00081 }
00082 
00083 VALUE
00084 rb_dlhandle_s_allocate(VALUE klass)
00085 {
00086     VALUE obj;
00087     struct dl_handle *dlhandle;
00088 
00089     obj = TypedData_Make_Struct(rb_cDLHandle, struct dl_handle, &dlhandle_data_type, dlhandle);
00090     dlhandle->ptr  = 0;
00091     dlhandle->open = 0;
00092     dlhandle->enable_close = 0;
00093 
00094     return obj;
00095 }
00096 
00097 static VALUE
00098 predefined_dlhandle(void *handle)
00099 {
00100     VALUE obj = rb_dlhandle_s_allocate(rb_cDLHandle);
00101     struct dl_handle *dlhandle = DATA_PTR(obj);
00102 
00103     dlhandle->ptr = handle;
00104     dlhandle->open = 1;
00105     OBJ_FREEZE(obj);
00106     return obj;
00107 }
00108 
00109 /*
00110  * call-seq:
00111  *    initialize(lib = nil, flags = DL::RTLD_LAZY | DL::RTLD_GLOBAL)
00112  *
00113  * Create a new handler that opens library named +lib+ with +flags+.  If no
00114  * library is specified, RTLD_DEFAULT is used.
00115  */
00116 VALUE
00117 rb_dlhandle_initialize(int argc, VALUE argv[], VALUE self)
00118 {
00119     void *ptr;
00120     struct dl_handle *dlhandle;
00121     VALUE lib, flag;
00122     char  *clib;
00123     int   cflag;
00124     const char *err;
00125 
00126     switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
00127       case 0:
00128         clib = NULL;
00129         cflag = RTLD_LAZY | RTLD_GLOBAL;
00130         break;
00131       case 1:
00132         clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
00133         cflag = RTLD_LAZY | RTLD_GLOBAL;
00134         break;
00135       case 2:
00136         clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
00137         cflag = NUM2INT(flag);
00138         break;
00139       default:
00140         rb_bug("rb_dlhandle_new");
00141     }
00142 
00143     rb_secure(2);
00144 
00145 #if defined(_WIN32)
00146     if( !clib ){
00147         HANDLE rb_libruby_handle(void);
00148         ptr = rb_libruby_handle();
00149     }
00150     else if( STRCASECMP(clib, "libc") == 0
00151 # ifdef RUBY_COREDLL
00152              || STRCASECMP(clib, RUBY_COREDLL) == 0
00153              || STRCASECMP(clib, RUBY_COREDLL".dll") == 0
00154 # endif
00155         ){
00156 # ifdef _WIN32_WCE
00157         ptr = dlopen("coredll.dll", cflag);
00158 # else
00159         ptr = w32_coredll();
00160 # endif
00161     }
00162     else
00163 #endif
00164         ptr = dlopen(clib, cflag);
00165 #if defined(HAVE_DLERROR)
00166     if( !ptr && (err = dlerror()) ){
00167         rb_raise(rb_eDLError, "%s", err);
00168     }
00169 #else
00170     if( !ptr ){
00171         err = dlerror();
00172         rb_raise(rb_eDLError, "%s", err);
00173     }
00174 #endif
00175     TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
00176     if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
00177         dlclose(dlhandle->ptr);
00178     }
00179     dlhandle->ptr = ptr;
00180     dlhandle->open = 1;
00181     dlhandle->enable_close = 0;
00182 
00183     if( rb_block_given_p() ){
00184         rb_ensure(rb_yield, self, rb_dlhandle_close, self);
00185     }
00186 
00187     return Qnil;
00188 }
00189 
00190 /*
00191  * call-seq: enable_close
00192  *
00193  * Enable a call to dlclose() when this DL::Handle is garbage collected.
00194  */
00195 VALUE
00196 rb_dlhandle_enable_close(VALUE self)
00197 {
00198     struct dl_handle *dlhandle;
00199 
00200     TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
00201     dlhandle->enable_close = 1;
00202     return Qnil;
00203 }
00204 
00205 /*
00206  * call-seq: disable_close
00207  *
00208  * Disable a call to dlclose() when this DL::Handle is garbage collected.
00209  */
00210 VALUE
00211 rb_dlhandle_disable_close(VALUE self)
00212 {
00213     struct dl_handle *dlhandle;
00214 
00215     TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
00216     dlhandle->enable_close = 0;
00217     return Qnil;
00218 }
00219 
00220 /*
00221  * call-seq: close_enabled?
00222  *
00223  * Returns +true+ if dlclose() will be called when this DL::Handle is
00224  * garbage collected.
00225  */
00226 static VALUE
00227 rb_dlhandle_close_enabled_p(VALUE self)
00228 {
00229     struct dl_handle *dlhandle;
00230 
00231     TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
00232 
00233     if(dlhandle->enable_close) return Qtrue;
00234     return Qfalse;
00235 }
00236 
00237 /*
00238  * call-seq: to_i
00239  *
00240  * Returns the memory address for this handle.
00241  */
00242 VALUE
00243 rb_dlhandle_to_i(VALUE self)
00244 {
00245     struct dl_handle *dlhandle;
00246 
00247     TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
00248     return PTR2NUM(dlhandle);
00249 }
00250 
00251 static VALUE dlhandle_sym(void *handle, const char *symbol);
00252 
00253 /*
00254  * Document-method: sym
00255  * Document-method: []
00256  *
00257  * call-seq: sym(name)
00258  *
00259  * Get the address as an Integer for the function named +name+.
00260  */
00261 VALUE
00262 rb_dlhandle_sym(VALUE self, VALUE sym)
00263 {
00264     struct dl_handle *dlhandle;
00265 
00266     TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle);
00267     if( ! dlhandle->open ){
00268         rb_raise(rb_eDLError, "closed handle");
00269     }
00270 
00271     return dlhandle_sym(dlhandle->ptr, StringValueCStr(sym));
00272 }
00273 
00274 #ifndef RTLD_NEXT
00275 #define RTLD_NEXT NULL
00276 #endif
00277 #ifndef RTLD_DEFAULT
00278 #define RTLD_DEFAULT NULL
00279 #endif
00280 
00281 /*
00282  * Document-method: sym
00283  * Document-method: []
00284  *
00285  * call-seq: sym(name)
00286  *
00287  * Get the address as an Integer for the function named +name+.  The function
00288  * is searched via dlsym on RTLD_NEXT.  See man(3) dlsym() for more info.
00289  */
00290 VALUE
00291 rb_dlhandle_s_sym(VALUE self, VALUE sym)
00292 {
00293     return dlhandle_sym(RTLD_NEXT, StringValueCStr(sym));
00294 }
00295 
00296 static VALUE
00297 dlhandle_sym(void *handle, const char *name)
00298 {
00299 #if defined(HAVE_DLERROR)
00300     const char *err;
00301 # define CHECK_DLERROR if( err = dlerror() ){ func = 0; }
00302 #else
00303 # define CHECK_DLERROR
00304 #endif
00305     void (*func)();
00306 
00307     rb_secure(2);
00308 #ifdef HAVE_DLERROR
00309     dlerror();
00310 #endif
00311     func = (void (*)())(VALUE)dlsym(handle, name);
00312     CHECK_DLERROR;
00313 #if defined(FUNC_STDCALL)
00314     if( !func ){
00315         int  i;
00316         int  len = (int)strlen(name);
00317         char *name_n;
00318 #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
00319         {
00320             char *name_a = (char*)xmalloc(len+2);
00321             strcpy(name_a, name);
00322             name_n = name_a;
00323             name_a[len]   = 'A';
00324             name_a[len+1] = '\0';
00325             func = dlsym(handle, name_a);
00326             CHECK_DLERROR;
00327             if( func ) goto found;
00328             name_n = xrealloc(name_a, len+6);
00329         }
00330 #else
00331         name_n = (char*)xmalloc(len+6);
00332 #endif
00333         memcpy(name_n, name, len);
00334         name_n[len++] = '@';
00335         for( i = 0; i < 256; i += 4 ){
00336             sprintf(name_n + len, "%d", i);
00337             func = dlsym(handle, name_n);
00338             CHECK_DLERROR;
00339             if( func ) break;
00340         }
00341         if( func ) goto found;
00342         name_n[len-1] = 'A';
00343         name_n[len++] = '@';
00344         for( i = 0; i < 256; i += 4 ){
00345             sprintf(name_n + len, "%d", i);
00346             func = dlsym(handle, name_n);
00347             CHECK_DLERROR;
00348             if( func ) break;
00349         }
00350       found:
00351         xfree(name_n);
00352     }
00353 #endif
00354     if( !func ){
00355         rb_raise(rb_eDLError, "unknown symbol \"%s\"", name);
00356     }
00357 
00358     return PTR2NUM(func);
00359 }
00360 
00361 void
00362 Init_dlhandle(void)
00363 {
00364     /*
00365      * Document-class: DL::Handle
00366      *
00367      * The DL::Handle is the manner to access the dynamic library
00368      *
00369      * == Example
00370      *
00371      * === Setup
00372      *
00373      *   libc_so = "/lib64/libc.so.6"
00374      *   => "/lib64/libc.so.6"
00375      *   @handle = DL::Handle.new(libc_so)
00376      *   => #<DL::Handle:0x00000000d69ef8>
00377      *
00378      * === Setup, with flags
00379      *
00380      *   libc_so = "/lib64/libc.so.6"
00381      *   => "/lib64/libc.so.6"
00382      *   @handle = DL::Handle.new(libc_so, DL::RTLD_LAZY | DL::RTLD_GLOBAL)
00383      *   => #<DL::Handle:0x00000000d69ef8>
00384      *
00385      * === Addresses to symbols
00386      *
00387      *   strcpy_addr = @handle['strcpy']
00388      *   => 140062278451968
00389      *
00390      * or
00391      *
00392      *   strcpy_addr = @handle.sym('strcpy')
00393      *   => 140062278451968
00394      *
00395      */
00396     rb_cDLHandle = rb_define_class_under(rb_mDL, "Handle", rb_cObject);
00397     rb_define_alloc_func(rb_cDLHandle, rb_dlhandle_s_allocate);
00398     rb_define_singleton_method(rb_cDLHandle, "sym", rb_dlhandle_s_sym, 1);
00399     rb_define_singleton_method(rb_cDLHandle, "[]", rb_dlhandle_s_sym,  1);
00400 
00401     /* Document-const: NEXT
00402      *
00403      * A predefined pseudo-handle of RTLD_NEXT
00404      *
00405      * Which will find the next occurrence of a function in the search order
00406      * after the current library.
00407      */
00408     rb_define_const(rb_cDLHandle, "NEXT", predefined_dlhandle(RTLD_NEXT));
00409 
00410     /* Document-const: DEFAULT
00411      *
00412      * A predefined pseudo-handle of RTLD_DEFAULT
00413      *
00414      * Which will find the first occurrence of the desired symbol using the
00415      * default library search order
00416      */
00417     rb_define_const(rb_cDLHandle, "DEFAULT", predefined_dlhandle(RTLD_DEFAULT));
00418     rb_define_method(rb_cDLHandle, "initialize", rb_dlhandle_initialize, -1);
00419     rb_define_method(rb_cDLHandle, "to_i", rb_dlhandle_to_i, 0);
00420     rb_define_method(rb_cDLHandle, "close", rb_dlhandle_close, 0);
00421     rb_define_method(rb_cDLHandle, "sym",  rb_dlhandle_sym, 1);
00422     rb_define_method(rb_cDLHandle, "[]",  rb_dlhandle_sym,  1);
00423     rb_define_method(rb_cDLHandle, "disable_close", rb_dlhandle_disable_close, 0);
00424     rb_define_method(rb_cDLHandle, "enable_close", rb_dlhandle_enable_close, 0);
00425     rb_define_method(rb_cDLHandle, "close_enabled?", rb_dlhandle_close_enabled_p, 0);
00426 }
00427 
00428 /* mode: c; tab-with=8; sw=4; ts=8; noexpandtab: */
00429