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