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