From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 16 Mar 2020 18:49:26 +0900 Subject: Fresh start --- jni/ruby/ext/fiddle/function.c | 279 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 jni/ruby/ext/fiddle/function.c (limited to 'jni/ruby/ext/fiddle/function.c') diff --git a/jni/ruby/ext/fiddle/function.c b/jni/ruby/ext/fiddle/function.c new file mode 100644 index 0000000..e0da8b6 --- /dev/null +++ b/jni/ruby/ext/fiddle/function.c @@ -0,0 +1,279 @@ +#include + +#ifdef PRIsVALUE +# define RB_OBJ_CLASSNAME(obj) rb_obj_class(obj) +# define RB_OBJ_STRING(obj) (obj) +#else +# define PRIsVALUE "s" +# define RB_OBJ_CLASSNAME(obj) rb_obj_classname(obj) +# define RB_OBJ_STRING(obj) StringValueCStr(obj) +#endif + +VALUE cFiddleFunction; + +#define MAX_ARGS (SIZE_MAX / (sizeof(void *) + sizeof(fiddle_generic)) - 1) + +#define Check_Max_Args(name, len) \ + if ((size_t)(len) < MAX_ARGS) { \ + /* OK */ \ + } \ + else { \ + rb_raise(rb_eTypeError, \ + name" is so large that it can cause integer overflow (%d)", \ + (len)); \ + } + +static void +deallocate(void *p) +{ + ffi_cif *ptr = p; + if (ptr->arg_types) xfree(ptr->arg_types); + xfree(ptr); +} + +static size_t +function_memsize(const void *p) +{ + /* const */ffi_cif *ptr = (ffi_cif *)p; + size_t size = 0; + + if (ptr) { + size += sizeof(*ptr); +#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API + size += ffi_raw_size(ptr); +#endif + } + return size; +} + +const rb_data_type_t function_data_type = { + "fiddle/function", + {0, deallocate, function_memsize,}, +}; + +static VALUE +allocate(VALUE klass) +{ + ffi_cif * cif; + + return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif); +} + +VALUE +rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type) +{ + VALUE argv[3]; + + argv[0] = address; + argv[1] = arg_types; + argv[2] = ret_type; + + return rb_class_new_instance(3, argv, cFiddleFunction); +} + +static int +parse_keyword_arg_i(VALUE key, VALUE value, VALUE self) +{ + if (key == ID2SYM(rb_intern("name"))) { + rb_iv_set(self, "@name", value); + } else { + rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE, + RB_OBJ_STRING(key)); + } + return ST_CONTINUE; +} + +static VALUE +initialize(int argc, VALUE argv[], VALUE self) +{ + ffi_cif * cif; + ffi_type **arg_types; + ffi_status result; + VALUE ptr, args, ret_type, abi, kwds; + int i; + + rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds); + if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI); + + Check_Type(args, T_ARRAY); + Check_Max_Args("args", RARRAY_LENINT(args)); + + rb_iv_set(self, "@ptr", ptr); + rb_iv_set(self, "@args", args); + rb_iv_set(self, "@return_type", ret_type); + rb_iv_set(self, "@abi", abi); + + if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self); + + TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); + + arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *)); + + for (i = 0; i < RARRAY_LEN(args); i++) { + int type = NUM2INT(RARRAY_PTR(args)[i]); + arg_types[i] = INT2FFI_TYPE(type); + } + arg_types[RARRAY_LEN(args)] = NULL; + + result = ffi_prep_cif ( + cif, + NUM2INT(abi), + RARRAY_LENINT(args), + INT2FFI_TYPE(NUM2INT(ret_type)), + arg_types); + + if (result) + rb_raise(rb_eRuntimeError, "error creating CIF %d", result); + + return self; +} + +static VALUE +function_call(int argc, VALUE argv[], VALUE self) +{ + ffi_cif * cif; + fiddle_generic retval; + fiddle_generic *generic_args; + void **values; + VALUE cfunc, types, cPointer; + int i; + VALUE alloc_buffer = 0; + + cfunc = rb_iv_get(self, "@ptr"); + types = rb_iv_get(self, "@args"); + cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); + + Check_Max_Args("number of arguments", argc); + if(argc != RARRAY_LENINT(types)) { + rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", + argc, RARRAY_LENINT(types)); + } + + TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); + + if (rb_safe_level() >= 1) { + for (i = 0; i < argc; i++) { + VALUE src = argv[i]; + if (OBJ_TAINTED(src)) { + rb_raise(rb_eSecurityError, "tainted parameter not allowed"); + } + } + } + + generic_args = ALLOCV(alloc_buffer, + (size_t)(argc + 1) * sizeof(void *) + (size_t)argc * sizeof(fiddle_generic)); + values = (void **)((char *)generic_args + (size_t)argc * sizeof(fiddle_generic)); + + for (i = 0; i < argc; i++) { + VALUE type = RARRAY_PTR(types)[i]; + VALUE src = argv[i]; + + if(NUM2INT(type) == TYPE_VOIDP) { + if(NIL_P(src)) { + src = INT2FIX(0); + } else if(cPointer != CLASS_OF(src)) { + src = rb_funcall(cPointer, rb_intern("[]"), 1, src); + } + src = rb_Integer(src); + } + + VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]); + values[i] = (void *)&generic_args[i]; + } + values[argc] = NULL; + + ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values); + + rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno)); +#if defined(_WIN32) + rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno)); +#endif + + ALLOCV_END(alloc_buffer); + + return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval); +} + +void +Init_fiddle_function(void) +{ + /* + * Document-class: Fiddle::Function + * + * == Description + * + * A representation of a C function + * + * == Examples + * + * === 'strcpy' + * + * @libc = Fiddle.dlopen "/lib/libc.so.6" + * #=> # + * f = Fiddle::Function.new( + * @libc['strcpy'], + * [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], + * Fiddle::TYPE_VOIDP) + * #=> # + * buff = "000" + * #=> "000" + * str = f.call(buff, "123") + * #=> # + * str.to_s + * => "123" + * + * === ABI check + * + * @libc = Fiddle.dlopen "/lib/libc.so.6" + * #=> # + * f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) + * #=> # + * f.abi == Fiddle::Function::DEFAULT + * #=> true + */ + cFiddleFunction = rb_define_class_under(mFiddle, "Function", rb_cObject); + + /* + * Document-const: DEFAULT + * + * Default ABI + * + */ + rb_define_const(cFiddleFunction, "DEFAULT", INT2NUM(FFI_DEFAULT_ABI)); + +#ifdef HAVE_CONST_FFI_STDCALL + /* + * Document-const: STDCALL + * + * FFI implementation of WIN32 stdcall convention + * + */ + rb_define_const(cFiddleFunction, "STDCALL", INT2NUM(FFI_STDCALL)); +#endif + + rb_define_alloc_func(cFiddleFunction, allocate); + + /* + * Document-method: call + * + * Calls the constructed Function, with +args+ + * + * For an example see Fiddle::Function + * + */ + rb_define_method(cFiddleFunction, "call", function_call, -1); + + /* + * Document-method: new + * call-seq: new(ptr, args, ret_type, abi = DEFAULT) + * + * Constructs a Function object. + * * +ptr+ is a referenced function, of a Fiddle::Handle + * * +args+ is an Array of arguments, passed to the +ptr+ function + * * +ret_type+ is the return type of the function + * * +abi+ is the ABI of the function + * + */ + rb_define_method(cFiddleFunction, "initialize", initialize, -1); +} +/* vim: set noet sws=4 sw=4: */ -- cgit v1.2.3