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