00001 #include <ruby.h>
00002 #include <fiddle.h>
00003
00004 #define SafeStringValueCStr(v) (rb_check_safe_obj(rb_string_value(&v)), StringValueCStr(v))
00005
00006 VALUE rb_cHandle;
00007
00008 struct dl_handle {
00009 void *ptr;
00010 int open;
00011 int enable_close;
00012 };
00013
00014 #ifdef _WIN32
00015 # ifndef _WIN32_WCE
00016 static void *
00017 w32_coredll(void)
00018 {
00019 MEMORY_BASIC_INFORMATION m;
00020 memset(&m, 0, sizeof(m));
00021 if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL;
00022 return m.AllocationBase;
00023 }
00024 # endif
00025
00026 static int
00027 w32_dlclose(void *ptr)
00028 {
00029 # ifndef _WIN32_WCE
00030 if( ptr == w32_coredll() ) return 0;
00031 # endif
00032 if( FreeLibrary((HMODULE)ptr) ) return 0;
00033 return errno = rb_w32_map_errno(GetLastError());
00034 }
00035 #define dlclose(ptr) w32_dlclose(ptr)
00036 #endif
00037
00038 static void
00039 fiddle_handle_free(void *ptr)
00040 {
00041 struct dl_handle *fiddle_handle = ptr;
00042 if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
00043 dlclose(fiddle_handle->ptr);
00044 }
00045 xfree(ptr);
00046 }
00047
00048 static size_t
00049 fiddle_handle_memsize(const void *ptr)
00050 {
00051 return ptr ? sizeof(struct dl_handle) : 0;
00052 }
00053
00054 static const rb_data_type_t fiddle_handle_data_type = {
00055 "fiddle/handle",
00056 {0, fiddle_handle_free, fiddle_handle_memsize,},
00057 };
00058
00059
00060
00061
00062
00063
00064
00065
00066 static VALUE
00067 rb_fiddle_handle_close(VALUE self)
00068 {
00069 struct dl_handle *fiddle_handle;
00070
00071 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00072 if(fiddle_handle->open) {
00073 int ret = dlclose(fiddle_handle->ptr);
00074 fiddle_handle->open = 0;
00075
00076
00077 if(ret) {
00078 #if defined(HAVE_DLERROR)
00079 rb_raise(rb_eFiddleError, "%s", dlerror());
00080 #else
00081 rb_raise(rb_eFiddleError, "could not close handle");
00082 #endif
00083 }
00084 return INT2NUM(ret);
00085 }
00086 rb_raise(rb_eFiddleError, "dlclose() called too many times");
00087
00088 UNREACHABLE;
00089 }
00090
00091 static VALUE
00092 rb_fiddle_handle_s_allocate(VALUE klass)
00093 {
00094 VALUE obj;
00095 struct dl_handle *fiddle_handle;
00096
00097 obj = TypedData_Make_Struct(rb_cHandle, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00098 fiddle_handle->ptr = 0;
00099 fiddle_handle->open = 0;
00100 fiddle_handle->enable_close = 0;
00101
00102 return obj;
00103 }
00104
00105 static VALUE
00106 predefined_fiddle_handle(void *handle)
00107 {
00108 VALUE obj = rb_fiddle_handle_s_allocate(rb_cHandle);
00109 struct dl_handle *fiddle_handle = DATA_PTR(obj);
00110
00111 fiddle_handle->ptr = handle;
00112 fiddle_handle->open = 1;
00113 OBJ_FREEZE(obj);
00114 return obj;
00115 }
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132 static VALUE
00133 rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
00134 {
00135 void *ptr;
00136 struct dl_handle *fiddle_handle;
00137 VALUE lib, flag;
00138 char *clib;
00139 int cflag;
00140 const char *err;
00141
00142 switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
00143 case 0:
00144 clib = NULL;
00145 cflag = RTLD_LAZY | RTLD_GLOBAL;
00146 break;
00147 case 1:
00148 clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib);
00149 cflag = RTLD_LAZY | RTLD_GLOBAL;
00150 break;
00151 case 2:
00152 clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib);
00153 cflag = NUM2INT(flag);
00154 break;
00155 default:
00156 rb_bug("rb_fiddle_handle_new");
00157 }
00158
00159 rb_secure(2);
00160
00161 #if defined(_WIN32)
00162 if( !clib ){
00163 HANDLE rb_libruby_handle(void);
00164 ptr = rb_libruby_handle();
00165 }
00166 else if( STRCASECMP(clib, "libc") == 0
00167 # ifdef RUBY_COREDLL
00168 || STRCASECMP(clib, RUBY_COREDLL) == 0
00169 || STRCASECMP(clib, RUBY_COREDLL".dll") == 0
00170 # endif
00171 ){
00172 # ifdef _WIN32_WCE
00173 ptr = dlopen("coredll.dll", cflag);
00174 # else
00175 ptr = w32_coredll();
00176 # endif
00177 }
00178 else
00179 #endif
00180 ptr = dlopen(clib, cflag);
00181 #if defined(HAVE_DLERROR)
00182 if( !ptr && (err = dlerror()) ){
00183 rb_raise(rb_eFiddleError, "%s", err);
00184 }
00185 #else
00186 if( !ptr ){
00187 err = dlerror();
00188 rb_raise(rb_eFiddleError, "%s", err);
00189 }
00190 #endif
00191 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00192 if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
00193 dlclose(fiddle_handle->ptr);
00194 }
00195 fiddle_handle->ptr = ptr;
00196 fiddle_handle->open = 1;
00197 fiddle_handle->enable_close = 0;
00198
00199 if( rb_block_given_p() ){
00200 rb_ensure(rb_yield, self, rb_fiddle_handle_close, self);
00201 }
00202
00203 return Qnil;
00204 }
00205
00206
00207
00208
00209
00210
00211 static VALUE
00212 rb_fiddle_handle_enable_close(VALUE self)
00213 {
00214 struct dl_handle *fiddle_handle;
00215
00216 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00217 fiddle_handle->enable_close = 1;
00218 return Qnil;
00219 }
00220
00221
00222
00223
00224
00225
00226 static VALUE
00227 rb_fiddle_handle_disable_close(VALUE self)
00228 {
00229 struct dl_handle *fiddle_handle;
00230
00231 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00232 fiddle_handle->enable_close = 0;
00233 return Qnil;
00234 }
00235
00236
00237
00238
00239
00240
00241
00242
00243 static VALUE
00244 rb_fiddle_handle_close_enabled_p(VALUE self)
00245 {
00246 struct dl_handle *fiddle_handle;
00247
00248 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00249
00250 if(fiddle_handle->enable_close) return Qtrue;
00251 return Qfalse;
00252 }
00253
00254
00255
00256
00257
00258
00259 static VALUE
00260 rb_fiddle_handle_to_i(VALUE self)
00261 {
00262 struct dl_handle *fiddle_handle;
00263
00264 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00265 return PTR2NUM(fiddle_handle);
00266 }
00267
00268 static VALUE fiddle_handle_sym(void *handle, VALUE symbol);
00269
00270
00271
00272
00273
00274
00275
00276
00277 static VALUE
00278 rb_fiddle_handle_sym(VALUE self, VALUE sym)
00279 {
00280 struct dl_handle *fiddle_handle;
00281
00282 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00283 if( ! fiddle_handle->open ){
00284 rb_raise(rb_eFiddleError, "closed handle");
00285 }
00286
00287 return fiddle_handle_sym(fiddle_handle->ptr, sym);
00288 }
00289
00290 #ifndef RTLD_NEXT
00291 #define RTLD_NEXT NULL
00292 #endif
00293 #ifndef RTLD_DEFAULT
00294 #define RTLD_DEFAULT NULL
00295 #endif
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307 static VALUE
00308 rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
00309 {
00310 return fiddle_handle_sym(RTLD_NEXT, sym);
00311 }
00312
00313 static VALUE
00314 fiddle_handle_sym(void *handle, VALUE symbol)
00315 {
00316 #if defined(HAVE_DLERROR)
00317 const char *err;
00318 # define CHECK_DLERROR if( err = dlerror() ){ func = 0; }
00319 #else
00320 # define CHECK_DLERROR
00321 #endif
00322 void (*func)();
00323 const char *name = SafeStringValueCStr(symbol);
00324
00325 rb_secure(2);
00326 #ifdef HAVE_DLERROR
00327 dlerror();
00328 #endif
00329 func = (void (*)())(VALUE)dlsym(handle, name);
00330 CHECK_DLERROR;
00331 #if defined(FUNC_STDCALL)
00332 if( !func ){
00333 int i;
00334 int len = (int)strlen(name);
00335 char *name_n;
00336 #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
00337 {
00338 char *name_a = (char*)xmalloc(len+2);
00339 strcpy(name_a, name);
00340 name_n = name_a;
00341 name_a[len] = 'A';
00342 name_a[len+1] = '\0';
00343 func = dlsym(handle, name_a);
00344 CHECK_DLERROR;
00345 if( func ) goto found;
00346 name_n = xrealloc(name_a, len+6);
00347 }
00348 #else
00349 name_n = (char*)xmalloc(len+6);
00350 #endif
00351 memcpy(name_n, name, len);
00352 name_n[len++] = '@';
00353 for( i = 0; i < 256; i += 4 ){
00354 sprintf(name_n + len, "%d", i);
00355 func = dlsym(handle, name_n);
00356 CHECK_DLERROR;
00357 if( func ) break;
00358 }
00359 if( func ) goto found;
00360 name_n[len-1] = 'A';
00361 name_n[len++] = '@';
00362 for( i = 0; i < 256; i += 4 ){
00363 sprintf(name_n + len, "%d", i);
00364 func = dlsym(handle, name_n);
00365 CHECK_DLERROR;
00366 if( func ) break;
00367 }
00368 found:
00369 xfree(name_n);
00370 }
00371 #endif
00372 if( !func ){
00373 rb_raise(rb_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
00374 }
00375
00376 return PTR2NUM(func);
00377 }
00378
00379 void
00380 Init_fiddle_handle(void)
00381 {
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416 rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject);
00417 rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
00418 rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
00419 rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);
00420
00421
00422
00423
00424
00425
00426
00427
00428 rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT));
00429
00430
00431
00432
00433
00434
00435
00436
00437 rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT));
00438
00439
00440
00441
00442
00443
00444
00445
00446 rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458 rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469 rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW));
00470
00471 rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1);
00472 rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0);
00473 rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
00474 rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1);
00475 rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1);
00476 rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
00477 rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
00478 rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0);
00479 }
00480
00481
00482