|
Ruby
2.0.0p594(2014-10-27revision48167)
|
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
1.7.6.1