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