Ruby
2.0.0p247(2013-06-27revision41674)
|
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