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
00059
00060
00061
00062
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
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
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130 static VALUE
00131 rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
00132 {
00133 void *ptr;
00134 struct dl_handle *fiddle_handle;
00135 VALUE lib, flag;
00136 char *clib;
00137 int cflag;
00138 const char *err;
00139
00140 switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
00141 case 0:
00142 clib = NULL;
00143 cflag = RTLD_LAZY | RTLD_GLOBAL;
00144 break;
00145 case 1:
00146 clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
00147 cflag = RTLD_LAZY | RTLD_GLOBAL;
00148 break;
00149 case 2:
00150 clib = NIL_P(lib) ? NULL : StringValuePtr(lib);
00151 cflag = NUM2INT(flag);
00152 break;
00153 default:
00154 rb_bug("rb_fiddle_handle_new");
00155 }
00156
00157 rb_secure(2);
00158
00159 #if defined(_WIN32)
00160 if( !clib ){
00161 HANDLE rb_libruby_handle(void);
00162 ptr = rb_libruby_handle();
00163 }
00164 else if( STRCASECMP(clib, "libc") == 0
00165 # ifdef RUBY_COREDLL
00166 || STRCASECMP(clib, RUBY_COREDLL) == 0
00167 || STRCASECMP(clib, RUBY_COREDLL".dll") == 0
00168 # endif
00169 ){
00170 # ifdef _WIN32_WCE
00171 ptr = dlopen("coredll.dll", cflag);
00172 # else
00173 ptr = w32_coredll();
00174 # endif
00175 }
00176 else
00177 #endif
00178 ptr = dlopen(clib, cflag);
00179 #if defined(HAVE_DLERROR)
00180 if( !ptr && (err = dlerror()) ){
00181 rb_raise(rb_eFiddleError, "%s", err);
00182 }
00183 #else
00184 if( !ptr ){
00185 err = dlerror();
00186 rb_raise(rb_eFiddleError, "%s", err);
00187 }
00188 #endif
00189 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00190 if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
00191 dlclose(fiddle_handle->ptr);
00192 }
00193 fiddle_handle->ptr = ptr;
00194 fiddle_handle->open = 1;
00195 fiddle_handle->enable_close = 0;
00196
00197 if( rb_block_given_p() ){
00198 rb_ensure(rb_yield, self, rb_fiddle_handle_close, self);
00199 }
00200
00201 return Qnil;
00202 }
00203
00204
00205
00206
00207
00208
00209 static VALUE
00210 rb_fiddle_handle_enable_close(VALUE self)
00211 {
00212 struct dl_handle *fiddle_handle;
00213
00214 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00215 fiddle_handle->enable_close = 1;
00216 return Qnil;
00217 }
00218
00219
00220
00221
00222
00223
00224 static VALUE
00225 rb_fiddle_handle_disable_close(VALUE self)
00226 {
00227 struct dl_handle *fiddle_handle;
00228
00229 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00230 fiddle_handle->enable_close = 0;
00231 return Qnil;
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241 static VALUE
00242 rb_fiddle_handle_close_enabled_p(VALUE self)
00243 {
00244 struct dl_handle *fiddle_handle;
00245
00246 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00247
00248 if(fiddle_handle->enable_close) return Qtrue;
00249 return Qfalse;
00250 }
00251
00252
00253
00254
00255
00256
00257 static VALUE
00258 rb_fiddle_handle_to_i(VALUE self)
00259 {
00260 struct dl_handle *fiddle_handle;
00261
00262 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00263 return PTR2NUM(fiddle_handle);
00264 }
00265
00266 static VALUE fiddle_handle_sym(void *handle, const char *symbol);
00267
00268
00269
00270
00271
00272
00273
00274
00275 static VALUE
00276 rb_fiddle_handle_sym(VALUE self, VALUE sym)
00277 {
00278 struct dl_handle *fiddle_handle;
00279
00280 TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
00281 if( ! fiddle_handle->open ){
00282 rb_raise(rb_eFiddleError, "closed handle");
00283 }
00284
00285 return fiddle_handle_sym(fiddle_handle->ptr, StringValueCStr(sym));
00286 }
00287
00288 #ifndef RTLD_NEXT
00289 #define RTLD_NEXT NULL
00290 #endif
00291 #ifndef RTLD_DEFAULT
00292 #define RTLD_DEFAULT NULL
00293 #endif
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305 static VALUE
00306 rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
00307 {
00308 return fiddle_handle_sym(RTLD_NEXT, StringValueCStr(sym));
00309 }
00310
00311 static VALUE
00312 fiddle_handle_sym(void *handle, const char *name)
00313 {
00314 #if defined(HAVE_DLERROR)
00315 const char *err;
00316 # define CHECK_DLERROR if( err = dlerror() ){ func = 0; }
00317 #else
00318 # define CHECK_DLERROR
00319 #endif
00320 void (*func)();
00321
00322 rb_secure(2);
00323 #ifdef HAVE_DLERROR
00324 dlerror();
00325 #endif
00326 func = (void (*)())(VALUE)dlsym(handle, name);
00327 CHECK_DLERROR;
00328 #if defined(FUNC_STDCALL)
00329 if( !func ){
00330 int i;
00331 int len = (int)strlen(name);
00332 char *name_n;
00333 #if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
00334 {
00335 char *name_a = (char*)xmalloc(len+2);
00336 strcpy(name_a, name);
00337 name_n = name_a;
00338 name_a[len] = 'A';
00339 name_a[len+1] = '\0';
00340 func = dlsym(handle, name_a);
00341 CHECK_DLERROR;
00342 if( func ) goto found;
00343 name_n = xrealloc(name_a, len+6);
00344 }
00345 #else
00346 name_n = (char*)xmalloc(len+6);
00347 #endif
00348 memcpy(name_n, name, len);
00349 name_n[len++] = '@';
00350 for( i = 0; i < 256; i += 4 ){
00351 sprintf(name_n + len, "%d", i);
00352 func = dlsym(handle, name_n);
00353 CHECK_DLERROR;
00354 if( func ) break;
00355 }
00356 if( func ) goto found;
00357 name_n[len-1] = 'A';
00358 name_n[len++] = '@';
00359 for( i = 0; i < 256; i += 4 ){
00360 sprintf(name_n + len, "%d", i);
00361 func = dlsym(handle, name_n);
00362 CHECK_DLERROR;
00363 if( func ) break;
00364 }
00365 found:
00366 xfree(name_n);
00367 }
00368 #endif
00369 if( !func ){
00370 rb_raise(rb_eFiddleError, "unknown symbol \"%s\"", name);
00371 }
00372
00373 return PTR2NUM(func);
00374 }
00375
00376 void
00377 Init_fiddle_handle(void)
00378 {
00379
00380
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 rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject);
00414 rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
00415 rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
00416 rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);
00417
00418
00419
00420
00421
00422
00423
00424
00425 rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT));
00426
00427
00428
00429
00430
00431
00432
00433
00434 rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT));
00435
00436
00437
00438
00439
00440
00441
00442
00443 rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455 rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466 rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW));
00467
00468 rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1);
00469 rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0);
00470 rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
00471 rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1);
00472 rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1);
00473 rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
00474 rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
00475 rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0);
00476 }
00477
00478
00479