Ruby 1.9.3p327(2012-11-10revision37606)
|
00001 /* -*- C -*- 00002 * $Id: handle.c 32983 2011-08-16 00:51:58Z 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 00083 VALUE 00084 rb_dlhandle_s_allocate(VALUE klass) 00085 { 00086 VALUE obj; 00087 struct dl_handle *dlhandle; 00088 00089 obj = TypedData_Make_Struct(rb_cDLHandle, struct dl_handle, &dlhandle_data_type, dlhandle); 00090 dlhandle->ptr = 0; 00091 dlhandle->open = 0; 00092 dlhandle->enable_close = 0; 00093 00094 return obj; 00095 } 00096 00097 static VALUE 00098 predefined_dlhandle(void *handle) 00099 { 00100 VALUE obj = rb_dlhandle_s_allocate(rb_cDLHandle); 00101 struct dl_handle *dlhandle = DATA_PTR(obj); 00102 00103 dlhandle->ptr = handle; 00104 dlhandle->open = 1; 00105 OBJ_FREEZE(obj); 00106 return obj; 00107 } 00108 00109 /* 00110 * call-seq: 00111 * initialize(lib = nil, flags = DL::RTLD_LAZY | DL::RTLD_GLOBAL) 00112 * 00113 * Create a new handler that opens library named +lib+ with +flags+. If no 00114 * library is specified, RTLD_DEFAULT is used. 00115 */ 00116 VALUE 00117 rb_dlhandle_initialize(int argc, VALUE argv[], VALUE self) 00118 { 00119 void *ptr; 00120 struct dl_handle *dlhandle; 00121 VALUE lib, flag; 00122 char *clib; 00123 int cflag; 00124 const char *err; 00125 00126 switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){ 00127 case 0: 00128 clib = NULL; 00129 cflag = RTLD_LAZY | RTLD_GLOBAL; 00130 break; 00131 case 1: 00132 clib = NIL_P(lib) ? NULL : StringValuePtr(lib); 00133 cflag = RTLD_LAZY | RTLD_GLOBAL; 00134 break; 00135 case 2: 00136 clib = NIL_P(lib) ? NULL : StringValuePtr(lib); 00137 cflag = NUM2INT(flag); 00138 break; 00139 default: 00140 rb_bug("rb_dlhandle_new"); 00141 } 00142 00143 rb_secure(2); 00144 00145 #if defined(_WIN32) 00146 if( !clib ){ 00147 HANDLE rb_libruby_handle(void); 00148 ptr = rb_libruby_handle(); 00149 } 00150 else if( STRCASECMP(clib, "libc") == 0 00151 # ifdef RUBY_COREDLL 00152 || STRCASECMP(clib, RUBY_COREDLL) == 0 00153 || STRCASECMP(clib, RUBY_COREDLL".dll") == 0 00154 # endif 00155 ){ 00156 # ifdef _WIN32_WCE 00157 ptr = dlopen("coredll.dll", cflag); 00158 # else 00159 ptr = w32_coredll(); 00160 # endif 00161 } 00162 else 00163 #endif 00164 ptr = dlopen(clib, cflag); 00165 #if defined(HAVE_DLERROR) 00166 if( !ptr && (err = dlerror()) ){ 00167 rb_raise(rb_eDLError, "%s", err); 00168 } 00169 #else 00170 if( !ptr ){ 00171 err = dlerror(); 00172 rb_raise(rb_eDLError, "%s", err); 00173 } 00174 #endif 00175 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 00176 if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){ 00177 dlclose(dlhandle->ptr); 00178 } 00179 dlhandle->ptr = ptr; 00180 dlhandle->open = 1; 00181 dlhandle->enable_close = 0; 00182 00183 if( rb_block_given_p() ){ 00184 rb_ensure(rb_yield, self, rb_dlhandle_close, self); 00185 } 00186 00187 return Qnil; 00188 } 00189 00190 /* 00191 * call-seq: enable_close 00192 * 00193 * Enable a call to dlclose() when this DL::Handle is garbage collected. 00194 */ 00195 VALUE 00196 rb_dlhandle_enable_close(VALUE self) 00197 { 00198 struct dl_handle *dlhandle; 00199 00200 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 00201 dlhandle->enable_close = 1; 00202 return Qnil; 00203 } 00204 00205 /* 00206 * call-seq: disable_close 00207 * 00208 * Disable a call to dlclose() when this DL::Handle is garbage collected. 00209 */ 00210 VALUE 00211 rb_dlhandle_disable_close(VALUE self) 00212 { 00213 struct dl_handle *dlhandle; 00214 00215 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 00216 dlhandle->enable_close = 0; 00217 return Qnil; 00218 } 00219 00220 /* 00221 * call-seq: close_enabled? 00222 * 00223 * Returns +true+ if dlclose() will be called when this DL::Handle is 00224 * garbage collected. 00225 */ 00226 static VALUE 00227 rb_dlhandle_close_enabled_p(VALUE self) 00228 { 00229 struct dl_handle *dlhandle; 00230 00231 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 00232 00233 if(dlhandle->enable_close) return Qtrue; 00234 return Qfalse; 00235 } 00236 00237 /* 00238 * call-seq: to_i 00239 * 00240 * Returns the memory address for this handle. 00241 */ 00242 VALUE 00243 rb_dlhandle_to_i(VALUE self) 00244 { 00245 struct dl_handle *dlhandle; 00246 00247 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 00248 return PTR2NUM(dlhandle); 00249 } 00250 00251 static VALUE dlhandle_sym(void *handle, const char *symbol); 00252 00253 /* 00254 * Document-method: sym 00255 * Document-method: [] 00256 * 00257 * call-seq: sym(name) 00258 * 00259 * Get the address as an Integer for the function named +name+. 00260 */ 00261 VALUE 00262 rb_dlhandle_sym(VALUE self, VALUE sym) 00263 { 00264 struct dl_handle *dlhandle; 00265 00266 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 00267 if( ! dlhandle->open ){ 00268 rb_raise(rb_eDLError, "closed handle"); 00269 } 00270 00271 return dlhandle_sym(dlhandle->ptr, StringValueCStr(sym)); 00272 } 00273 00274 #ifndef RTLD_NEXT 00275 #define RTLD_NEXT NULL 00276 #endif 00277 #ifndef RTLD_DEFAULT 00278 #define RTLD_DEFAULT NULL 00279 #endif 00280 00281 /* 00282 * Document-method: sym 00283 * Document-method: [] 00284 * 00285 * call-seq: sym(name) 00286 * 00287 * Get the address as an Integer for the function named +name+. The function 00288 * is searched via dlsym on RTLD_NEXT. See man(3) dlsym() for more info. 00289 */ 00290 VALUE 00291 rb_dlhandle_s_sym(VALUE self, VALUE sym) 00292 { 00293 return dlhandle_sym(RTLD_NEXT, StringValueCStr(sym)); 00294 } 00295 00296 static VALUE 00297 dlhandle_sym(void *handle, const char *name) 00298 { 00299 #if defined(HAVE_DLERROR) 00300 const char *err; 00301 # define CHECK_DLERROR if( err = dlerror() ){ func = 0; } 00302 #else 00303 # define CHECK_DLERROR 00304 #endif 00305 void (*func)(); 00306 00307 rb_secure(2); 00308 #ifdef HAVE_DLERROR 00309 dlerror(); 00310 #endif 00311 func = (void (*)())(VALUE)dlsym(handle, name); 00312 CHECK_DLERROR; 00313 #if defined(FUNC_STDCALL) 00314 if( !func ){ 00315 int i; 00316 int len = (int)strlen(name); 00317 char *name_n; 00318 #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__) 00319 { 00320 char *name_a = (char*)xmalloc(len+2); 00321 strcpy(name_a, name); 00322 name_n = name_a; 00323 name_a[len] = 'A'; 00324 name_a[len+1] = '\0'; 00325 func = dlsym(handle, name_a); 00326 CHECK_DLERROR; 00327 if( func ) goto found; 00328 name_n = xrealloc(name_a, len+6); 00329 } 00330 #else 00331 name_n = (char*)xmalloc(len+6); 00332 #endif 00333 memcpy(name_n, name, len); 00334 name_n[len++] = '@'; 00335 for( i = 0; i < 256; i += 4 ){ 00336 sprintf(name_n + len, "%d", i); 00337 func = dlsym(handle, name_n); 00338 CHECK_DLERROR; 00339 if( func ) break; 00340 } 00341 if( func ) goto found; 00342 name_n[len-1] = 'A'; 00343 name_n[len++] = '@'; 00344 for( i = 0; i < 256; i += 4 ){ 00345 sprintf(name_n + len, "%d", i); 00346 func = dlsym(handle, name_n); 00347 CHECK_DLERROR; 00348 if( func ) break; 00349 } 00350 found: 00351 xfree(name_n); 00352 } 00353 #endif 00354 if( !func ){ 00355 rb_raise(rb_eDLError, "unknown symbol \"%s\"", name); 00356 } 00357 00358 return PTR2NUM(func); 00359 } 00360 00361 void 00362 Init_dlhandle(void) 00363 { 00364 /* 00365 * Document-class: DL::Handle 00366 * 00367 * The DL::Handle is the manner to access the dynamic library 00368 * 00369 * == Example 00370 * 00371 * === Setup 00372 * 00373 * libc_so = "/lib64/libc.so.6" 00374 * => "/lib64/libc.so.6" 00375 * @handle = DL::Handle.new(libc_so) 00376 * => #<DL::Handle:0x00000000d69ef8> 00377 * 00378 * === Setup, with flags 00379 * 00380 * libc_so = "/lib64/libc.so.6" 00381 * => "/lib64/libc.so.6" 00382 * @handle = DL::Handle.new(libc_so, DL::RTLD_LAZY | DL::RTLD_GLOBAL) 00383 * => #<DL::Handle:0x00000000d69ef8> 00384 * 00385 * === Addresses to symbols 00386 * 00387 * strcpy_addr = @handle['strcpy'] 00388 * => 140062278451968 00389 * 00390 * or 00391 * 00392 * strcpy_addr = @handle.sym('strcpy') 00393 * => 140062278451968 00394 * 00395 */ 00396 rb_cDLHandle = rb_define_class_under(rb_mDL, "Handle", rb_cObject); 00397 rb_define_alloc_func(rb_cDLHandle, rb_dlhandle_s_allocate); 00398 rb_define_singleton_method(rb_cDLHandle, "sym", rb_dlhandle_s_sym, 1); 00399 rb_define_singleton_method(rb_cDLHandle, "[]", rb_dlhandle_s_sym, 1); 00400 00401 /* Document-const: NEXT 00402 * 00403 * A predefined pseudo-handle of RTLD_NEXT 00404 * 00405 * Which will find the next occurrence of a function in the search order 00406 * after the current library. 00407 */ 00408 rb_define_const(rb_cDLHandle, "NEXT", predefined_dlhandle(RTLD_NEXT)); 00409 00410 /* Document-const: DEFAULT 00411 * 00412 * A predefined pseudo-handle of RTLD_DEFAULT 00413 * 00414 * Which will find the first occurrence of the desired symbol using the 00415 * default library search order 00416 */ 00417 rb_define_const(rb_cDLHandle, "DEFAULT", predefined_dlhandle(RTLD_DEFAULT)); 00418 rb_define_method(rb_cDLHandle, "initialize", rb_dlhandle_initialize, -1); 00419 rb_define_method(rb_cDLHandle, "to_i", rb_dlhandle_to_i, 0); 00420 rb_define_method(rb_cDLHandle, "close", rb_dlhandle_close, 0); 00421 rb_define_method(rb_cDLHandle, "sym", rb_dlhandle_sym, 1); 00422 rb_define_method(rb_cDLHandle, "[]", rb_dlhandle_sym, 1); 00423 rb_define_method(rb_cDLHandle, "disable_close", rb_dlhandle_disable_close, 0); 00424 rb_define_method(rb_cDLHandle, "enable_close", rb_dlhandle_enable_close, 0); 00425 rb_define_method(rb_cDLHandle, "close_enabled?", rb_dlhandle_close_enabled_p, 0); 00426 } 00427 00428 /* mode: c; tab-with=8; sw=4; ts=8; noexpandtab: */ 00429