1/* -*- C -*- 2 * $Id: handle.c 35321 2012-04-13 23:45:37Z drbrain $ 3 */ 4 5#include <ruby.h> 6#include "dl.h" 7 8VALUE rb_cDLHandle; 9 10#ifdef _WIN32 11# ifndef _WIN32_WCE 12static void * 13w32_coredll(void) 14{ 15 MEMORY_BASIC_INFORMATION m; 16 memset(&m, 0, sizeof(m)); 17 if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL; 18 return m.AllocationBase; 19} 20# endif 21 22static int 23w32_dlclose(void *ptr) 24{ 25# ifndef _WIN32_WCE 26 if( ptr == w32_coredll() ) return 0; 27# endif 28 if( FreeLibrary((HMODULE)ptr) ) return 0; 29 return errno = rb_w32_map_errno(GetLastError()); 30} 31#define dlclose(ptr) w32_dlclose(ptr) 32#endif 33 34static void 35dlhandle_free(void *ptr) 36{ 37 struct dl_handle *dlhandle = ptr; 38 if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){ 39 dlclose(dlhandle->ptr); 40 } 41} 42 43static size_t 44dlhandle_memsize(const void *ptr) 45{ 46 return ptr ? sizeof(struct dl_handle) : 0; 47} 48 49static const rb_data_type_t dlhandle_data_type = { 50 "dl/handle", 51 {0, dlhandle_free, dlhandle_memsize,}, 52}; 53 54/* 55 * call-seq: close 56 * 57 * Close this DL::Handle. Calling close more than once will raise a 58 * DL::DLError exception. 59 */ 60VALUE 61rb_dlhandle_close(VALUE self) 62{ 63 struct dl_handle *dlhandle; 64 65 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 66 if(dlhandle->open) { 67 int ret = dlclose(dlhandle->ptr); 68 dlhandle->open = 0; 69 70 /* Check dlclose for successful return value */ 71 if(ret) { 72#if defined(HAVE_DLERROR) 73 rb_raise(rb_eDLError, "%s", dlerror()); 74#else 75 rb_raise(rb_eDLError, "could not close handle"); 76#endif 77 } 78 return INT2NUM(ret); 79 } 80 rb_raise(rb_eDLError, "dlclose() called too many times"); 81 82 UNREACHABLE; 83} 84 85VALUE 86rb_dlhandle_s_allocate(VALUE klass) 87{ 88 VALUE obj; 89 struct dl_handle *dlhandle; 90 91 obj = TypedData_Make_Struct(rb_cDLHandle, struct dl_handle, &dlhandle_data_type, dlhandle); 92 dlhandle->ptr = 0; 93 dlhandle->open = 0; 94 dlhandle->enable_close = 0; 95 96 return obj; 97} 98 99static VALUE 100predefined_dlhandle(void *handle) 101{ 102 VALUE obj = rb_dlhandle_s_allocate(rb_cDLHandle); 103 struct dl_handle *dlhandle = DATA_PTR(obj); 104 105 dlhandle->ptr = handle; 106 dlhandle->open = 1; 107 OBJ_FREEZE(obj); 108 return obj; 109} 110 111/* 112 * call-seq: 113 * initialize(lib = nil, flags = DL::RTLD_LAZY | DL::RTLD_GLOBAL) 114 * 115 * Create a new handler that opens library named +lib+ with +flags+. If no 116 * library is specified, RTLD_DEFAULT is used. 117 */ 118VALUE 119rb_dlhandle_initialize(int argc, VALUE argv[], VALUE self) 120{ 121 void *ptr; 122 struct dl_handle *dlhandle; 123 VALUE lib, flag; 124 char *clib; 125 int cflag; 126 const char *err; 127 128 switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){ 129 case 0: 130 clib = NULL; 131 cflag = RTLD_LAZY | RTLD_GLOBAL; 132 break; 133 case 1: 134 clib = NIL_P(lib) ? NULL : StringValuePtr(lib); 135 cflag = RTLD_LAZY | RTLD_GLOBAL; 136 break; 137 case 2: 138 clib = NIL_P(lib) ? NULL : StringValuePtr(lib); 139 cflag = NUM2INT(flag); 140 break; 141 default: 142 rb_bug("rb_dlhandle_new"); 143 } 144 145 rb_secure(2); 146 147#if defined(_WIN32) 148 if( !clib ){ 149 HANDLE rb_libruby_handle(void); 150 ptr = rb_libruby_handle(); 151 } 152 else if( STRCASECMP(clib, "libc") == 0 153# ifdef RUBY_COREDLL 154 || STRCASECMP(clib, RUBY_COREDLL) == 0 155 || STRCASECMP(clib, RUBY_COREDLL".dll") == 0 156# endif 157 ){ 158# ifdef _WIN32_WCE 159 ptr = dlopen("coredll.dll", cflag); 160# else 161 ptr = w32_coredll(); 162# endif 163 } 164 else 165#endif 166 ptr = dlopen(clib, cflag); 167#if defined(HAVE_DLERROR) 168 if( !ptr && (err = dlerror()) ){ 169 rb_raise(rb_eDLError, "%s", err); 170 } 171#else 172 if( !ptr ){ 173 err = dlerror(); 174 rb_raise(rb_eDLError, "%s", err); 175 } 176#endif 177 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 178 if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){ 179 dlclose(dlhandle->ptr); 180 } 181 dlhandle->ptr = ptr; 182 dlhandle->open = 1; 183 dlhandle->enable_close = 0; 184 185 if( rb_block_given_p() ){ 186 rb_ensure(rb_yield, self, rb_dlhandle_close, self); 187 } 188 189 return Qnil; 190} 191 192/* 193 * call-seq: enable_close 194 * 195 * Enable a call to dlclose() when this DL::Handle is garbage collected. 196 */ 197VALUE 198rb_dlhandle_enable_close(VALUE self) 199{ 200 struct dl_handle *dlhandle; 201 202 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 203 dlhandle->enable_close = 1; 204 return Qnil; 205} 206 207/* 208 * call-seq: disable_close 209 * 210 * Disable a call to dlclose() when this DL::Handle is garbage collected. 211 */ 212VALUE 213rb_dlhandle_disable_close(VALUE self) 214{ 215 struct dl_handle *dlhandle; 216 217 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 218 dlhandle->enable_close = 0; 219 return Qnil; 220} 221 222/* 223 * call-seq: close_enabled? 224 * 225 * Returns +true+ if dlclose() will be called when this DL::Handle is 226 * garbage collected. 227 */ 228static VALUE 229rb_dlhandle_close_enabled_p(VALUE self) 230{ 231 struct dl_handle *dlhandle; 232 233 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 234 235 if(dlhandle->enable_close) return Qtrue; 236 return Qfalse; 237} 238 239/* 240 * call-seq: to_i 241 * 242 * Returns the memory address for this handle. 243 */ 244VALUE 245rb_dlhandle_to_i(VALUE self) 246{ 247 struct dl_handle *dlhandle; 248 249 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 250 return PTR2NUM(dlhandle); 251} 252 253static VALUE dlhandle_sym(void *handle, const char *symbol); 254 255/* 256 * Document-method: sym 257 * Document-method: [] 258 * 259 * call-seq: sym(name) 260 * 261 * Get the address as an Integer for the function named +name+. 262 */ 263VALUE 264rb_dlhandle_sym(VALUE self, VALUE sym) 265{ 266 struct dl_handle *dlhandle; 267 268 TypedData_Get_Struct(self, struct dl_handle, &dlhandle_data_type, dlhandle); 269 if( ! dlhandle->open ){ 270 rb_raise(rb_eDLError, "closed handle"); 271 } 272 273 return dlhandle_sym(dlhandle->ptr, StringValueCStr(sym)); 274} 275 276#ifndef RTLD_NEXT 277#define RTLD_NEXT NULL 278#endif 279#ifndef RTLD_DEFAULT 280#define RTLD_DEFAULT NULL 281#endif 282 283/* 284 * Document-method: sym 285 * Document-method: [] 286 * 287 * call-seq: sym(name) 288 * 289 * Get the address as an Integer for the function named +name+. The function 290 * is searched via dlsym on RTLD_NEXT. See man(3) dlsym() for more info. 291 */ 292VALUE 293rb_dlhandle_s_sym(VALUE self, VALUE sym) 294{ 295 return dlhandle_sym(RTLD_NEXT, StringValueCStr(sym)); 296} 297 298static VALUE 299dlhandle_sym(void *handle, const char *name) 300{ 301#if defined(HAVE_DLERROR) 302 const char *err; 303# define CHECK_DLERROR if( err = dlerror() ){ func = 0; } 304#else 305# define CHECK_DLERROR 306#endif 307 void (*func)(); 308 309 rb_secure(2); 310#ifdef HAVE_DLERROR 311 dlerror(); 312#endif 313 func = (void (*)())(VALUE)dlsym(handle, name); 314 CHECK_DLERROR; 315#if defined(FUNC_STDCALL) 316 if( !func ){ 317 int i; 318 int len = (int)strlen(name); 319 char *name_n; 320#if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__) 321 { 322 char *name_a = (char*)xmalloc(len+2); 323 strcpy(name_a, name); 324 name_n = name_a; 325 name_a[len] = 'A'; 326 name_a[len+1] = '\0'; 327 func = dlsym(handle, name_a); 328 CHECK_DLERROR; 329 if( func ) goto found; 330 name_n = xrealloc(name_a, len+6); 331 } 332#else 333 name_n = (char*)xmalloc(len+6); 334#endif 335 memcpy(name_n, name, len); 336 name_n[len++] = '@'; 337 for( i = 0; i < 256; i += 4 ){ 338 sprintf(name_n + len, "%d", i); 339 func = dlsym(handle, name_n); 340 CHECK_DLERROR; 341 if( func ) break; 342 } 343 if( func ) goto found; 344 name_n[len-1] = 'A'; 345 name_n[len++] = '@'; 346 for( i = 0; i < 256; i += 4 ){ 347 sprintf(name_n + len, "%d", i); 348 func = dlsym(handle, name_n); 349 CHECK_DLERROR; 350 if( func ) break; 351 } 352 found: 353 xfree(name_n); 354 } 355#endif 356 if( !func ){ 357 rb_raise(rb_eDLError, "unknown symbol \"%s\"", name); 358 } 359 360 return PTR2NUM(func); 361} 362 363void 364Init_dlhandle(void) 365{ 366 /* 367 * Document-class: DL::Handle 368 * 369 * The DL::Handle is the manner to access the dynamic library 370 * 371 * == Example 372 * 373 * === Setup 374 * 375 * libc_so = "/lib64/libc.so.6" 376 * => "/lib64/libc.so.6" 377 * @handle = DL::Handle.new(libc_so) 378 * => #<DL::Handle:0x00000000d69ef8> 379 * 380 * === Setup, with flags 381 * 382 * libc_so = "/lib64/libc.so.6" 383 * => "/lib64/libc.so.6" 384 * @handle = DL::Handle.new(libc_so, DL::RTLD_LAZY | DL::RTLD_GLOBAL) 385 * => #<DL::Handle:0x00000000d69ef8> 386 * 387 * === Addresses to symbols 388 * 389 * strcpy_addr = @handle['strcpy'] 390 * => 140062278451968 391 * 392 * or 393 * 394 * strcpy_addr = @handle.sym('strcpy') 395 * => 140062278451968 396 * 397 */ 398 rb_cDLHandle = rb_define_class_under(rb_mDL, "Handle", rb_cObject); 399 rb_define_alloc_func(rb_cDLHandle, rb_dlhandle_s_allocate); 400 rb_define_singleton_method(rb_cDLHandle, "sym", rb_dlhandle_s_sym, 1); 401 rb_define_singleton_method(rb_cDLHandle, "[]", rb_dlhandle_s_sym, 1); 402 403 /* Document-const: NEXT 404 * 405 * A predefined pseudo-handle of RTLD_NEXT 406 * 407 * Which will find the next occurrence of a function in the search order 408 * after the current library. 409 */ 410 rb_define_const(rb_cDLHandle, "NEXT", predefined_dlhandle(RTLD_NEXT)); 411 412 /* Document-const: DEFAULT 413 * 414 * A predefined pseudo-handle of RTLD_DEFAULT 415 * 416 * Which will find the first occurrence of the desired symbol using the 417 * default library search order 418 */ 419 rb_define_const(rb_cDLHandle, "DEFAULT", predefined_dlhandle(RTLD_DEFAULT)); 420 rb_define_method(rb_cDLHandle, "initialize", rb_dlhandle_initialize, -1); 421 rb_define_method(rb_cDLHandle, "to_i", rb_dlhandle_to_i, 0); 422 rb_define_method(rb_cDLHandle, "close", rb_dlhandle_close, 0); 423 rb_define_method(rb_cDLHandle, "sym", rb_dlhandle_sym, 1); 424 rb_define_method(rb_cDLHandle, "[]", rb_dlhandle_sym, 1); 425 rb_define_method(rb_cDLHandle, "disable_close", rb_dlhandle_disable_close, 0); 426 rb_define_method(rb_cDLHandle, "enable_close", rb_dlhandle_enable_close, 0); 427 rb_define_method(rb_cDLHandle, "close_enabled?", rb_dlhandle_close_enabled_p, 0); 428} 429 430/* mode: c; tab-with=8; sw=4; ts=8; noexpandtab: */ 431