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(library = nil, flags = Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
00118  *
00119  * Create a new handler that opens +library+ with +flags+.
00120  *
00121  * If no +library+ is specified or +nil+ is given, DEFAULT is used, which is
00122  * the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
00123  *
00124  *      lib = Fiddle::Handle.new
00125  *
00126  * The default is dependent on OS, and provide a handle for all libraries
00127  * already loaded. For example, in most cases you can use this to access +libc+
00128  * functions, or ruby functions like +rb_str_new+.
00129  */
00130 static VALUE
00131 rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
00132 {
00133     void *ptr;
00134     struct dl_handle *fiddle_handle;
00135     VALUE lib, flag;
00136     char  *clib;
00137     int   cflag;
00138     const char *err;
00139 
00140     switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
00141       case 0:
00142         clib = NULL;
00143         cflag = RTLD_LAZY | RTLD_GLOBAL;
00144         break;
00145       case 1:
00146         clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
00147         cflag = RTLD_LAZY | RTLD_GLOBAL;
00148         break;
00149       case 2:
00150         clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
00151         cflag = NUM2INT(flag);
00152         break;
00153       default:
00154         rb_bug("rb_fiddle_handle_new");
00155     }
00156 
00157     rb_secure(2);
00158 
00159 #if defined(_WIN32)
00160     if( !clib ){
00161         HANDLE rb_libruby_handle(void);
00162         ptr = rb_libruby_handle();
00163     }
00164     else if( STRCASECMP(clib, "libc") == 0
00165 # ifdef RUBY_COREDLL
00166              || STRCASECMP(clib, RUBY_COREDLL) == 0
00167              || STRCASECMP(clib, RUBY_COREDLL".dll") == 0
00168 # endif
00169         ){
00170 # ifdef _WIN32_WCE
00171         ptr = dlopen("coredll.dll", cflag);
00172 # else
00173         ptr = w32_coredll();
00174 # endif
00175     }
00176     else
00177 #endif
00178         ptr = dlopen(clib, cflag);
00179 #if defined(HAVE_DLERROR)
00180     if( !ptr && (err = dlerror()) ){
00181         rb_raise(rb_eFiddleError, "%s", err);
00182     }
00183 #else
00184     if( !ptr ){
00185         err = dlerror();
00186         rb_raise(rb_eFiddleError, "%s", err);
00187     }
00188 #endif
00189     TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00190     if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
00191         dlclose(fiddle_handle->ptr);
00192     }
00193     fiddle_handle->ptr = ptr;
00194     fiddle_handle->open = 1;
00195     fiddle_handle->enable_close = 0;
00196 
00197     if( rb_block_given_p() ){
00198         rb_ensure(rb_yield, self, rb_fiddle_handle_close, self);
00199     }
00200 
00201     return Qnil;
00202 }
00203 
00204 /*
00205  * call-seq: enable_close
00206  *
00207  * Enable a call to dlclose() when this handle is garbage collected.
00208  */
00209 static VALUE
00210 rb_fiddle_handle_enable_close(VALUE self)
00211 {
00212     struct dl_handle *fiddle_handle;
00213 
00214     TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00215     fiddle_handle->enable_close = 1;
00216     return Qnil;
00217 }
00218 
00219 /*
00220  * call-seq: disable_close
00221  *
00222  * Disable a call to dlclose() when this handle is garbage collected.
00223  */
00224 static VALUE
00225 rb_fiddle_handle_disable_close(VALUE self)
00226 {
00227     struct dl_handle *fiddle_handle;
00228 
00229     TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00230     fiddle_handle->enable_close = 0;
00231     return Qnil;
00232 }
00233 
00234 /*
00235  * call-seq: close_enabled?
00236  *
00237  * Returns +true+ if dlclose() will be called when this handle is garbage collected.
00238  *
00239  * See man(3) dlclose() for more info.
00240  */
00241 static VALUE
00242 rb_fiddle_handle_close_enabled_p(VALUE self)
00243 {
00244     struct dl_handle *fiddle_handle;
00245 
00246     TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00247 
00248     if(fiddle_handle->enable_close) return Qtrue;
00249     return Qfalse;
00250 }
00251 
00252 /*
00253  * call-seq: to_i
00254  *
00255  * Returns the memory address for this handle.
00256  */
00257 static VALUE
00258 rb_fiddle_handle_to_i(VALUE self)
00259 {
00260     struct dl_handle *fiddle_handle;
00261 
00262     TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00263     return PTR2NUM(fiddle_handle);
00264 }
00265 
00266 static VALUE fiddle_handle_sym(void *handle, const char *symbol);
00267 
00268 /*
00269  * Document-method: sym
00270  *
00271  * call-seq: sym(name)
00272  *
00273  * Get the address as an Integer for the function named +name+.
00274  */
00275 static VALUE
00276 rb_fiddle_handle_sym(VALUE self, VALUE sym)
00277 {
00278     struct dl_handle *fiddle_handle;
00279 
00280     TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00281     if( ! fiddle_handle->open ){
00282         rb_raise(rb_eFiddleError, "closed handle");
00283     }
00284 
00285     return fiddle_handle_sym(fiddle_handle->ptr, StringValueCStr(sym));
00286 }
00287 
00288 #ifndef RTLD_NEXT
00289 #define RTLD_NEXT NULL
00290 #endif
00291 #ifndef RTLD_DEFAULT
00292 #define RTLD_DEFAULT NULL
00293 #endif
00294 
00295 /*
00296  * Document-method: sym
00297  *
00298  * call-seq: sym(name)
00299  *
00300  * Get the address as an Integer for the function named +name+.  The function
00301  * is searched via dlsym on RTLD_NEXT.
00302  *
00303  * See man(3) dlsym() for more info.
00304  */
00305 static VALUE
00306 rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
00307 {
00308     return fiddle_handle_sym(RTLD_NEXT, StringValueCStr(sym));
00309 }
00310 
00311 static VALUE
00312 fiddle_handle_sym(void *handle, const char *name)
00313 {
00314 #if defined(HAVE_DLERROR)
00315     const char *err;
00316 # define CHECK_DLERROR if( err = dlerror() ){ func = 0; }
00317 #else
00318 # define CHECK_DLERROR
00319 #endif
00320     void (*func)();
00321 
00322     rb_secure(2);
00323 #ifdef HAVE_DLERROR
00324     dlerror();
00325 #endif
00326     func = (void (*)())(VALUE)dlsym(handle, name);
00327     CHECK_DLERROR;
00328 #if defined(FUNC_STDCALL)
00329     if( !func ){
00330         int  i;
00331         int  len = (int)strlen(name);
00332         char *name_n;
00333 #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
00334         {
00335             char *name_a = (char*)xmalloc(len+2);
00336             strcpy(name_a, name);
00337             name_n = name_a;
00338             name_a[len]   = 'A';
00339             name_a[len+1] = '\0';
00340             func = dlsym(handle, name_a);
00341             CHECK_DLERROR;
00342             if( func ) goto found;
00343             name_n = xrealloc(name_a, len+6);
00344         }
00345 #else
00346         name_n = (char*)xmalloc(len+6);
00347 #endif
00348         memcpy(name_n, name, len);
00349         name_n[len++] = '@';
00350         for( i = 0; i < 256; i += 4 ){
00351             sprintf(name_n + len, "%d", i);
00352             func = dlsym(handle, name_n);
00353             CHECK_DLERROR;
00354             if( func ) break;
00355         }
00356         if( func ) goto found;
00357         name_n[len-1] = 'A';
00358         name_n[len++] = '@';
00359         for( i = 0; i < 256; i += 4 ){
00360             sprintf(name_n + len, "%d", i);
00361             func = dlsym(handle, name_n);
00362             CHECK_DLERROR;
00363             if( func ) break;
00364         }
00365       found:
00366         xfree(name_n);
00367     }
00368 #endif
00369     if( !func ){
00370         rb_raise(rb_eFiddleError, "unknown symbol \"%s\"", name);
00371     }
00372 
00373     return PTR2NUM(func);
00374 }
00375 
00376 void
00377 Init_fiddle_handle(void)
00378 {
00379     /*
00380      * Document-class: Fiddle::Handle
00381      *
00382      * The Fiddle::Handle is the manner to access the dynamic library
00383      *
00384      * == Example
00385      *
00386      * === Setup
00387      *
00388      *   libc_so = "/lib64/libc.so.6"
00389      *   => "/lib64/libc.so.6"
00390      *   @handle = Fiddle::Handle.new(libc_so)
00391      *   => #<Fiddle::Handle:0x00000000d69ef8>
00392      *
00393      * === Setup, with flags
00394      *
00395      *   libc_so = "/lib64/libc.so.6"
00396      *   => "/lib64/libc.so.6"
00397      *   @handle = Fiddle::Handle.new(libc_so, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
00398      *   => #<Fiddle::Handle:0x00000000d69ef8>
00399      *
00400      * See RTLD_LAZY and RTLD_GLOBAL
00401      *
00402      * === Addresses to symbols
00403      *
00404      *   strcpy_addr = @handle['strcpy']
00405      *   => 140062278451968
00406      *
00407      * or
00408      *
00409      *   strcpy_addr = @handle.sym('strcpy')
00410      *   => 140062278451968
00411      *
00412      */
00413     rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject);
00414     rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
00415     rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
00416     rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym,  1);
00417 
00418     /* Document-const: NEXT
00419      *
00420      * A predefined pseudo-handle of RTLD_NEXT
00421      *
00422      * Which will find the next occurrence of a function in the search order
00423      * after the current library.
00424      */
00425     rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT));
00426 
00427     /* Document-const: DEFAULT
00428      *
00429      * A predefined pseudo-handle of RTLD_DEFAULT
00430      *
00431      * Which will find the first occurrence of the desired symbol using the
00432      * default library search order
00433      */
00434     rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT));
00435 
00436     /* Document-const: RTLD_GLOBAL
00437      *
00438      * rtld Fiddle::Handle flag.
00439      *
00440      * The symbols defined by this library will be made available for symbol
00441      * resolution of subsequently loaded libraries.
00442      */
00443     rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
00444 
00445     /* Document-const: RTLD_LAZY
00446      *
00447      * rtld Fiddle::Handle flag.
00448      *
00449      * Perform lazy binding.  Only resolve symbols as the code that references
00450      * them is executed.  If the  symbol is never referenced, then it is never
00451      * resolved.  (Lazy binding is only performed for function references;
00452      * references to variables are always immediately bound when the library
00453      * is loaded.)
00454      */
00455     rb_define_const(rb_cHandle, "RTLD_LAZY",   INT2NUM(RTLD_LAZY));
00456 
00457     /* Document-const: RTLD_NOW
00458      *
00459      * rtld Fiddle::Handle flag.
00460      *
00461      * If this value is specified or the environment variable LD_BIND_NOW is
00462      * set to a nonempty string, all undefined symbols in the library are
00463      * resolved before Fiddle.dlopen returns.  If this cannot be done an error
00464      * is returned.
00465      */
00466     rb_define_const(rb_cHandle, "RTLD_NOW",    INT2NUM(RTLD_NOW));
00467 
00468     rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1);
00469     rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0);
00470     rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
00471     rb_define_method(rb_cHandle, "sym",  rb_fiddle_handle_sym, 1);
00472     rb_define_method(rb_cHandle, "[]",  rb_fiddle_handle_sym,  1);
00473     rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
00474     rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
00475     rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0);
00476 }
00477 
00478 /* vim: set noet sws=4 sw=4: */
00479 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7