1/* -*- C -*- 2 * $Id: cfunc.c 36281 2012-07-03 03:10:02Z usa $ 3 */ 4 5#include <ruby/ruby.h> 6#include <ruby/util.h> 7#include <errno.h> 8#include "dl.h" 9 10VALUE rb_cDLCFunc; 11 12static ID id_last_error; 13 14static VALUE 15rb_dl_get_last_error(VALUE self) 16{ 17 return rb_thread_local_aref(rb_thread_current(), id_last_error); 18} 19 20static VALUE 21rb_dl_set_last_error(VALUE self, VALUE val) 22{ 23 rb_thread_local_aset(rb_thread_current(), id_last_error, val); 24 return Qnil; 25} 26 27#if defined(_WIN32) 28#include <windows.h> 29static ID id_win32_last_error; 30 31static VALUE 32rb_dl_get_win32_last_error(VALUE self) 33{ 34 return rb_thread_local_aref(rb_thread_current(), id_win32_last_error); 35} 36 37static VALUE 38rb_dl_set_win32_last_error(VALUE self, VALUE val) 39{ 40 rb_thread_local_aset(rb_thread_current(), id_win32_last_error, val); 41 return Qnil; 42} 43#endif 44 45static void 46dlcfunc_mark(void *ptr) 47{ 48 struct cfunc_data *data = ptr; 49 if (data->wrap) { 50 rb_gc_mark(data->wrap); 51 } 52} 53 54static void 55dlcfunc_free(void *ptr) 56{ 57 struct cfunc_data *data = ptr; 58 if( data->name ){ 59 xfree(data->name); 60 } 61 xfree(data); 62} 63 64static size_t 65dlcfunc_memsize(const void *ptr) 66{ 67 const struct cfunc_data *data = ptr; 68 size_t size = 0; 69 if( data ){ 70 size += sizeof(*data); 71 if( data->name ){ 72 size += strlen(data->name) + 1; 73 } 74 } 75 return size; 76} 77 78const rb_data_type_t dlcfunc_data_type = { 79 "dl/cfunc", 80 {dlcfunc_mark, dlcfunc_free, dlcfunc_memsize,}, 81}; 82 83VALUE 84rb_dlcfunc_new(void (*func)(), int type, const char *name, ID calltype) 85{ 86 VALUE val; 87 struct cfunc_data *data; 88 89 rb_secure(4); 90 if( func ){ 91 val = TypedData_Make_Struct(rb_cDLCFunc, struct cfunc_data, &dlcfunc_data_type, data); 92 data->ptr = (void *)(VALUE)func; 93 data->name = name ? strdup(name) : NULL; 94 data->type = type; 95 data->calltype = calltype; 96 } 97 else{ 98 val = Qnil; 99 } 100 101 return val; 102} 103 104void * 105rb_dlcfunc2ptr(VALUE val) 106{ 107 struct cfunc_data *data; 108 void * func; 109 110 if( rb_typeddata_is_kind_of(val, &dlcfunc_data_type) ){ 111 data = DATA_PTR(val); 112 func = data->ptr; 113 } 114 else if( val == Qnil ){ 115 func = NULL; 116 } 117 else{ 118 rb_raise(rb_eTypeError, "DL::CFunc was expected"); 119 } 120 121 return func; 122} 123 124static VALUE 125rb_dlcfunc_s_allocate(VALUE klass) 126{ 127 VALUE obj; 128 struct cfunc_data *data; 129 130 obj = TypedData_Make_Struct(klass, struct cfunc_data, &dlcfunc_data_type, data); 131 data->ptr = 0; 132 data->name = 0; 133 data->type = 0; 134 data->calltype = CFUNC_CDECL; 135 136 return obj; 137} 138 139int 140rb_dlcfunc_kind_p(VALUE func) 141{ 142 return rb_typeddata_is_kind_of(func, &dlcfunc_data_type); 143} 144 145/* 146 * call-seq: 147 * DL::CFunc.new(address, type=DL::TYPE_VOID, name=nil, calltype=:cdecl) 148 * 149 * Create a new function that points to +address+ with an optional return type 150 * of +type+, a name of +name+ and a calltype of +calltype+. 151 */ 152static VALUE 153rb_dlcfunc_initialize(int argc, VALUE argv[], VALUE self) 154{ 155 VALUE addr, name, type, calltype, addrnum; 156 struct cfunc_data *data; 157 void *saddr; 158 const char *sname; 159 160 rb_scan_args(argc, argv, "13", &addr, &type, &name, &calltype); 161 162 addrnum = rb_Integer(addr); 163 saddr = (void*)(NUM2PTR(addrnum)); 164 sname = NIL_P(name) ? NULL : StringValuePtr(name); 165 166 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, data); 167 if( data->name ) xfree(data->name); 168 data->ptr = saddr; 169 data->name = sname ? strdup(sname) : 0; 170 data->type = NIL_P(type) ? DLTYPE_VOID : NUM2INT(type); 171 data->calltype = NIL_P(calltype) ? CFUNC_CDECL : SYM2ID(calltype); 172 data->wrap = (addrnum == addr) ? 0 : addr; 173 174 return Qnil; 175} 176 177/* 178 * call-seq: 179 * name => str 180 * 181 * Get the name of this function 182 */ 183static VALUE 184rb_dlcfunc_name(VALUE self) 185{ 186 struct cfunc_data *cfunc; 187 188 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 189 return cfunc->name ? rb_tainted_str_new2(cfunc->name) : Qnil; 190} 191 192/* 193 * call-seq: 194 * cfunc.ctype => num 195 * 196 * Get the C function return value type. See DL for a list of constants 197 * corresponding to this method's return value. 198 */ 199static VALUE 200rb_dlcfunc_ctype(VALUE self) 201{ 202 struct cfunc_data *cfunc; 203 204 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 205 return INT2NUM(cfunc->type); 206} 207 208/* 209 * call-seq: 210 * cfunc.ctype = type 211 * 212 * Set the C function return value type to +type+. 213 */ 214static VALUE 215rb_dlcfunc_set_ctype(VALUE self, VALUE ctype) 216{ 217 struct cfunc_data *cfunc; 218 219 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 220 cfunc->type = NUM2INT(ctype); 221 return ctype; 222} 223 224/* 225 * call-seq: 226 * cfunc.calltype => symbol 227 * 228 * Get the call type of this function. 229 */ 230static VALUE 231rb_dlcfunc_calltype(VALUE self) 232{ 233 struct cfunc_data *cfunc; 234 235 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 236 return ID2SYM(cfunc->calltype); 237} 238 239/* 240 * call-seq: 241 * cfunc.calltype = symbol 242 * 243 * Set the call type for this function. 244 */ 245static VALUE 246rb_dlcfunc_set_calltype(VALUE self, VALUE sym) 247{ 248 struct cfunc_data *cfunc; 249 250 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 251 cfunc->calltype = SYM2ID(sym); 252 return sym; 253} 254 255/* 256 * call-seq: 257 * cfunc.ptr 258 * 259 * Get the underlying function pointer as a DL::CPtr object. 260 */ 261static VALUE 262rb_dlcfunc_ptr(VALUE self) 263{ 264 struct cfunc_data *cfunc; 265 266 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 267 return PTR2NUM(cfunc->ptr); 268} 269 270/* 271 * call-seq: 272 * cfunc.ptr = pointer 273 * 274 * Set the underlying function pointer to a DL::CPtr named +pointer+. 275 */ 276static VALUE 277rb_dlcfunc_set_ptr(VALUE self, VALUE addr) 278{ 279 struct cfunc_data *cfunc; 280 281 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 282 cfunc->ptr = NUM2PTR(addr); 283 284 return Qnil; 285} 286 287/* 288 * call-seq: inspect 289 * 290 * Returns a string formatted with an easily readable representation of the 291 * internal state of the DL::CFunc 292 */ 293static VALUE 294rb_dlcfunc_inspect(VALUE self) 295{ 296 VALUE val; 297 struct cfunc_data *cfunc; 298 299 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 300 301 val = rb_sprintf("#<DL::CFunc:%p ptr=%p type=%d name='%s'>", 302 cfunc, 303 cfunc->ptr, 304 cfunc->type, 305 cfunc->name ? cfunc->name : ""); 306 OBJ_TAINT(val); 307 return val; 308} 309 310 311# define DECL_FUNC_CDECL(f,ret,args,val) \ 312 ret (FUNC_CDECL(*(f)))(args) = (ret (FUNC_CDECL(*))(args))(VALUE)(val) 313#ifdef FUNC_STDCALL 314# define DECL_FUNC_STDCALL(f,ret,args,val) \ 315 ret (FUNC_STDCALL(*(f)))(args) = (ret (FUNC_STDCALL(*))(args))(VALUE)(val) 316#endif 317 318#define CALL_CASE switch( RARRAY_LEN(ary) ){ \ 319 CASE(0); break; \ 320 CASE(1); break; CASE(2); break; CASE(3); break; CASE(4); break; CASE(5); break; \ 321 CASE(6); break; CASE(7); break; CASE(8); break; CASE(9); break; CASE(10);break; \ 322 CASE(11);break; CASE(12);break; CASE(13);break; CASE(14);break; CASE(15);break; \ 323 CASE(16);break; CASE(17);break; CASE(18);break; CASE(19);break; CASE(20);break; \ 324 default: rb_raise(rb_eArgError, "too many arguments"); \ 325} 326 327 328#if defined(_MSC_VER) && defined(_M_AMD64) && _MSC_VER >= 1400 && _MSC_VER < 1600 329# pragma optimize("", off) 330#endif 331/* 332 * call-seq: 333 * dlcfunc.call(ary) => some_value 334 * dlcfunc[ary] => some_value 335 * 336 * Calls the function pointer passing in +ary+ as values to the underlying 337 * C function. The return value depends on the ctype. 338 */ 339static VALUE 340rb_dlcfunc_call(VALUE self, VALUE ary) 341{ 342 struct cfunc_data *cfunc; 343 int i; 344 DLSTACK_TYPE stack[DLSTACK_SIZE]; 345 VALUE result = Qnil; 346 347 rb_secure_update(self); 348 349 memset(stack, 0, sizeof(DLSTACK_TYPE) * DLSTACK_SIZE); 350 Check_Type(ary, T_ARRAY); 351 352 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 353 354 if( cfunc->ptr == 0 ){ 355 rb_raise(rb_eDLError, "can't call null-function"); 356 return Qnil; 357 } 358 359 for( i = 0; i < RARRAY_LEN(ary); i++ ){ 360 VALUE arg; 361 if( i >= DLSTACK_SIZE ){ 362 rb_raise(rb_eDLError, "too many arguments (stack overflow)"); 363 } 364 arg = rb_to_int(RARRAY_PTR(ary)[i]); 365 rb_check_safe_obj(arg); 366 if (FIXNUM_P(arg)) { 367 stack[i] = (DLSTACK_TYPE)FIX2LONG(arg); 368 } 369 else if (RB_TYPE_P(arg, T_BIGNUM)) { 370#if SIZEOF_VOIDP == SIZEOF_LONG 371 stack[i] = (DLSTACK_TYPE)rb_big2ulong_pack(arg); 372#else 373 stack[i] = (DLSTACK_TYPE)rb_big2ull(arg); 374#endif 375 } 376 else { 377 Check_Type(arg, T_FIXNUM); 378 } 379 } 380 381 /* calltype == CFUNC_CDECL */ 382 if( cfunc->calltype == CFUNC_CDECL 383#ifndef FUNC_STDCALL 384 || cfunc->calltype == CFUNC_STDCALL 385#endif 386 ){ 387 switch( cfunc->type ){ 388 case DLTYPE_VOID: 389#define CASE(n) case n: { \ 390 DECL_FUNC_CDECL(f,void,DLSTACK_PROTO##n,cfunc->ptr); \ 391 f(DLSTACK_ARGS##n(stack)); \ 392 result = Qnil; \ 393} 394 CALL_CASE; 395#undef CASE 396 break; 397 case DLTYPE_VOIDP: 398#define CASE(n) case n: { \ 399 DECL_FUNC_CDECL(f,void*,DLSTACK_PROTO##n,cfunc->ptr); \ 400 void * ret; \ 401 ret = f(DLSTACK_ARGS##n(stack)); \ 402 result = PTR2NUM(ret); \ 403} 404 CALL_CASE; 405#undef CASE 406 break; 407 case DLTYPE_CHAR: 408#define CASE(n) case n: { \ 409 DECL_FUNC_CDECL(f,char,DLSTACK_PROTO##n,cfunc->ptr); \ 410 char ret; \ 411 ret = f(DLSTACK_ARGS##n(stack)); \ 412 result = CHR2FIX(ret); \ 413} 414 CALL_CASE; 415#undef CASE 416 break; 417 case DLTYPE_SHORT: 418#define CASE(n) case n: { \ 419 DECL_FUNC_CDECL(f,short,DLSTACK_PROTO##n,cfunc->ptr); \ 420 short ret; \ 421 ret = f(DLSTACK_ARGS##n(stack)); \ 422 result = INT2NUM((int)ret); \ 423} 424 CALL_CASE; 425#undef CASE 426 break; 427 case DLTYPE_INT: 428#define CASE(n) case n: { \ 429 DECL_FUNC_CDECL(f,int,DLSTACK_PROTO##n,cfunc->ptr); \ 430 int ret; \ 431 ret = f(DLSTACK_ARGS##n(stack)); \ 432 result = INT2NUM(ret); \ 433} 434 CALL_CASE; 435#undef CASE 436 break; 437 case DLTYPE_LONG: 438#define CASE(n) case n: { \ 439 DECL_FUNC_CDECL(f,long,DLSTACK_PROTO##n,cfunc->ptr); \ 440 long ret; \ 441 ret = f(DLSTACK_ARGS##n(stack)); \ 442 result = LONG2NUM(ret); \ 443} 444 CALL_CASE; 445#undef CASE 446 break; 447#if HAVE_LONG_LONG /* used in ruby.h */ 448 case DLTYPE_LONG_LONG: 449#define CASE(n) case n: { \ 450 DECL_FUNC_CDECL(f,LONG_LONG,DLSTACK_PROTO##n,cfunc->ptr); \ 451 LONG_LONG ret; \ 452 ret = f(DLSTACK_ARGS##n(stack)); \ 453 result = LL2NUM(ret); \ 454} 455 CALL_CASE; 456#undef CASE 457 break; 458#endif 459 case DLTYPE_FLOAT: 460#define CASE(n) case n: { \ 461 DECL_FUNC_CDECL(f,float,DLSTACK_PROTO##n,cfunc->ptr); \ 462 float ret; \ 463 ret = f(DLSTACK_ARGS##n(stack)); \ 464 result = rb_float_new(ret); \ 465} 466 CALL_CASE; 467#undef CASE 468 break; 469 case DLTYPE_DOUBLE: 470#define CASE(n) case n: { \ 471 DECL_FUNC_CDECL(f,double,DLSTACK_PROTO##n,cfunc->ptr); \ 472 double ret; \ 473 ret = f(DLSTACK_ARGS##n(stack)); \ 474 result = rb_float_new(ret); \ 475} 476 CALL_CASE; 477#undef CASE 478 break; 479 default: 480 rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type); 481 } 482 } 483#ifdef FUNC_STDCALL 484 else if( cfunc->calltype == CFUNC_STDCALL ){ 485 /* calltype == CFUNC_STDCALL */ 486 switch( cfunc->type ){ 487 case DLTYPE_VOID: 488#define CASE(n) case n: { \ 489 DECL_FUNC_STDCALL(f,void,DLSTACK_PROTO##n##_,cfunc->ptr); \ 490 f(DLSTACK_ARGS##n(stack)); \ 491 result = Qnil; \ 492} 493 CALL_CASE; 494#undef CASE 495 break; 496 case DLTYPE_VOIDP: 497#define CASE(n) case n: { \ 498 DECL_FUNC_STDCALL(f,void*,DLSTACK_PROTO##n##_,cfunc->ptr); \ 499 void * ret; \ 500 ret = f(DLSTACK_ARGS##n(stack)); \ 501 result = PTR2NUM(ret); \ 502} 503 CALL_CASE; 504#undef CASE 505 break; 506 case DLTYPE_CHAR: 507#define CASE(n) case n: { \ 508 DECL_FUNC_STDCALL(f,char,DLSTACK_PROTO##n##_,cfunc->ptr); \ 509 char ret; \ 510 ret = f(DLSTACK_ARGS##n(stack)); \ 511 result = CHR2FIX(ret); \ 512} 513 CALL_CASE; 514#undef CASE 515 break; 516 case DLTYPE_SHORT: 517#define CASE(n) case n: { \ 518 DECL_FUNC_STDCALL(f,short,DLSTACK_PROTO##n##_,cfunc->ptr); \ 519 short ret; \ 520 ret = f(DLSTACK_ARGS##n(stack)); \ 521 result = INT2NUM((int)ret); \ 522} 523 CALL_CASE; 524#undef CASE 525 break; 526 case DLTYPE_INT: 527#define CASE(n) case n: { \ 528 DECL_FUNC_STDCALL(f,int,DLSTACK_PROTO##n##_,cfunc->ptr); \ 529 int ret; \ 530 ret = f(DLSTACK_ARGS##n(stack)); \ 531 result = INT2NUM(ret); \ 532} 533 CALL_CASE; 534#undef CASE 535 break; 536 case DLTYPE_LONG: 537#define CASE(n) case n: { \ 538 DECL_FUNC_STDCALL(f,long,DLSTACK_PROTO##n##_,cfunc->ptr); \ 539 long ret; \ 540 ret = f(DLSTACK_ARGS##n(stack)); \ 541 result = LONG2NUM(ret); \ 542} 543 CALL_CASE; 544#undef CASE 545 break; 546#if HAVE_LONG_LONG /* used in ruby.h */ 547 case DLTYPE_LONG_LONG: 548#define CASE(n) case n: { \ 549 DECL_FUNC_STDCALL(f,LONG_LONG,DLSTACK_PROTO##n##_,cfunc->ptr); \ 550 LONG_LONG ret; \ 551 ret = f(DLSTACK_ARGS##n(stack)); \ 552 result = LL2NUM(ret); \ 553} 554 CALL_CASE; 555#undef CASE 556 break; 557#endif 558 case DLTYPE_FLOAT: 559#define CASE(n) case n: { \ 560 DECL_FUNC_STDCALL(f,float,DLSTACK_PROTO##n##_,cfunc->ptr); \ 561 float ret; \ 562 ret = f(DLSTACK_ARGS##n(stack)); \ 563 result = rb_float_new(ret); \ 564} 565 CALL_CASE; 566#undef CASE 567 break; 568 case DLTYPE_DOUBLE: 569#define CASE(n) case n: { \ 570 DECL_FUNC_STDCALL(f,double,DLSTACK_PROTO##n##_,cfunc->ptr); \ 571 double ret; \ 572 ret = f(DLSTACK_ARGS##n(stack)); \ 573 result = rb_float_new(ret); \ 574} 575 CALL_CASE; 576#undef CASE 577 break; 578 default: 579 rb_raise(rb_eDLTypeError, "unknown type %d", cfunc->type); 580 } 581 } 582#endif 583 else{ 584 const char *name = rb_id2name(cfunc->calltype); 585 if( name ){ 586 rb_raise(rb_eDLError, "unsupported call type: %s", 587 name); 588 } 589 else{ 590 rb_raise(rb_eDLError, "unsupported call type: %"PRIxVALUE, 591 cfunc->calltype); 592 } 593 } 594 595 rb_dl_set_last_error(self, INT2NUM(errno)); 596#if defined(_WIN32) 597 rb_dl_set_win32_last_error(self, INT2NUM(GetLastError())); 598#endif 599 600 return result; 601} 602#if defined(_MSC_VER) && defined(_M_AMD64) && _MSC_VER >= 1400 && _MSC_VER < 1600 603# pragma optimize("", on) 604#endif 605 606/* 607 * call-seq: 608 * dlfunc.to_i => integer 609 * 610 * Returns the memory location of this function pointer as an integer. 611 */ 612static VALUE 613rb_dlcfunc_to_i(VALUE self) 614{ 615 struct cfunc_data *cfunc; 616 617 TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc); 618 return PTR2NUM(cfunc->ptr); 619} 620 621void 622Init_dlcfunc(void) 623{ 624 id_last_error = rb_intern("__DL2_LAST_ERROR__"); 625#if defined(_WIN32) 626 id_win32_last_error = rb_intern("__DL2_WIN32_LAST_ERROR__"); 627#endif 628 629 /* 630 * Document-class: DL::CFunc 631 * 632 * A direct accessor to a function in a C library 633 * 634 * == Example 635 * 636 * libc_so = "/lib64/libc.so.6" 637 * => "/lib64/libc.so.6" 638 * libc = DL::dlopen(libc_so) 639 * => #<DL::Handle:0x00000000e05b00> 640 * @cfunc = DL::CFunc.new(libc,['strcpy'], DL::TYPE_VOIDP, 'strcpy') 641 * => #<DL::CFunc:0x000000012daec0 ptr=0x007f62ca5a8300 type=1 name='strcpy'> 642 * 643 */ 644 rb_cDLCFunc = rb_define_class_under(rb_mDL, "CFunc", rb_cObject); 645 rb_define_alloc_func(rb_cDLCFunc, rb_dlcfunc_s_allocate); 646 647 /* 648 * Document-method: last_error 649 * 650 * Returns the last error for the current executing thread 651 */ 652 rb_define_module_function(rb_cDLCFunc, "last_error", rb_dl_get_last_error, 0); 653#if defined(_WIN32) 654 655 /* 656 * Document-method: win32_last_error 657 * 658 * Returns the last win32 error for the current executing thread 659 */ 660 rb_define_module_function(rb_cDLCFunc, "win32_last_error", rb_dl_get_win32_last_error, 0); 661#endif 662 rb_define_method(rb_cDLCFunc, "initialize", rb_dlcfunc_initialize, -1); 663 rb_define_method(rb_cDLCFunc, "call", rb_dlcfunc_call, 1); 664 rb_define_method(rb_cDLCFunc, "[]", rb_dlcfunc_call, 1); 665 rb_define_method(rb_cDLCFunc, "name", rb_dlcfunc_name, 0); 666 rb_define_method(rb_cDLCFunc, "ctype", rb_dlcfunc_ctype, 0); 667 rb_define_method(rb_cDLCFunc, "ctype=", rb_dlcfunc_set_ctype, 1); 668 rb_define_method(rb_cDLCFunc, "calltype", rb_dlcfunc_calltype, 0); 669 rb_define_method(rb_cDLCFunc, "calltype=", rb_dlcfunc_set_calltype, 1); 670 rb_define_method(rb_cDLCFunc, "ptr", rb_dlcfunc_ptr, 0); 671 rb_define_method(rb_cDLCFunc, "ptr=", rb_dlcfunc_set_ptr, 1); 672 rb_define_method(rb_cDLCFunc, "inspect", rb_dlcfunc_inspect, 0); 673 rb_define_method(rb_cDLCFunc, "to_s", rb_dlcfunc_inspect, 0); 674 rb_define_method(rb_cDLCFunc, "to_i", rb_dlcfunc_to_i, 0); 675} 676