diff options
Diffstat (limited to 'ext/fiddle')
30 files changed, 7569 insertions, 0 deletions
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c new file mode 100644 index 0000000000..c08ec5940d --- /dev/null +++ b/ext/fiddle/closure.c @@ -0,0 +1,390 @@ +#include <fiddle.h> +#include <ruby/thread.h> + +int ruby_thread_has_gvl_p(void); /* from internal.h */ + +VALUE cFiddleClosure; + +typedef struct { + void * code; + ffi_closure *pcl; + ffi_cif cif; + int argc; + ffi_type **argv; +} fiddle_closure; + +#if defined(__OpenBSD__) +# define USE_FFI_CLOSURE_ALLOC 0 +#endif + +#if defined(USE_FFI_CLOSURE_ALLOC) +#elif !defined(HAVE_FFI_CLOSURE_ALLOC) +# define USE_FFI_CLOSURE_ALLOC 0 +#else +# define USE_FFI_CLOSURE_ALLOC 1 +#endif + +static void +dealloc(void * ptr) +{ + fiddle_closure * cls = (fiddle_closure *)ptr; +#if USE_FFI_CLOSURE_ALLOC + ffi_closure_free(cls->pcl); +#else + munmap(cls->pcl, sizeof(*cls->pcl)); +#endif + if (cls->argv) xfree(cls->argv); + xfree(cls); +} + +static size_t +closure_memsize(const void * ptr) +{ + fiddle_closure * cls = (fiddle_closure *)ptr; + size_t size = 0; + + size += sizeof(*cls); +#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API + size += ffi_raw_size(&cls->cif); +#endif + size += sizeof(*cls->argv); + size += sizeof(ffi_closure); + + return size; +} + +const rb_data_type_t closure_data_type = { + "fiddle/closure", + {0, dealloc, closure_memsize,}, +}; + +struct callback_args { + ffi_cif *cif; + void *resp; + void **args; + void *ctx; +}; + +static void * +with_gvl_callback(void *ptr) +{ + struct callback_args *x = ptr; + + VALUE self = (VALUE)x->ctx; + VALUE rbargs = rb_iv_get(self, "@args"); + VALUE ctype = rb_iv_get(self, "@ctype"); + int argc = RARRAY_LENINT(rbargs); + VALUE params = rb_ary_tmp_new(argc); + VALUE ret; + VALUE cPointer; + int i, type; + + cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); + + for (i = 0; i < argc; i++) { + type = NUM2INT(RARRAY_AREF(rbargs, i)); + switch (type) { + case TYPE_VOID: + argc = 0; + break; + case TYPE_INT: + rb_ary_push(params, INT2NUM(*(int *)x->args[i])); + break; + case -TYPE_INT: + rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i])); + break; + case TYPE_VOIDP: + rb_ary_push(params, + rb_funcall(cPointer, rb_intern("[]"), 1, + PTR2NUM(*(void **)x->args[i]))); + break; + case TYPE_LONG: + rb_ary_push(params, LONG2NUM(*(long *)x->args[i])); + break; + case -TYPE_LONG: + rb_ary_push(params, ULONG2NUM(*(unsigned long *)x->args[i])); + break; + case TYPE_CHAR: + rb_ary_push(params, INT2NUM(*(signed char *)x->args[i])); + break; + case -TYPE_CHAR: + rb_ary_push(params, UINT2NUM(*(unsigned char *)x->args[i])); + break; + case TYPE_SHORT: + rb_ary_push(params, INT2NUM(*(signed short *)x->args[i])); + break; + case -TYPE_SHORT: + rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i])); + break; + case TYPE_DOUBLE: + rb_ary_push(params, rb_float_new(*(double *)x->args[i])); + break; + case TYPE_FLOAT: + rb_ary_push(params, rb_float_new(*(float *)x->args[i])); + break; +#if HAVE_LONG_LONG + case TYPE_LONG_LONG: + rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i])); + break; + case -TYPE_LONG_LONG: + rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i])); + break; +#endif + case TYPE_CONST_STRING: + rb_ary_push(params, + rb_str_new_cstr(*((const char **)(x->args[i])))); + break; + default: + rb_raise(rb_eRuntimeError, "closure args: %d", type); + } + } + + ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_CONST_PTR(params)); + RB_GC_GUARD(params); + + type = NUM2INT(ctype); + switch (type) { + case TYPE_VOID: + break; + case TYPE_LONG: + *(long *)x->resp = NUM2LONG(ret); + break; + case -TYPE_LONG: + *(unsigned long *)x->resp = NUM2ULONG(ret); + break; + case TYPE_CHAR: + case TYPE_SHORT: + case TYPE_INT: + *(ffi_sarg *)x->resp = NUM2INT(ret); + break; + case -TYPE_CHAR: + case -TYPE_SHORT: + case -TYPE_INT: + *(ffi_arg *)x->resp = NUM2UINT(ret); + break; + case TYPE_VOIDP: + *(void **)x->resp = NUM2PTR(ret); + break; + case TYPE_DOUBLE: + *(double *)x->resp = NUM2DBL(ret); + break; + case TYPE_FLOAT: + *(float *)x->resp = (float)NUM2DBL(ret); + break; +#if HAVE_LONG_LONG + case TYPE_LONG_LONG: + *(LONG_LONG *)x->resp = NUM2LL(ret); + break; + case -TYPE_LONG_LONG: + *(unsigned LONG_LONG *)x->resp = NUM2ULL(ret); + break; +#endif + case TYPE_CONST_STRING: + /* Dangerous. Callback must keep reference of the String. */ + *((const char **)(x->resp)) = StringValueCStr(ret); + break; + default: + rb_raise(rb_eRuntimeError, "closure retval: %d", type); + } + return 0; +} + +static void +callback(ffi_cif *cif, void *resp, void **args, void *ctx) +{ + struct callback_args x; + + x.cif = cif; + x.resp = resp; + x.args = args; + x.ctx = ctx; + + if (ruby_thread_has_gvl_p()) { + (void)with_gvl_callback(&x); + } else { + (void)rb_thread_call_with_gvl(with_gvl_callback, &x); + } +} + +static VALUE +allocate(VALUE klass) +{ + fiddle_closure * closure; + + VALUE i = TypedData_Make_Struct(klass, fiddle_closure, + &closure_data_type, closure); + +#if USE_FFI_CLOSURE_ALLOC + closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code); +#else + closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); +#endif + + return i; +} + +typedef struct { + VALUE self; + int argc; + VALUE *argv; +} initialize_data; + +static VALUE +initialize_body(VALUE user_data) +{ + initialize_data *data = (initialize_data *)user_data; + VALUE ret; + VALUE args; + VALUE normalized_args; + VALUE abi; + fiddle_closure * cl; + ffi_cif * cif; + ffi_closure *pcl; + ffi_status result; + int i, argc; + + if (2 == rb_scan_args(data->argc, data->argv, "21", &ret, &args, &abi)) + abi = INT2NUM(FFI_DEFAULT_ABI); + + Check_Type(args, T_ARRAY); + + argc = RARRAY_LENINT(args); + + TypedData_Get_Struct(data->self, fiddle_closure, &closure_data_type, cl); + + cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *)); + + normalized_args = rb_ary_new_capa(argc); + for (i = 0; i < argc; i++) { + VALUE arg = rb_fiddle_type_ensure(RARRAY_AREF(args, i)); + rb_ary_push(normalized_args, arg); + cl->argv[i] = rb_fiddle_int_to_ffi_type(NUM2INT(arg)); + } + cl->argv[argc] = NULL; + + ret = rb_fiddle_type_ensure(ret); + rb_iv_set(data->self, "@ctype", ret); + rb_iv_set(data->self, "@args", normalized_args); + + cif = &cl->cif; + pcl = cl->pcl; + + result = ffi_prep_cif(cif, + NUM2INT(abi), + argc, + rb_fiddle_int_to_ffi_type(NUM2INT(ret)), + cl->argv); + + if (FFI_OK != result) { + rb_raise(rb_eRuntimeError, "error prepping CIF %d", result); + } + +#if USE_FFI_CLOSURE_ALLOC + result = ffi_prep_closure_loc(pcl, cif, callback, + (void *)(data->self), cl->code); +#else + result = ffi_prep_closure(pcl, cif, callback, (void *)(data->self)); + cl->code = (void *)pcl; + i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC); + if (i) { + rb_sys_fail("mprotect"); + } +#endif + + if (FFI_OK != result) { + rb_raise(rb_eRuntimeError, "error prepping closure %d", result); + } + + return data->self; +} + +static VALUE +initialize_rescue(VALUE user_data, VALUE exception) +{ + initialize_data *data = (initialize_data *)user_data; + dealloc(RTYPEDDATA_DATA(data->self)); + RTYPEDDATA_DATA(data->self) = NULL; + rb_exc_raise(exception); + return data->self; +} + +static VALUE +initialize(int argc, VALUE *argv, VALUE self) +{ + initialize_data data; + data.self = self; + data.argc = argc; + data.argv = argv; + return rb_rescue(initialize_body, (VALUE)&data, + initialize_rescue, (VALUE)&data); +} + +static VALUE +to_i(VALUE self) +{ + fiddle_closure * cl; + void *code; + + TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl); + + code = cl->code; + + return PTR2NUM(code); +} + +void +Init_fiddle_closure(void) +{ +#if 0 + mFiddle = rb_define_module("Fiddle"); /* let rdoc know about mFiddle */ +#endif + + /* + * Document-class: Fiddle::Closure + * + * == Description + * + * An FFI closure wrapper, for handling callbacks. + * + * == Example + * + * closure = Class.new(Fiddle::Closure) { + * def call + * 10 + * end + * }.new(Fiddle::TYPE_INT, []) + * #=> #<#<Class:0x0000000150d308>:0x0000000150d240> + * func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT) + * #=> #<Fiddle::Function:0x00000001516e58> + * func.call + * #=> 10 + */ + cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject); + + rb_define_alloc_func(cFiddleClosure, allocate); + + /* + * Document-method: new + * + * call-seq: new(ret, args, abi = Fiddle::DEFAULT) + * + * Construct a new Closure object. + * + * * +ret+ is the C type to be returned + * * +args+ is an Array of arguments, passed to the callback function + * * +abi+ is the abi of the closure + * + * If there is an error in preparing the ffi_cif or ffi_prep_closure, + * then a RuntimeError will be raised. + */ + rb_define_method(cFiddleClosure, "initialize", initialize, -1); + + /* + * Document-method: to_i + * + * Returns the memory address for this closure + */ + rb_define_method(cFiddleClosure, "to_i", to_i, 0); +} +/* vim: set noet sw=4 sts=4 */ diff --git a/ext/fiddle/closure.h b/ext/fiddle/closure.h new file mode 100644 index 0000000000..d0a8be6180 --- /dev/null +++ b/ext/fiddle/closure.h @@ -0,0 +1,8 @@ +#ifndef FIDDLE_CLOSURE_H +#define FIDDLE_CLOSURE_H + +#include <fiddle.h> + +void Init_fiddle_closure(void); + +#endif diff --git a/ext/fiddle/conversions.c b/ext/fiddle/conversions.c new file mode 100644 index 0000000000..6e0ce36378 --- /dev/null +++ b/ext/fiddle/conversions.c @@ -0,0 +1,330 @@ +#include <fiddle.h> + +VALUE +rb_fiddle_type_ensure(VALUE type) +{ + VALUE original_type = type; + + if (!RB_SYMBOL_P(type)) { + VALUE type_string = rb_check_string_type(type); + if (!NIL_P(type_string)) { + type = rb_to_symbol(type_string); + } + } + + if (RB_SYMBOL_P(type)) { + ID type_id = rb_sym2id(type); + ID void_id; + ID voidp_id; + ID char_id; + ID short_id; + ID int_id; + ID long_id; +#ifdef TYPE_LONG_LONG + ID long_long_id; +#endif +#ifdef TYPE_INT8_T + ID int8_t_id; +#endif +#ifdef TYPE_INT16_T + ID int16_t_id; +#endif +#ifdef TYPE_INT32_T + ID int32_t_id; +#endif +#ifdef TYPE_INT64_T + ID int64_t_id; +#endif + ID float_id; + ID double_id; + ID variadic_id; + ID const_string_id; + ID size_t_id; + ID ssize_t_id; + ID ptrdiff_t_id; + ID intptr_t_id; + ID uintptr_t_id; + RUBY_CONST_ID(void_id, "void"); + RUBY_CONST_ID(voidp_id, "voidp"); + RUBY_CONST_ID(char_id, "char"); + RUBY_CONST_ID(short_id, "short"); + RUBY_CONST_ID(int_id, "int"); + RUBY_CONST_ID(long_id, "long"); +#ifdef TYPE_LONG_LONG + RUBY_CONST_ID(long_long_id, "long_long"); +#endif +#ifdef TYPE_INT8_T + RUBY_CONST_ID(int8_t_id, "int8_t"); +#endif +#ifdef TYPE_INT16_T + RUBY_CONST_ID(int16_t_id, "int16_t"); +#endif +#ifdef TYPE_INT32_T + RUBY_CONST_ID(int32_t_id, "int32_t"); +#endif +#ifdef TYPE_INT64_T + RUBY_CONST_ID(int64_t_id, "int64_t"); +#endif + RUBY_CONST_ID(float_id, "float"); + RUBY_CONST_ID(double_id, "double"); + RUBY_CONST_ID(variadic_id, "variadic"); + RUBY_CONST_ID(const_string_id, "const_string"); + RUBY_CONST_ID(size_t_id, "size_t"); + RUBY_CONST_ID(ssize_t_id, "ssize_t"); + RUBY_CONST_ID(ptrdiff_t_id, "ptrdiff_t"); + RUBY_CONST_ID(intptr_t_id, "intptr_t"); + RUBY_CONST_ID(uintptr_t_id, "uintptr_t"); + if (type_id == void_id) { + return INT2NUM(TYPE_VOID); + } + else if (type_id == voidp_id) { + return INT2NUM(TYPE_VOIDP); + } + else if (type_id == char_id) { + return INT2NUM(TYPE_CHAR); + } + else if (type_id == short_id) { + return INT2NUM(TYPE_SHORT); + } + else if (type_id == int_id) { + return INT2NUM(TYPE_INT); + } + else if (type_id == long_id) { + return INT2NUM(TYPE_LONG); + } +#ifdef TYPE_LONG_LONG + else if (type_id == long_long_id) { + return INT2NUM(TYPE_LONG_LONG); + } +#endif +#ifdef TYPE_INT8_T + else if (type_id == int8_t_id) { + return INT2NUM(TYPE_INT8_T); + } +#endif +#ifdef TYPE_INT16_T + else if (type_id == int16_t_id) { + return INT2NUM(TYPE_INT16_T); + } +#endif +#ifdef TYPE_INT32_T + else if (type_id == int32_t_id) { + return INT2NUM(TYPE_INT32_T); + } +#endif +#ifdef TYPE_INT64_T + else if (type_id == int64_t_id) { + return INT2NUM(TYPE_INT64_T); + } +#endif + else if (type_id == float_id) { + return INT2NUM(TYPE_FLOAT); + } + else if (type_id == double_id) { + return INT2NUM(TYPE_DOUBLE); + } + else if (type_id == variadic_id) { + return INT2NUM(TYPE_VARIADIC); + } + else if (type_id == const_string_id) { + return INT2NUM(TYPE_CONST_STRING); + } + else if (type_id == size_t_id) { + return INT2NUM(TYPE_SIZE_T); + } + else if (type_id == ssize_t_id) { + return INT2NUM(TYPE_SSIZE_T); + } + else if (type_id == ptrdiff_t_id) { + return INT2NUM(TYPE_PTRDIFF_T); + } + else if (type_id == intptr_t_id) { + return INT2NUM(TYPE_INTPTR_T); + } + else if (type_id == uintptr_t_id) { + return INT2NUM(TYPE_UINTPTR_T); + } + else { + type = original_type; + } + } + + return rb_to_int(type); +} + +ffi_type * +rb_fiddle_int_to_ffi_type(int type) +{ + int signed_p = 1; + + if (type < 0) { + type = -1 * type; + signed_p = 0; + } + +#define rb_ffi_type_of(t) (signed_p ? &ffi_type_s##t : &ffi_type_u##t) + + switch (type) { + case TYPE_VOID: + return &ffi_type_void; + case TYPE_VOIDP: + return &ffi_type_pointer; + case TYPE_CHAR: + return rb_ffi_type_of(char); + case TYPE_SHORT: + return rb_ffi_type_of(short); + case TYPE_INT: + return rb_ffi_type_of(int); + case TYPE_LONG: + return rb_ffi_type_of(long); +#if HAVE_LONG_LONG + case TYPE_LONG_LONG: + return rb_ffi_type_of(long_long); +#endif + case TYPE_FLOAT: + return &ffi_type_float; + case TYPE_DOUBLE: + return &ffi_type_double; + case TYPE_CONST_STRING: + return &ffi_type_pointer; + default: + rb_raise(rb_eRuntimeError, "unknown type %d", type); + } + return &ffi_type_pointer; +} + +ffi_type * +int_to_ffi_type(int type) +{ + return rb_fiddle_int_to_ffi_type(type); +} + +void +rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst) +{ + switch (type) { + case TYPE_VOID: + break; + case TYPE_VOIDP: + dst->pointer = NUM2PTR(rb_Integer(*src)); + break; + case TYPE_CHAR: + dst->schar = (signed char)NUM2INT(*src); + break; + case -TYPE_CHAR: + dst->uchar = (unsigned char)NUM2UINT(*src); + break; + case TYPE_SHORT: + dst->sshort = (unsigned short)NUM2INT(*src); + break; + case -TYPE_SHORT: + dst->sshort = (signed short)NUM2UINT(*src); + break; + case TYPE_INT: + dst->sint = NUM2INT(*src); + break; + case -TYPE_INT: + dst->uint = NUM2UINT(*src); + break; + case TYPE_LONG: + dst->slong = NUM2LONG(*src); + break; + case -TYPE_LONG: + dst->ulong = NUM2ULONG(*src); + break; +#if HAVE_LONG_LONG + case TYPE_LONG_LONG: + dst->slong_long = NUM2LL(*src); + break; + case -TYPE_LONG_LONG: + dst->ulong_long = NUM2ULL(*src); + break; +#endif + case TYPE_FLOAT: + dst->ffloat = (float)NUM2DBL(*src); + break; + case TYPE_DOUBLE: + dst->ddouble = NUM2DBL(*src); + break; + case TYPE_CONST_STRING: + if (NIL_P(*src)) { + dst->pointer = NULL; + } + else { + dst->pointer = rb_string_value_cstr(src); + } + break; + default: + rb_raise(rb_eRuntimeError, "unknown type %d", type); + } +} + +void +value_to_generic(int type, VALUE src, fiddle_generic *dst) +{ + /* src isn't safe from GC when type is TYPE_CONST_STRING and src + * isn't String. */ + rb_fiddle_value_to_generic(type, &src, dst); +} + +VALUE +rb_fiddle_generic_to_value(VALUE rettype, fiddle_generic retval) +{ + int type = NUM2INT(rettype); + VALUE cPointer; + + cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); + + switch (type) { + case TYPE_VOID: + return Qnil; + case TYPE_VOIDP: + return rb_funcall(cPointer, rb_intern("[]"), 1, + PTR2NUM((void *)retval.pointer)); + case TYPE_CHAR: + return INT2NUM((signed char)retval.fffi_sarg); + case -TYPE_CHAR: + return INT2NUM((unsigned char)retval.fffi_arg); + case TYPE_SHORT: + return INT2NUM((signed short)retval.fffi_sarg); + case -TYPE_SHORT: + return INT2NUM((unsigned short)retval.fffi_arg); + case TYPE_INT: + return INT2NUM((signed int)retval.fffi_sarg); + case -TYPE_INT: + return UINT2NUM((unsigned int)retval.fffi_arg); + case TYPE_LONG: + return LONG2NUM(retval.slong); + case -TYPE_LONG: + return ULONG2NUM(retval.ulong); +#if HAVE_LONG_LONG + case TYPE_LONG_LONG: + return LL2NUM(retval.slong_long); + case -TYPE_LONG_LONG: + return ULL2NUM(retval.ulong_long); +#endif + case TYPE_FLOAT: + return rb_float_new(retval.ffloat); + case TYPE_DOUBLE: + return rb_float_new(retval.ddouble); + case TYPE_CONST_STRING: + if (retval.pointer) { + return rb_str_new_cstr(retval.pointer); + } + else { + return Qnil; + } + default: + rb_raise(rb_eRuntimeError, "unknown type %d", type); + } + + UNREACHABLE; +} + +VALUE +generic_to_value(VALUE rettype, fiddle_generic retval) +{ + return rb_fiddle_generic_to_value(rettype, retval); +} + +/* vim: set noet sw=4 sts=4 */ diff --git a/ext/fiddle/conversions.h b/ext/fiddle/conversions.h new file mode 100644 index 0000000000..c7c12a9234 --- /dev/null +++ b/ext/fiddle/conversions.h @@ -0,0 +1,53 @@ +#ifndef FIDDLE_CONVERSIONS_H +#define FIDDLE_CONVERSIONS_H + +#include <fiddle.h> + +typedef union +{ + ffi_arg fffi_arg; /* rvalue smaller than unsigned long */ + ffi_sarg fffi_sarg; /* rvalue smaller than signed long */ + unsigned char uchar; /* ffi_type_uchar */ + signed char schar; /* ffi_type_schar */ + unsigned short ushort; /* ffi_type_sshort */ + signed short sshort; /* ffi_type_ushort */ + unsigned int uint; /* ffi_type_uint */ + signed int sint; /* ffi_type_sint */ + unsigned long ulong; /* ffi_type_ulong */ + signed long slong; /* ffi_type_slong */ + float ffloat; /* ffi_type_float */ + double ddouble; /* ffi_type_double */ +#if HAVE_LONG_LONG + unsigned LONG_LONG ulong_long; /* ffi_type_ulong_long */ + signed LONG_LONG slong_long; /* ffi_type_ulong_long */ +#endif + void * pointer; /* ffi_type_pointer */ +} fiddle_generic; + +VALUE rb_fiddle_type_ensure(VALUE type); +ffi_type * rb_fiddle_int_to_ffi_type(int type); +void rb_fiddle_value_to_generic(int type, VALUE *src, fiddle_generic *dst); +VALUE rb_fiddle_generic_to_value(VALUE rettype, fiddle_generic retval); + +/* Deprecated. Use rb_fiddle_*() version. */ +ffi_type * int_to_ffi_type(int type); +void value_to_generic(int type, VALUE src, fiddle_generic *dst); +VALUE generic_to_value(VALUE rettype, fiddle_generic retval); + +#define VALUE2GENERIC(_type, _src, _dst) \ + rb_fiddle_value_to_generic((_type), &(_src), (_dst)) +#define INT2FFI_TYPE(_type) \ + rb_fiddle_int_to_ffi_type(_type) +#define GENERIC2VALUE(_type, _retval) \ + rb_fiddle_generic_to_value((_type), (_retval)) + +#if SIZEOF_VOIDP == SIZEOF_LONG +# define PTR2NUM(x) (LONG2NUM((long)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULONG(x))) +#else +/* # error --->> Ruby/DL2 requires sizeof(void*) == sizeof(long) to be compiled. <<--- */ +# define PTR2NUM(x) (LL2NUM((LONG_LONG)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULL(x))) +#endif + +#endif diff --git a/ext/fiddle/depend b/ext/fiddle/depend new file mode 100644 index 0000000000..4d33d46d33 --- /dev/null +++ b/ext/fiddle/depend @@ -0,0 +1,1388 @@ +PWD = + +CONFIGURE_LIBFFI = \ + $(LIBFFI_CONFIGURE) --disable-shared \ + --host=$(LIBFFI_ARCH) --enable-builddir=$(arch) \ + CC="$(CC)" CFLAGS="$(LIBFFI_CFLAGS)" \ + LD="$(LD)" LDFLAGS="$(LIBFFI_LDFLAGS)" + +$(STATIC_LIB) $(RUBYARCHDIR)/$(DLLIB) $(DLLIB): $(LIBFFI_A) + +$(OBJS): $(FFI_H) + +.PHONY: .FORCE hdr + +.FORCE: + +hdr: $(FFI_H) + +configure-libffi build-libffi: .FORCE +configure-libffi \ +$(LIBFFI_DIR)/include/ffi.h \ +$(LIBFFI_DIR)/include/ffitarget.h \ +$(LIBFFI_DIR)/fficonfig.h \ +$(LIBFFI_DIR)/Makefile: + $(Q) $(MAKEDIRS) $(LIBFFI_DIR) + $(Q) $(CONFIGURE_LIBFFI) + +build-libffi $(LIBFFI_A): + $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG) + +clean-none: +clean-libffi: + $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG) clean + +distclean-none: +distclean-libffi: + $(Q) $(SUBMAKE_PRE) $(MAKE) $(SUBMAKE_ARG) distclean + $(Q) $(RM) $(LIBFFI_DIR)/local.exp + $(Q) $(RUBY) -rfileutils -e "FileUtils.rmdir(Dir.glob(ARGV[0]+'/**/{,.*/}'), :parents=>true)" $(LIBFFI_DIR) + +realclean-none: +realclean-libffi: + $(Q) $(RMALL) $(LIBFFI_DIR) + +.PHONY: clean-libffi distclean-libffi realclean-libffi +.PHONY: clean-none distclean-none realclean-none + +clean: clean-$(LIBFFI_CLEAN) +distclean: distclean-$(LIBFFI_CLEAN) +realclean: realclean-$(LIBFFI_CLEAN) + +.PHONY: configure configure-libffi + +# AUTOGENERATED DEPENDENCIES START +closure.o: $(RUBY_EXTCONF_H) +closure.o: $(arch_hdrdir)/ruby/config.h +closure.o: $(hdrdir)/ruby.h +closure.o: $(hdrdir)/ruby/assert.h +closure.o: $(hdrdir)/ruby/backward.h +closure.o: $(hdrdir)/ruby/backward/2/assume.h +closure.o: $(hdrdir)/ruby/backward/2/attributes.h +closure.o: $(hdrdir)/ruby/backward/2/bool.h +closure.o: $(hdrdir)/ruby/backward/2/inttypes.h +closure.o: $(hdrdir)/ruby/backward/2/limits.h +closure.o: $(hdrdir)/ruby/backward/2/long_long.h +closure.o: $(hdrdir)/ruby/backward/2/stdalign.h +closure.o: $(hdrdir)/ruby/backward/2/stdarg.h +closure.o: $(hdrdir)/ruby/defines.h +closure.o: $(hdrdir)/ruby/intern.h +closure.o: $(hdrdir)/ruby/internal/anyargs.h +closure.o: $(hdrdir)/ruby/internal/arithmetic.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/char.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/double.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/int.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/long.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/short.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +closure.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +closure.o: $(hdrdir)/ruby/internal/assume.h +closure.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +closure.o: $(hdrdir)/ruby/internal/attr/artificial.h +closure.o: $(hdrdir)/ruby/internal/attr/cold.h +closure.o: $(hdrdir)/ruby/internal/attr/const.h +closure.o: $(hdrdir)/ruby/internal/attr/constexpr.h +closure.o: $(hdrdir)/ruby/internal/attr/deprecated.h +closure.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +closure.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +closure.o: $(hdrdir)/ruby/internal/attr/error.h +closure.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +closure.o: $(hdrdir)/ruby/internal/attr/forceinline.h +closure.o: $(hdrdir)/ruby/internal/attr/format.h +closure.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +closure.o: $(hdrdir)/ruby/internal/attr/noalias.h +closure.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +closure.o: $(hdrdir)/ruby/internal/attr/noexcept.h +closure.o: $(hdrdir)/ruby/internal/attr/noinline.h +closure.o: $(hdrdir)/ruby/internal/attr/nonnull.h +closure.o: $(hdrdir)/ruby/internal/attr/noreturn.h +closure.o: $(hdrdir)/ruby/internal/attr/pure.h +closure.o: $(hdrdir)/ruby/internal/attr/restrict.h +closure.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +closure.o: $(hdrdir)/ruby/internal/attr/warning.h +closure.o: $(hdrdir)/ruby/internal/attr/weakref.h +closure.o: $(hdrdir)/ruby/internal/cast.h +closure.o: $(hdrdir)/ruby/internal/compiler_is.h +closure.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +closure.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +closure.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +closure.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +closure.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +closure.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +closure.o: $(hdrdir)/ruby/internal/compiler_since.h +closure.o: $(hdrdir)/ruby/internal/config.h +closure.o: $(hdrdir)/ruby/internal/constant_p.h +closure.o: $(hdrdir)/ruby/internal/core.h +closure.o: $(hdrdir)/ruby/internal/core/rarray.h +closure.o: $(hdrdir)/ruby/internal/core/rbasic.h +closure.o: $(hdrdir)/ruby/internal/core/rbignum.h +closure.o: $(hdrdir)/ruby/internal/core/rclass.h +closure.o: $(hdrdir)/ruby/internal/core/rdata.h +closure.o: $(hdrdir)/ruby/internal/core/rfile.h +closure.o: $(hdrdir)/ruby/internal/core/rhash.h +closure.o: $(hdrdir)/ruby/internal/core/robject.h +closure.o: $(hdrdir)/ruby/internal/core/rregexp.h +closure.o: $(hdrdir)/ruby/internal/core/rstring.h +closure.o: $(hdrdir)/ruby/internal/core/rstruct.h +closure.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +closure.o: $(hdrdir)/ruby/internal/ctype.h +closure.o: $(hdrdir)/ruby/internal/dllexport.h +closure.o: $(hdrdir)/ruby/internal/dosish.h +closure.o: $(hdrdir)/ruby/internal/error.h +closure.o: $(hdrdir)/ruby/internal/eval.h +closure.o: $(hdrdir)/ruby/internal/event.h +closure.o: $(hdrdir)/ruby/internal/fl_type.h +closure.o: $(hdrdir)/ruby/internal/gc.h +closure.o: $(hdrdir)/ruby/internal/glob.h +closure.o: $(hdrdir)/ruby/internal/globals.h +closure.o: $(hdrdir)/ruby/internal/has/attribute.h +closure.o: $(hdrdir)/ruby/internal/has/builtin.h +closure.o: $(hdrdir)/ruby/internal/has/c_attribute.h +closure.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +closure.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +closure.o: $(hdrdir)/ruby/internal/has/extension.h +closure.o: $(hdrdir)/ruby/internal/has/feature.h +closure.o: $(hdrdir)/ruby/internal/has/warning.h +closure.o: $(hdrdir)/ruby/internal/intern/array.h +closure.o: $(hdrdir)/ruby/internal/intern/bignum.h +closure.o: $(hdrdir)/ruby/internal/intern/class.h +closure.o: $(hdrdir)/ruby/internal/intern/compar.h +closure.o: $(hdrdir)/ruby/internal/intern/complex.h +closure.o: $(hdrdir)/ruby/internal/intern/cont.h +closure.o: $(hdrdir)/ruby/internal/intern/dir.h +closure.o: $(hdrdir)/ruby/internal/intern/enum.h +closure.o: $(hdrdir)/ruby/internal/intern/enumerator.h +closure.o: $(hdrdir)/ruby/internal/intern/error.h +closure.o: $(hdrdir)/ruby/internal/intern/eval.h +closure.o: $(hdrdir)/ruby/internal/intern/file.h +closure.o: $(hdrdir)/ruby/internal/intern/gc.h +closure.o: $(hdrdir)/ruby/internal/intern/hash.h +closure.o: $(hdrdir)/ruby/internal/intern/io.h +closure.o: $(hdrdir)/ruby/internal/intern/load.h +closure.o: $(hdrdir)/ruby/internal/intern/marshal.h +closure.o: $(hdrdir)/ruby/internal/intern/numeric.h +closure.o: $(hdrdir)/ruby/internal/intern/object.h +closure.o: $(hdrdir)/ruby/internal/intern/parse.h +closure.o: $(hdrdir)/ruby/internal/intern/proc.h +closure.o: $(hdrdir)/ruby/internal/intern/process.h +closure.o: $(hdrdir)/ruby/internal/intern/random.h +closure.o: $(hdrdir)/ruby/internal/intern/range.h +closure.o: $(hdrdir)/ruby/internal/intern/rational.h +closure.o: $(hdrdir)/ruby/internal/intern/re.h +closure.o: $(hdrdir)/ruby/internal/intern/ruby.h +closure.o: $(hdrdir)/ruby/internal/intern/select.h +closure.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +closure.o: $(hdrdir)/ruby/internal/intern/signal.h +closure.o: $(hdrdir)/ruby/internal/intern/sprintf.h +closure.o: $(hdrdir)/ruby/internal/intern/string.h +closure.o: $(hdrdir)/ruby/internal/intern/struct.h +closure.o: $(hdrdir)/ruby/internal/intern/thread.h +closure.o: $(hdrdir)/ruby/internal/intern/time.h +closure.o: $(hdrdir)/ruby/internal/intern/variable.h +closure.o: $(hdrdir)/ruby/internal/intern/vm.h +closure.o: $(hdrdir)/ruby/internal/interpreter.h +closure.o: $(hdrdir)/ruby/internal/iterator.h +closure.o: $(hdrdir)/ruby/internal/memory.h +closure.o: $(hdrdir)/ruby/internal/method.h +closure.o: $(hdrdir)/ruby/internal/module.h +closure.o: $(hdrdir)/ruby/internal/newobj.h +closure.o: $(hdrdir)/ruby/internal/rgengc.h +closure.o: $(hdrdir)/ruby/internal/scan_args.h +closure.o: $(hdrdir)/ruby/internal/special_consts.h +closure.o: $(hdrdir)/ruby/internal/static_assert.h +closure.o: $(hdrdir)/ruby/internal/stdalign.h +closure.o: $(hdrdir)/ruby/internal/stdbool.h +closure.o: $(hdrdir)/ruby/internal/symbol.h +closure.o: $(hdrdir)/ruby/internal/value.h +closure.o: $(hdrdir)/ruby/internal/value_type.h +closure.o: $(hdrdir)/ruby/internal/variable.h +closure.o: $(hdrdir)/ruby/internal/warning_push.h +closure.o: $(hdrdir)/ruby/internal/xmalloc.h +closure.o: $(hdrdir)/ruby/missing.h +closure.o: $(hdrdir)/ruby/ruby.h +closure.o: $(hdrdir)/ruby/st.h +closure.o: $(hdrdir)/ruby/subst.h +closure.o: $(hdrdir)/ruby/thread.h +closure.o: closure.c +closure.o: closure.h +closure.o: conversions.h +closure.o: fiddle.h +closure.o: function.h +conversions.o: $(RUBY_EXTCONF_H) +conversions.o: $(arch_hdrdir)/ruby/config.h +conversions.o: $(hdrdir)/ruby.h +conversions.o: $(hdrdir)/ruby/assert.h +conversions.o: $(hdrdir)/ruby/backward.h +conversions.o: $(hdrdir)/ruby/backward/2/assume.h +conversions.o: $(hdrdir)/ruby/backward/2/attributes.h +conversions.o: $(hdrdir)/ruby/backward/2/bool.h +conversions.o: $(hdrdir)/ruby/backward/2/inttypes.h +conversions.o: $(hdrdir)/ruby/backward/2/limits.h +conversions.o: $(hdrdir)/ruby/backward/2/long_long.h +conversions.o: $(hdrdir)/ruby/backward/2/stdalign.h +conversions.o: $(hdrdir)/ruby/backward/2/stdarg.h +conversions.o: $(hdrdir)/ruby/defines.h +conversions.o: $(hdrdir)/ruby/intern.h +conversions.o: $(hdrdir)/ruby/internal/anyargs.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/char.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/double.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/int.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/long.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/short.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +conversions.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +conversions.o: $(hdrdir)/ruby/internal/assume.h +conversions.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +conversions.o: $(hdrdir)/ruby/internal/attr/artificial.h +conversions.o: $(hdrdir)/ruby/internal/attr/cold.h +conversions.o: $(hdrdir)/ruby/internal/attr/const.h +conversions.o: $(hdrdir)/ruby/internal/attr/constexpr.h +conversions.o: $(hdrdir)/ruby/internal/attr/deprecated.h +conversions.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +conversions.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +conversions.o: $(hdrdir)/ruby/internal/attr/error.h +conversions.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +conversions.o: $(hdrdir)/ruby/internal/attr/forceinline.h +conversions.o: $(hdrdir)/ruby/internal/attr/format.h +conversions.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +conversions.o: $(hdrdir)/ruby/internal/attr/noalias.h +conversions.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +conversions.o: $(hdrdir)/ruby/internal/attr/noexcept.h +conversions.o: $(hdrdir)/ruby/internal/attr/noinline.h +conversions.o: $(hdrdir)/ruby/internal/attr/nonnull.h +conversions.o: $(hdrdir)/ruby/internal/attr/noreturn.h +conversions.o: $(hdrdir)/ruby/internal/attr/pure.h +conversions.o: $(hdrdir)/ruby/internal/attr/restrict.h +conversions.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +conversions.o: $(hdrdir)/ruby/internal/attr/warning.h +conversions.o: $(hdrdir)/ruby/internal/attr/weakref.h +conversions.o: $(hdrdir)/ruby/internal/cast.h +conversions.o: $(hdrdir)/ruby/internal/compiler_is.h +conversions.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +conversions.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +conversions.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +conversions.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +conversions.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +conversions.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +conversions.o: $(hdrdir)/ruby/internal/compiler_since.h +conversions.o: $(hdrdir)/ruby/internal/config.h +conversions.o: $(hdrdir)/ruby/internal/constant_p.h +conversions.o: $(hdrdir)/ruby/internal/core.h +conversions.o: $(hdrdir)/ruby/internal/core/rarray.h +conversions.o: $(hdrdir)/ruby/internal/core/rbasic.h +conversions.o: $(hdrdir)/ruby/internal/core/rbignum.h +conversions.o: $(hdrdir)/ruby/internal/core/rclass.h +conversions.o: $(hdrdir)/ruby/internal/core/rdata.h +conversions.o: $(hdrdir)/ruby/internal/core/rfile.h +conversions.o: $(hdrdir)/ruby/internal/core/rhash.h +conversions.o: $(hdrdir)/ruby/internal/core/robject.h +conversions.o: $(hdrdir)/ruby/internal/core/rregexp.h +conversions.o: $(hdrdir)/ruby/internal/core/rstring.h +conversions.o: $(hdrdir)/ruby/internal/core/rstruct.h +conversions.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +conversions.o: $(hdrdir)/ruby/internal/ctype.h +conversions.o: $(hdrdir)/ruby/internal/dllexport.h +conversions.o: $(hdrdir)/ruby/internal/dosish.h +conversions.o: $(hdrdir)/ruby/internal/error.h +conversions.o: $(hdrdir)/ruby/internal/eval.h +conversions.o: $(hdrdir)/ruby/internal/event.h +conversions.o: $(hdrdir)/ruby/internal/fl_type.h +conversions.o: $(hdrdir)/ruby/internal/gc.h +conversions.o: $(hdrdir)/ruby/internal/glob.h +conversions.o: $(hdrdir)/ruby/internal/globals.h +conversions.o: $(hdrdir)/ruby/internal/has/attribute.h +conversions.o: $(hdrdir)/ruby/internal/has/builtin.h +conversions.o: $(hdrdir)/ruby/internal/has/c_attribute.h +conversions.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +conversions.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +conversions.o: $(hdrdir)/ruby/internal/has/extension.h +conversions.o: $(hdrdir)/ruby/internal/has/feature.h +conversions.o: $(hdrdir)/ruby/internal/has/warning.h +conversions.o: $(hdrdir)/ruby/internal/intern/array.h +conversions.o: $(hdrdir)/ruby/internal/intern/bignum.h +conversions.o: $(hdrdir)/ruby/internal/intern/class.h +conversions.o: $(hdrdir)/ruby/internal/intern/compar.h +conversions.o: $(hdrdir)/ruby/internal/intern/complex.h +conversions.o: $(hdrdir)/ruby/internal/intern/cont.h +conversions.o: $(hdrdir)/ruby/internal/intern/dir.h +conversions.o: $(hdrdir)/ruby/internal/intern/enum.h +conversions.o: $(hdrdir)/ruby/internal/intern/enumerator.h +conversions.o: $(hdrdir)/ruby/internal/intern/error.h +conversions.o: $(hdrdir)/ruby/internal/intern/eval.h +conversions.o: $(hdrdir)/ruby/internal/intern/file.h +conversions.o: $(hdrdir)/ruby/internal/intern/gc.h +conversions.o: $(hdrdir)/ruby/internal/intern/hash.h +conversions.o: $(hdrdir)/ruby/internal/intern/io.h +conversions.o: $(hdrdir)/ruby/internal/intern/load.h +conversions.o: $(hdrdir)/ruby/internal/intern/marshal.h +conversions.o: $(hdrdir)/ruby/internal/intern/numeric.h +conversions.o: $(hdrdir)/ruby/internal/intern/object.h +conversions.o: $(hdrdir)/ruby/internal/intern/parse.h +conversions.o: $(hdrdir)/ruby/internal/intern/proc.h +conversions.o: $(hdrdir)/ruby/internal/intern/process.h +conversions.o: $(hdrdir)/ruby/internal/intern/random.h +conversions.o: $(hdrdir)/ruby/internal/intern/range.h +conversions.o: $(hdrdir)/ruby/internal/intern/rational.h +conversions.o: $(hdrdir)/ruby/internal/intern/re.h +conversions.o: $(hdrdir)/ruby/internal/intern/ruby.h +conversions.o: $(hdrdir)/ruby/internal/intern/select.h +conversions.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +conversions.o: $(hdrdir)/ruby/internal/intern/signal.h +conversions.o: $(hdrdir)/ruby/internal/intern/sprintf.h +conversions.o: $(hdrdir)/ruby/internal/intern/string.h +conversions.o: $(hdrdir)/ruby/internal/intern/struct.h +conversions.o: $(hdrdir)/ruby/internal/intern/thread.h +conversions.o: $(hdrdir)/ruby/internal/intern/time.h +conversions.o: $(hdrdir)/ruby/internal/intern/variable.h +conversions.o: $(hdrdir)/ruby/internal/intern/vm.h +conversions.o: $(hdrdir)/ruby/internal/interpreter.h +conversions.o: $(hdrdir)/ruby/internal/iterator.h +conversions.o: $(hdrdir)/ruby/internal/memory.h +conversions.o: $(hdrdir)/ruby/internal/method.h +conversions.o: $(hdrdir)/ruby/internal/module.h +conversions.o: $(hdrdir)/ruby/internal/newobj.h +conversions.o: $(hdrdir)/ruby/internal/rgengc.h +conversions.o: $(hdrdir)/ruby/internal/scan_args.h +conversions.o: $(hdrdir)/ruby/internal/special_consts.h +conversions.o: $(hdrdir)/ruby/internal/static_assert.h +conversions.o: $(hdrdir)/ruby/internal/stdalign.h +conversions.o: $(hdrdir)/ruby/internal/stdbool.h +conversions.o: $(hdrdir)/ruby/internal/symbol.h +conversions.o: $(hdrdir)/ruby/internal/value.h +conversions.o: $(hdrdir)/ruby/internal/value_type.h +conversions.o: $(hdrdir)/ruby/internal/variable.h +conversions.o: $(hdrdir)/ruby/internal/warning_push.h +conversions.o: $(hdrdir)/ruby/internal/xmalloc.h +conversions.o: $(hdrdir)/ruby/missing.h +conversions.o: $(hdrdir)/ruby/ruby.h +conversions.o: $(hdrdir)/ruby/st.h +conversions.o: $(hdrdir)/ruby/subst.h +conversions.o: closure.h +conversions.o: conversions.c +conversions.o: conversions.h +conversions.o: fiddle.h +conversions.o: function.h +fiddle.o: $(RUBY_EXTCONF_H) +fiddle.o: $(arch_hdrdir)/ruby/config.h +fiddle.o: $(hdrdir)/ruby.h +fiddle.o: $(hdrdir)/ruby/assert.h +fiddle.o: $(hdrdir)/ruby/backward.h +fiddle.o: $(hdrdir)/ruby/backward/2/assume.h +fiddle.o: $(hdrdir)/ruby/backward/2/attributes.h +fiddle.o: $(hdrdir)/ruby/backward/2/bool.h +fiddle.o: $(hdrdir)/ruby/backward/2/inttypes.h +fiddle.o: $(hdrdir)/ruby/backward/2/limits.h +fiddle.o: $(hdrdir)/ruby/backward/2/long_long.h +fiddle.o: $(hdrdir)/ruby/backward/2/stdalign.h +fiddle.o: $(hdrdir)/ruby/backward/2/stdarg.h +fiddle.o: $(hdrdir)/ruby/defines.h +fiddle.o: $(hdrdir)/ruby/intern.h +fiddle.o: $(hdrdir)/ruby/internal/anyargs.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/char.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/double.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/int.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/long.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/short.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +fiddle.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +fiddle.o: $(hdrdir)/ruby/internal/assume.h +fiddle.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +fiddle.o: $(hdrdir)/ruby/internal/attr/artificial.h +fiddle.o: $(hdrdir)/ruby/internal/attr/cold.h +fiddle.o: $(hdrdir)/ruby/internal/attr/const.h +fiddle.o: $(hdrdir)/ruby/internal/attr/constexpr.h +fiddle.o: $(hdrdir)/ruby/internal/attr/deprecated.h +fiddle.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +fiddle.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +fiddle.o: $(hdrdir)/ruby/internal/attr/error.h +fiddle.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +fiddle.o: $(hdrdir)/ruby/internal/attr/forceinline.h +fiddle.o: $(hdrdir)/ruby/internal/attr/format.h +fiddle.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +fiddle.o: $(hdrdir)/ruby/internal/attr/noalias.h +fiddle.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +fiddle.o: $(hdrdir)/ruby/internal/attr/noexcept.h +fiddle.o: $(hdrdir)/ruby/internal/attr/noinline.h +fiddle.o: $(hdrdir)/ruby/internal/attr/nonnull.h +fiddle.o: $(hdrdir)/ruby/internal/attr/noreturn.h +fiddle.o: $(hdrdir)/ruby/internal/attr/pure.h +fiddle.o: $(hdrdir)/ruby/internal/attr/restrict.h +fiddle.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +fiddle.o: $(hdrdir)/ruby/internal/attr/warning.h +fiddle.o: $(hdrdir)/ruby/internal/attr/weakref.h +fiddle.o: $(hdrdir)/ruby/internal/cast.h +fiddle.o: $(hdrdir)/ruby/internal/compiler_is.h +fiddle.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +fiddle.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +fiddle.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +fiddle.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +fiddle.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +fiddle.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +fiddle.o: $(hdrdir)/ruby/internal/compiler_since.h +fiddle.o: $(hdrdir)/ruby/internal/config.h +fiddle.o: $(hdrdir)/ruby/internal/constant_p.h +fiddle.o: $(hdrdir)/ruby/internal/core.h +fiddle.o: $(hdrdir)/ruby/internal/core/rarray.h +fiddle.o: $(hdrdir)/ruby/internal/core/rbasic.h +fiddle.o: $(hdrdir)/ruby/internal/core/rbignum.h +fiddle.o: $(hdrdir)/ruby/internal/core/rclass.h +fiddle.o: $(hdrdir)/ruby/internal/core/rdata.h +fiddle.o: $(hdrdir)/ruby/internal/core/rfile.h +fiddle.o: $(hdrdir)/ruby/internal/core/rhash.h +fiddle.o: $(hdrdir)/ruby/internal/core/robject.h +fiddle.o: $(hdrdir)/ruby/internal/core/rregexp.h +fiddle.o: $(hdrdir)/ruby/internal/core/rstring.h +fiddle.o: $(hdrdir)/ruby/internal/core/rstruct.h +fiddle.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +fiddle.o: $(hdrdir)/ruby/internal/ctype.h +fiddle.o: $(hdrdir)/ruby/internal/dllexport.h +fiddle.o: $(hdrdir)/ruby/internal/dosish.h +fiddle.o: $(hdrdir)/ruby/internal/error.h +fiddle.o: $(hdrdir)/ruby/internal/eval.h +fiddle.o: $(hdrdir)/ruby/internal/event.h +fiddle.o: $(hdrdir)/ruby/internal/fl_type.h +fiddle.o: $(hdrdir)/ruby/internal/gc.h +fiddle.o: $(hdrdir)/ruby/internal/glob.h +fiddle.o: $(hdrdir)/ruby/internal/globals.h +fiddle.o: $(hdrdir)/ruby/internal/has/attribute.h +fiddle.o: $(hdrdir)/ruby/internal/has/builtin.h +fiddle.o: $(hdrdir)/ruby/internal/has/c_attribute.h +fiddle.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +fiddle.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +fiddle.o: $(hdrdir)/ruby/internal/has/extension.h +fiddle.o: $(hdrdir)/ruby/internal/has/feature.h +fiddle.o: $(hdrdir)/ruby/internal/has/warning.h +fiddle.o: $(hdrdir)/ruby/internal/intern/array.h +fiddle.o: $(hdrdir)/ruby/internal/intern/bignum.h +fiddle.o: $(hdrdir)/ruby/internal/intern/class.h +fiddle.o: $(hdrdir)/ruby/internal/intern/compar.h +fiddle.o: $(hdrdir)/ruby/internal/intern/complex.h +fiddle.o: $(hdrdir)/ruby/internal/intern/cont.h +fiddle.o: $(hdrdir)/ruby/internal/intern/dir.h +fiddle.o: $(hdrdir)/ruby/internal/intern/enum.h +fiddle.o: $(hdrdir)/ruby/internal/intern/enumerator.h +fiddle.o: $(hdrdir)/ruby/internal/intern/error.h +fiddle.o: $(hdrdir)/ruby/internal/intern/eval.h +fiddle.o: $(hdrdir)/ruby/internal/intern/file.h +fiddle.o: $(hdrdir)/ruby/internal/intern/gc.h +fiddle.o: $(hdrdir)/ruby/internal/intern/hash.h +fiddle.o: $(hdrdir)/ruby/internal/intern/io.h +fiddle.o: $(hdrdir)/ruby/internal/intern/load.h +fiddle.o: $(hdrdir)/ruby/internal/intern/marshal.h +fiddle.o: $(hdrdir)/ruby/internal/intern/numeric.h +fiddle.o: $(hdrdir)/ruby/internal/intern/object.h +fiddle.o: $(hdrdir)/ruby/internal/intern/parse.h +fiddle.o: $(hdrdir)/ruby/internal/intern/proc.h +fiddle.o: $(hdrdir)/ruby/internal/intern/process.h +fiddle.o: $(hdrdir)/ruby/internal/intern/random.h +fiddle.o: $(hdrdir)/ruby/internal/intern/range.h +fiddle.o: $(hdrdir)/ruby/internal/intern/rational.h +fiddle.o: $(hdrdir)/ruby/internal/intern/re.h +fiddle.o: $(hdrdir)/ruby/internal/intern/ruby.h +fiddle.o: $(hdrdir)/ruby/internal/intern/select.h +fiddle.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +fiddle.o: $(hdrdir)/ruby/internal/intern/signal.h +fiddle.o: $(hdrdir)/ruby/internal/intern/sprintf.h +fiddle.o: $(hdrdir)/ruby/internal/intern/string.h +fiddle.o: $(hdrdir)/ruby/internal/intern/struct.h +fiddle.o: $(hdrdir)/ruby/internal/intern/thread.h +fiddle.o: $(hdrdir)/ruby/internal/intern/time.h +fiddle.o: $(hdrdir)/ruby/internal/intern/variable.h +fiddle.o: $(hdrdir)/ruby/internal/intern/vm.h +fiddle.o: $(hdrdir)/ruby/internal/interpreter.h +fiddle.o: $(hdrdir)/ruby/internal/iterator.h +fiddle.o: $(hdrdir)/ruby/internal/memory.h +fiddle.o: $(hdrdir)/ruby/internal/method.h +fiddle.o: $(hdrdir)/ruby/internal/module.h +fiddle.o: $(hdrdir)/ruby/internal/newobj.h +fiddle.o: $(hdrdir)/ruby/internal/rgengc.h +fiddle.o: $(hdrdir)/ruby/internal/scan_args.h +fiddle.o: $(hdrdir)/ruby/internal/special_consts.h +fiddle.o: $(hdrdir)/ruby/internal/static_assert.h +fiddle.o: $(hdrdir)/ruby/internal/stdalign.h +fiddle.o: $(hdrdir)/ruby/internal/stdbool.h +fiddle.o: $(hdrdir)/ruby/internal/symbol.h +fiddle.o: $(hdrdir)/ruby/internal/value.h +fiddle.o: $(hdrdir)/ruby/internal/value_type.h +fiddle.o: $(hdrdir)/ruby/internal/variable.h +fiddle.o: $(hdrdir)/ruby/internal/warning_push.h +fiddle.o: $(hdrdir)/ruby/internal/xmalloc.h +fiddle.o: $(hdrdir)/ruby/missing.h +fiddle.o: $(hdrdir)/ruby/ruby.h +fiddle.o: $(hdrdir)/ruby/st.h +fiddle.o: $(hdrdir)/ruby/subst.h +fiddle.o: closure.h +fiddle.o: conversions.h +fiddle.o: fiddle.c +fiddle.o: fiddle.h +fiddle.o: function.h +function.o: $(RUBY_EXTCONF_H) +function.o: $(arch_hdrdir)/ruby/config.h +function.o: $(hdrdir)/ruby.h +function.o: $(hdrdir)/ruby/assert.h +function.o: $(hdrdir)/ruby/backward.h +function.o: $(hdrdir)/ruby/backward/2/assume.h +function.o: $(hdrdir)/ruby/backward/2/attributes.h +function.o: $(hdrdir)/ruby/backward/2/bool.h +function.o: $(hdrdir)/ruby/backward/2/inttypes.h +function.o: $(hdrdir)/ruby/backward/2/limits.h +function.o: $(hdrdir)/ruby/backward/2/long_long.h +function.o: $(hdrdir)/ruby/backward/2/stdalign.h +function.o: $(hdrdir)/ruby/backward/2/stdarg.h +function.o: $(hdrdir)/ruby/defines.h +function.o: $(hdrdir)/ruby/intern.h +function.o: $(hdrdir)/ruby/internal/anyargs.h +function.o: $(hdrdir)/ruby/internal/arithmetic.h +function.o: $(hdrdir)/ruby/internal/arithmetic/char.h +function.o: $(hdrdir)/ruby/internal/arithmetic/double.h +function.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +function.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +function.o: $(hdrdir)/ruby/internal/arithmetic/int.h +function.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +function.o: $(hdrdir)/ruby/internal/arithmetic/long.h +function.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +function.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +function.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +function.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +function.o: $(hdrdir)/ruby/internal/arithmetic/short.h +function.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +function.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +function.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +function.o: $(hdrdir)/ruby/internal/assume.h +function.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +function.o: $(hdrdir)/ruby/internal/attr/artificial.h +function.o: $(hdrdir)/ruby/internal/attr/cold.h +function.o: $(hdrdir)/ruby/internal/attr/const.h +function.o: $(hdrdir)/ruby/internal/attr/constexpr.h +function.o: $(hdrdir)/ruby/internal/attr/deprecated.h +function.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +function.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +function.o: $(hdrdir)/ruby/internal/attr/error.h +function.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +function.o: $(hdrdir)/ruby/internal/attr/forceinline.h +function.o: $(hdrdir)/ruby/internal/attr/format.h +function.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +function.o: $(hdrdir)/ruby/internal/attr/noalias.h +function.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +function.o: $(hdrdir)/ruby/internal/attr/noexcept.h +function.o: $(hdrdir)/ruby/internal/attr/noinline.h +function.o: $(hdrdir)/ruby/internal/attr/nonnull.h +function.o: $(hdrdir)/ruby/internal/attr/noreturn.h +function.o: $(hdrdir)/ruby/internal/attr/pure.h +function.o: $(hdrdir)/ruby/internal/attr/restrict.h +function.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +function.o: $(hdrdir)/ruby/internal/attr/warning.h +function.o: $(hdrdir)/ruby/internal/attr/weakref.h +function.o: $(hdrdir)/ruby/internal/cast.h +function.o: $(hdrdir)/ruby/internal/compiler_is.h +function.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +function.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +function.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +function.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +function.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +function.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +function.o: $(hdrdir)/ruby/internal/compiler_since.h +function.o: $(hdrdir)/ruby/internal/config.h +function.o: $(hdrdir)/ruby/internal/constant_p.h +function.o: $(hdrdir)/ruby/internal/core.h +function.o: $(hdrdir)/ruby/internal/core/rarray.h +function.o: $(hdrdir)/ruby/internal/core/rbasic.h +function.o: $(hdrdir)/ruby/internal/core/rbignum.h +function.o: $(hdrdir)/ruby/internal/core/rclass.h +function.o: $(hdrdir)/ruby/internal/core/rdata.h +function.o: $(hdrdir)/ruby/internal/core/rfile.h +function.o: $(hdrdir)/ruby/internal/core/rhash.h +function.o: $(hdrdir)/ruby/internal/core/robject.h +function.o: $(hdrdir)/ruby/internal/core/rregexp.h +function.o: $(hdrdir)/ruby/internal/core/rstring.h +function.o: $(hdrdir)/ruby/internal/core/rstruct.h +function.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +function.o: $(hdrdir)/ruby/internal/ctype.h +function.o: $(hdrdir)/ruby/internal/dllexport.h +function.o: $(hdrdir)/ruby/internal/dosish.h +function.o: $(hdrdir)/ruby/internal/error.h +function.o: $(hdrdir)/ruby/internal/eval.h +function.o: $(hdrdir)/ruby/internal/event.h +function.o: $(hdrdir)/ruby/internal/fl_type.h +function.o: $(hdrdir)/ruby/internal/gc.h +function.o: $(hdrdir)/ruby/internal/glob.h +function.o: $(hdrdir)/ruby/internal/globals.h +function.o: $(hdrdir)/ruby/internal/has/attribute.h +function.o: $(hdrdir)/ruby/internal/has/builtin.h +function.o: $(hdrdir)/ruby/internal/has/c_attribute.h +function.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +function.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +function.o: $(hdrdir)/ruby/internal/has/extension.h +function.o: $(hdrdir)/ruby/internal/has/feature.h +function.o: $(hdrdir)/ruby/internal/has/warning.h +function.o: $(hdrdir)/ruby/internal/intern/array.h +function.o: $(hdrdir)/ruby/internal/intern/bignum.h +function.o: $(hdrdir)/ruby/internal/intern/class.h +function.o: $(hdrdir)/ruby/internal/intern/compar.h +function.o: $(hdrdir)/ruby/internal/intern/complex.h +function.o: $(hdrdir)/ruby/internal/intern/cont.h +function.o: $(hdrdir)/ruby/internal/intern/dir.h +function.o: $(hdrdir)/ruby/internal/intern/enum.h +function.o: $(hdrdir)/ruby/internal/intern/enumerator.h +function.o: $(hdrdir)/ruby/internal/intern/error.h +function.o: $(hdrdir)/ruby/internal/intern/eval.h +function.o: $(hdrdir)/ruby/internal/intern/file.h +function.o: $(hdrdir)/ruby/internal/intern/gc.h +function.o: $(hdrdir)/ruby/internal/intern/hash.h +function.o: $(hdrdir)/ruby/internal/intern/io.h +function.o: $(hdrdir)/ruby/internal/intern/load.h +function.o: $(hdrdir)/ruby/internal/intern/marshal.h +function.o: $(hdrdir)/ruby/internal/intern/numeric.h +function.o: $(hdrdir)/ruby/internal/intern/object.h +function.o: $(hdrdir)/ruby/internal/intern/parse.h +function.o: $(hdrdir)/ruby/internal/intern/proc.h +function.o: $(hdrdir)/ruby/internal/intern/process.h +function.o: $(hdrdir)/ruby/internal/intern/random.h +function.o: $(hdrdir)/ruby/internal/intern/range.h +function.o: $(hdrdir)/ruby/internal/intern/rational.h +function.o: $(hdrdir)/ruby/internal/intern/re.h +function.o: $(hdrdir)/ruby/internal/intern/ruby.h +function.o: $(hdrdir)/ruby/internal/intern/select.h +function.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +function.o: $(hdrdir)/ruby/internal/intern/signal.h +function.o: $(hdrdir)/ruby/internal/intern/sprintf.h +function.o: $(hdrdir)/ruby/internal/intern/string.h +function.o: $(hdrdir)/ruby/internal/intern/struct.h +function.o: $(hdrdir)/ruby/internal/intern/thread.h +function.o: $(hdrdir)/ruby/internal/intern/time.h +function.o: $(hdrdir)/ruby/internal/intern/variable.h +function.o: $(hdrdir)/ruby/internal/intern/vm.h +function.o: $(hdrdir)/ruby/internal/interpreter.h +function.o: $(hdrdir)/ruby/internal/iterator.h +function.o: $(hdrdir)/ruby/internal/memory.h +function.o: $(hdrdir)/ruby/internal/method.h +function.o: $(hdrdir)/ruby/internal/module.h +function.o: $(hdrdir)/ruby/internal/newobj.h +function.o: $(hdrdir)/ruby/internal/rgengc.h +function.o: $(hdrdir)/ruby/internal/scan_args.h +function.o: $(hdrdir)/ruby/internal/special_consts.h +function.o: $(hdrdir)/ruby/internal/static_assert.h +function.o: $(hdrdir)/ruby/internal/stdalign.h +function.o: $(hdrdir)/ruby/internal/stdbool.h +function.o: $(hdrdir)/ruby/internal/symbol.h +function.o: $(hdrdir)/ruby/internal/value.h +function.o: $(hdrdir)/ruby/internal/value_type.h +function.o: $(hdrdir)/ruby/internal/variable.h +function.o: $(hdrdir)/ruby/internal/warning_push.h +function.o: $(hdrdir)/ruby/internal/xmalloc.h +function.o: $(hdrdir)/ruby/missing.h +function.o: $(hdrdir)/ruby/ruby.h +function.o: $(hdrdir)/ruby/st.h +function.o: $(hdrdir)/ruby/subst.h +function.o: $(hdrdir)/ruby/thread.h +function.o: closure.h +function.o: conversions.h +function.o: fiddle.h +function.o: function.c +function.o: function.h +handle.o: $(RUBY_EXTCONF_H) +handle.o: $(arch_hdrdir)/ruby/config.h +handle.o: $(hdrdir)/ruby.h +handle.o: $(hdrdir)/ruby/assert.h +handle.o: $(hdrdir)/ruby/backward.h +handle.o: $(hdrdir)/ruby/backward/2/assume.h +handle.o: $(hdrdir)/ruby/backward/2/attributes.h +handle.o: $(hdrdir)/ruby/backward/2/bool.h +handle.o: $(hdrdir)/ruby/backward/2/inttypes.h +handle.o: $(hdrdir)/ruby/backward/2/limits.h +handle.o: $(hdrdir)/ruby/backward/2/long_long.h +handle.o: $(hdrdir)/ruby/backward/2/stdalign.h +handle.o: $(hdrdir)/ruby/backward/2/stdarg.h +handle.o: $(hdrdir)/ruby/defines.h +handle.o: $(hdrdir)/ruby/intern.h +handle.o: $(hdrdir)/ruby/internal/anyargs.h +handle.o: $(hdrdir)/ruby/internal/arithmetic.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/char.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/double.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/int.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/long.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/short.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +handle.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +handle.o: $(hdrdir)/ruby/internal/assume.h +handle.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +handle.o: $(hdrdir)/ruby/internal/attr/artificial.h +handle.o: $(hdrdir)/ruby/internal/attr/cold.h +handle.o: $(hdrdir)/ruby/internal/attr/const.h +handle.o: $(hdrdir)/ruby/internal/attr/constexpr.h +handle.o: $(hdrdir)/ruby/internal/attr/deprecated.h +handle.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +handle.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +handle.o: $(hdrdir)/ruby/internal/attr/error.h +handle.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +handle.o: $(hdrdir)/ruby/internal/attr/forceinline.h +handle.o: $(hdrdir)/ruby/internal/attr/format.h +handle.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +handle.o: $(hdrdir)/ruby/internal/attr/noalias.h +handle.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +handle.o: $(hdrdir)/ruby/internal/attr/noexcept.h +handle.o: $(hdrdir)/ruby/internal/attr/noinline.h +handle.o: $(hdrdir)/ruby/internal/attr/nonnull.h +handle.o: $(hdrdir)/ruby/internal/attr/noreturn.h +handle.o: $(hdrdir)/ruby/internal/attr/pure.h +handle.o: $(hdrdir)/ruby/internal/attr/restrict.h +handle.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +handle.o: $(hdrdir)/ruby/internal/attr/warning.h +handle.o: $(hdrdir)/ruby/internal/attr/weakref.h +handle.o: $(hdrdir)/ruby/internal/cast.h +handle.o: $(hdrdir)/ruby/internal/compiler_is.h +handle.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +handle.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +handle.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +handle.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +handle.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +handle.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +handle.o: $(hdrdir)/ruby/internal/compiler_since.h +handle.o: $(hdrdir)/ruby/internal/config.h +handle.o: $(hdrdir)/ruby/internal/constant_p.h +handle.o: $(hdrdir)/ruby/internal/core.h +handle.o: $(hdrdir)/ruby/internal/core/rarray.h +handle.o: $(hdrdir)/ruby/internal/core/rbasic.h +handle.o: $(hdrdir)/ruby/internal/core/rbignum.h +handle.o: $(hdrdir)/ruby/internal/core/rclass.h +handle.o: $(hdrdir)/ruby/internal/core/rdata.h +handle.o: $(hdrdir)/ruby/internal/core/rfile.h +handle.o: $(hdrdir)/ruby/internal/core/rhash.h +handle.o: $(hdrdir)/ruby/internal/core/robject.h +handle.o: $(hdrdir)/ruby/internal/core/rregexp.h +handle.o: $(hdrdir)/ruby/internal/core/rstring.h +handle.o: $(hdrdir)/ruby/internal/core/rstruct.h +handle.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +handle.o: $(hdrdir)/ruby/internal/ctype.h +handle.o: $(hdrdir)/ruby/internal/dllexport.h +handle.o: $(hdrdir)/ruby/internal/dosish.h +handle.o: $(hdrdir)/ruby/internal/error.h +handle.o: $(hdrdir)/ruby/internal/eval.h +handle.o: $(hdrdir)/ruby/internal/event.h +handle.o: $(hdrdir)/ruby/internal/fl_type.h +handle.o: $(hdrdir)/ruby/internal/gc.h +handle.o: $(hdrdir)/ruby/internal/glob.h +handle.o: $(hdrdir)/ruby/internal/globals.h +handle.o: $(hdrdir)/ruby/internal/has/attribute.h +handle.o: $(hdrdir)/ruby/internal/has/builtin.h +handle.o: $(hdrdir)/ruby/internal/has/c_attribute.h +handle.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +handle.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +handle.o: $(hdrdir)/ruby/internal/has/extension.h +handle.o: $(hdrdir)/ruby/internal/has/feature.h +handle.o: $(hdrdir)/ruby/internal/has/warning.h +handle.o: $(hdrdir)/ruby/internal/intern/array.h +handle.o: $(hdrdir)/ruby/internal/intern/bignum.h +handle.o: $(hdrdir)/ruby/internal/intern/class.h +handle.o: $(hdrdir)/ruby/internal/intern/compar.h +handle.o: $(hdrdir)/ruby/internal/intern/complex.h +handle.o: $(hdrdir)/ruby/internal/intern/cont.h +handle.o: $(hdrdir)/ruby/internal/intern/dir.h +handle.o: $(hdrdir)/ruby/internal/intern/enum.h +handle.o: $(hdrdir)/ruby/internal/intern/enumerator.h +handle.o: $(hdrdir)/ruby/internal/intern/error.h +handle.o: $(hdrdir)/ruby/internal/intern/eval.h +handle.o: $(hdrdir)/ruby/internal/intern/file.h +handle.o: $(hdrdir)/ruby/internal/intern/gc.h +handle.o: $(hdrdir)/ruby/internal/intern/hash.h +handle.o: $(hdrdir)/ruby/internal/intern/io.h +handle.o: $(hdrdir)/ruby/internal/intern/load.h +handle.o: $(hdrdir)/ruby/internal/intern/marshal.h +handle.o: $(hdrdir)/ruby/internal/intern/numeric.h +handle.o: $(hdrdir)/ruby/internal/intern/object.h +handle.o: $(hdrdir)/ruby/internal/intern/parse.h +handle.o: $(hdrdir)/ruby/internal/intern/proc.h +handle.o: $(hdrdir)/ruby/internal/intern/process.h +handle.o: $(hdrdir)/ruby/internal/intern/random.h +handle.o: $(hdrdir)/ruby/internal/intern/range.h +handle.o: $(hdrdir)/ruby/internal/intern/rational.h +handle.o: $(hdrdir)/ruby/internal/intern/re.h +handle.o: $(hdrdir)/ruby/internal/intern/ruby.h +handle.o: $(hdrdir)/ruby/internal/intern/select.h +handle.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +handle.o: $(hdrdir)/ruby/internal/intern/signal.h +handle.o: $(hdrdir)/ruby/internal/intern/sprintf.h +handle.o: $(hdrdir)/ruby/internal/intern/string.h +handle.o: $(hdrdir)/ruby/internal/intern/struct.h +handle.o: $(hdrdir)/ruby/internal/intern/thread.h +handle.o: $(hdrdir)/ruby/internal/intern/time.h +handle.o: $(hdrdir)/ruby/internal/intern/variable.h +handle.o: $(hdrdir)/ruby/internal/intern/vm.h +handle.o: $(hdrdir)/ruby/internal/interpreter.h +handle.o: $(hdrdir)/ruby/internal/iterator.h +handle.o: $(hdrdir)/ruby/internal/memory.h +handle.o: $(hdrdir)/ruby/internal/method.h +handle.o: $(hdrdir)/ruby/internal/module.h +handle.o: $(hdrdir)/ruby/internal/newobj.h +handle.o: $(hdrdir)/ruby/internal/rgengc.h +handle.o: $(hdrdir)/ruby/internal/scan_args.h +handle.o: $(hdrdir)/ruby/internal/special_consts.h +handle.o: $(hdrdir)/ruby/internal/static_assert.h +handle.o: $(hdrdir)/ruby/internal/stdalign.h +handle.o: $(hdrdir)/ruby/internal/stdbool.h +handle.o: $(hdrdir)/ruby/internal/symbol.h +handle.o: $(hdrdir)/ruby/internal/value.h +handle.o: $(hdrdir)/ruby/internal/value_type.h +handle.o: $(hdrdir)/ruby/internal/variable.h +handle.o: $(hdrdir)/ruby/internal/warning_push.h +handle.o: $(hdrdir)/ruby/internal/xmalloc.h +handle.o: $(hdrdir)/ruby/missing.h +handle.o: $(hdrdir)/ruby/ruby.h +handle.o: $(hdrdir)/ruby/st.h +handle.o: $(hdrdir)/ruby/subst.h +handle.o: closure.h +handle.o: conversions.h +handle.o: fiddle.h +handle.o: function.h +handle.o: handle.c +memory_view.o: $(RUBY_EXTCONF_H) +memory_view.o: $(arch_hdrdir)/ruby/config.h +memory_view.o: $(hdrdir)/ruby.h +memory_view.o: $(hdrdir)/ruby/assert.h +memory_view.o: $(hdrdir)/ruby/backward.h +memory_view.o: $(hdrdir)/ruby/backward/2/assume.h +memory_view.o: $(hdrdir)/ruby/backward/2/attributes.h +memory_view.o: $(hdrdir)/ruby/backward/2/bool.h +memory_view.o: $(hdrdir)/ruby/backward/2/inttypes.h +memory_view.o: $(hdrdir)/ruby/backward/2/limits.h +memory_view.o: $(hdrdir)/ruby/backward/2/long_long.h +memory_view.o: $(hdrdir)/ruby/backward/2/stdalign.h +memory_view.o: $(hdrdir)/ruby/backward/2/stdarg.h +memory_view.o: $(hdrdir)/ruby/defines.h +memory_view.o: $(hdrdir)/ruby/encoding.h +memory_view.o: $(hdrdir)/ruby/intern.h +memory_view.o: $(hdrdir)/ruby/internal/anyargs.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/char.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/double.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/int.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/long.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/short.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +memory_view.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +memory_view.o: $(hdrdir)/ruby/internal/assume.h +memory_view.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +memory_view.o: $(hdrdir)/ruby/internal/attr/artificial.h +memory_view.o: $(hdrdir)/ruby/internal/attr/cold.h +memory_view.o: $(hdrdir)/ruby/internal/attr/const.h +memory_view.o: $(hdrdir)/ruby/internal/attr/constexpr.h +memory_view.o: $(hdrdir)/ruby/internal/attr/deprecated.h +memory_view.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +memory_view.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +memory_view.o: $(hdrdir)/ruby/internal/attr/error.h +memory_view.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +memory_view.o: $(hdrdir)/ruby/internal/attr/forceinline.h +memory_view.o: $(hdrdir)/ruby/internal/attr/format.h +memory_view.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +memory_view.o: $(hdrdir)/ruby/internal/attr/noalias.h +memory_view.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +memory_view.o: $(hdrdir)/ruby/internal/attr/noexcept.h +memory_view.o: $(hdrdir)/ruby/internal/attr/noinline.h +memory_view.o: $(hdrdir)/ruby/internal/attr/nonnull.h +memory_view.o: $(hdrdir)/ruby/internal/attr/noreturn.h +memory_view.o: $(hdrdir)/ruby/internal/attr/pure.h +memory_view.o: $(hdrdir)/ruby/internal/attr/restrict.h +memory_view.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +memory_view.o: $(hdrdir)/ruby/internal/attr/warning.h +memory_view.o: $(hdrdir)/ruby/internal/attr/weakref.h +memory_view.o: $(hdrdir)/ruby/internal/cast.h +memory_view.o: $(hdrdir)/ruby/internal/compiler_is.h +memory_view.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +memory_view.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +memory_view.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +memory_view.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +memory_view.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +memory_view.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +memory_view.o: $(hdrdir)/ruby/internal/compiler_since.h +memory_view.o: $(hdrdir)/ruby/internal/config.h +memory_view.o: $(hdrdir)/ruby/internal/constant_p.h +memory_view.o: $(hdrdir)/ruby/internal/core.h +memory_view.o: $(hdrdir)/ruby/internal/core/rarray.h +memory_view.o: $(hdrdir)/ruby/internal/core/rbasic.h +memory_view.o: $(hdrdir)/ruby/internal/core/rbignum.h +memory_view.o: $(hdrdir)/ruby/internal/core/rclass.h +memory_view.o: $(hdrdir)/ruby/internal/core/rdata.h +memory_view.o: $(hdrdir)/ruby/internal/core/rfile.h +memory_view.o: $(hdrdir)/ruby/internal/core/rhash.h +memory_view.o: $(hdrdir)/ruby/internal/core/robject.h +memory_view.o: $(hdrdir)/ruby/internal/core/rregexp.h +memory_view.o: $(hdrdir)/ruby/internal/core/rstring.h +memory_view.o: $(hdrdir)/ruby/internal/core/rstruct.h +memory_view.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +memory_view.o: $(hdrdir)/ruby/internal/ctype.h +memory_view.o: $(hdrdir)/ruby/internal/dllexport.h +memory_view.o: $(hdrdir)/ruby/internal/dosish.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/coderange.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/ctype.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/encoding.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/pathname.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/re.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/string.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/symbol.h +memory_view.o: $(hdrdir)/ruby/internal/encoding/transcode.h +memory_view.o: $(hdrdir)/ruby/internal/error.h +memory_view.o: $(hdrdir)/ruby/internal/eval.h +memory_view.o: $(hdrdir)/ruby/internal/event.h +memory_view.o: $(hdrdir)/ruby/internal/fl_type.h +memory_view.o: $(hdrdir)/ruby/internal/gc.h +memory_view.o: $(hdrdir)/ruby/internal/glob.h +memory_view.o: $(hdrdir)/ruby/internal/globals.h +memory_view.o: $(hdrdir)/ruby/internal/has/attribute.h +memory_view.o: $(hdrdir)/ruby/internal/has/builtin.h +memory_view.o: $(hdrdir)/ruby/internal/has/c_attribute.h +memory_view.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +memory_view.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +memory_view.o: $(hdrdir)/ruby/internal/has/extension.h +memory_view.o: $(hdrdir)/ruby/internal/has/feature.h +memory_view.o: $(hdrdir)/ruby/internal/has/warning.h +memory_view.o: $(hdrdir)/ruby/internal/intern/array.h +memory_view.o: $(hdrdir)/ruby/internal/intern/bignum.h +memory_view.o: $(hdrdir)/ruby/internal/intern/class.h +memory_view.o: $(hdrdir)/ruby/internal/intern/compar.h +memory_view.o: $(hdrdir)/ruby/internal/intern/complex.h +memory_view.o: $(hdrdir)/ruby/internal/intern/cont.h +memory_view.o: $(hdrdir)/ruby/internal/intern/dir.h +memory_view.o: $(hdrdir)/ruby/internal/intern/enum.h +memory_view.o: $(hdrdir)/ruby/internal/intern/enumerator.h +memory_view.o: $(hdrdir)/ruby/internal/intern/error.h +memory_view.o: $(hdrdir)/ruby/internal/intern/eval.h +memory_view.o: $(hdrdir)/ruby/internal/intern/file.h +memory_view.o: $(hdrdir)/ruby/internal/intern/gc.h +memory_view.o: $(hdrdir)/ruby/internal/intern/hash.h +memory_view.o: $(hdrdir)/ruby/internal/intern/io.h +memory_view.o: $(hdrdir)/ruby/internal/intern/load.h +memory_view.o: $(hdrdir)/ruby/internal/intern/marshal.h +memory_view.o: $(hdrdir)/ruby/internal/intern/numeric.h +memory_view.o: $(hdrdir)/ruby/internal/intern/object.h +memory_view.o: $(hdrdir)/ruby/internal/intern/parse.h +memory_view.o: $(hdrdir)/ruby/internal/intern/proc.h +memory_view.o: $(hdrdir)/ruby/internal/intern/process.h +memory_view.o: $(hdrdir)/ruby/internal/intern/random.h +memory_view.o: $(hdrdir)/ruby/internal/intern/range.h +memory_view.o: $(hdrdir)/ruby/internal/intern/rational.h +memory_view.o: $(hdrdir)/ruby/internal/intern/re.h +memory_view.o: $(hdrdir)/ruby/internal/intern/ruby.h +memory_view.o: $(hdrdir)/ruby/internal/intern/select.h +memory_view.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +memory_view.o: $(hdrdir)/ruby/internal/intern/signal.h +memory_view.o: $(hdrdir)/ruby/internal/intern/sprintf.h +memory_view.o: $(hdrdir)/ruby/internal/intern/string.h +memory_view.o: $(hdrdir)/ruby/internal/intern/struct.h +memory_view.o: $(hdrdir)/ruby/internal/intern/thread.h +memory_view.o: $(hdrdir)/ruby/internal/intern/time.h +memory_view.o: $(hdrdir)/ruby/internal/intern/variable.h +memory_view.o: $(hdrdir)/ruby/internal/intern/vm.h +memory_view.o: $(hdrdir)/ruby/internal/interpreter.h +memory_view.o: $(hdrdir)/ruby/internal/iterator.h +memory_view.o: $(hdrdir)/ruby/internal/memory.h +memory_view.o: $(hdrdir)/ruby/internal/method.h +memory_view.o: $(hdrdir)/ruby/internal/module.h +memory_view.o: $(hdrdir)/ruby/internal/newobj.h +memory_view.o: $(hdrdir)/ruby/internal/rgengc.h +memory_view.o: $(hdrdir)/ruby/internal/scan_args.h +memory_view.o: $(hdrdir)/ruby/internal/special_consts.h +memory_view.o: $(hdrdir)/ruby/internal/static_assert.h +memory_view.o: $(hdrdir)/ruby/internal/stdalign.h +memory_view.o: $(hdrdir)/ruby/internal/stdbool.h +memory_view.o: $(hdrdir)/ruby/internal/symbol.h +memory_view.o: $(hdrdir)/ruby/internal/value.h +memory_view.o: $(hdrdir)/ruby/internal/value_type.h +memory_view.o: $(hdrdir)/ruby/internal/variable.h +memory_view.o: $(hdrdir)/ruby/internal/warning_push.h +memory_view.o: $(hdrdir)/ruby/internal/xmalloc.h +memory_view.o: $(hdrdir)/ruby/memory_view.h +memory_view.o: $(hdrdir)/ruby/missing.h +memory_view.o: $(hdrdir)/ruby/onigmo.h +memory_view.o: $(hdrdir)/ruby/oniguruma.h +memory_view.o: $(hdrdir)/ruby/ruby.h +memory_view.o: $(hdrdir)/ruby/st.h +memory_view.o: $(hdrdir)/ruby/subst.h +memory_view.o: closure.h +memory_view.o: conversions.h +memory_view.o: fiddle.h +memory_view.o: function.h +memory_view.o: memory_view.c +pinned.o: $(RUBY_EXTCONF_H) +pinned.o: $(arch_hdrdir)/ruby/config.h +pinned.o: $(hdrdir)/ruby.h +pinned.o: $(hdrdir)/ruby/assert.h +pinned.o: $(hdrdir)/ruby/backward.h +pinned.o: $(hdrdir)/ruby/backward/2/assume.h +pinned.o: $(hdrdir)/ruby/backward/2/attributes.h +pinned.o: $(hdrdir)/ruby/backward/2/bool.h +pinned.o: $(hdrdir)/ruby/backward/2/inttypes.h +pinned.o: $(hdrdir)/ruby/backward/2/limits.h +pinned.o: $(hdrdir)/ruby/backward/2/long_long.h +pinned.o: $(hdrdir)/ruby/backward/2/stdalign.h +pinned.o: $(hdrdir)/ruby/backward/2/stdarg.h +pinned.o: $(hdrdir)/ruby/defines.h +pinned.o: $(hdrdir)/ruby/intern.h +pinned.o: $(hdrdir)/ruby/internal/anyargs.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/char.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/double.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/int.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/long.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/short.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +pinned.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +pinned.o: $(hdrdir)/ruby/internal/assume.h +pinned.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +pinned.o: $(hdrdir)/ruby/internal/attr/artificial.h +pinned.o: $(hdrdir)/ruby/internal/attr/cold.h +pinned.o: $(hdrdir)/ruby/internal/attr/const.h +pinned.o: $(hdrdir)/ruby/internal/attr/constexpr.h +pinned.o: $(hdrdir)/ruby/internal/attr/deprecated.h +pinned.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +pinned.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +pinned.o: $(hdrdir)/ruby/internal/attr/error.h +pinned.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +pinned.o: $(hdrdir)/ruby/internal/attr/forceinline.h +pinned.o: $(hdrdir)/ruby/internal/attr/format.h +pinned.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +pinned.o: $(hdrdir)/ruby/internal/attr/noalias.h +pinned.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +pinned.o: $(hdrdir)/ruby/internal/attr/noexcept.h +pinned.o: $(hdrdir)/ruby/internal/attr/noinline.h +pinned.o: $(hdrdir)/ruby/internal/attr/nonnull.h +pinned.o: $(hdrdir)/ruby/internal/attr/noreturn.h +pinned.o: $(hdrdir)/ruby/internal/attr/pure.h +pinned.o: $(hdrdir)/ruby/internal/attr/restrict.h +pinned.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +pinned.o: $(hdrdir)/ruby/internal/attr/warning.h +pinned.o: $(hdrdir)/ruby/internal/attr/weakref.h +pinned.o: $(hdrdir)/ruby/internal/cast.h +pinned.o: $(hdrdir)/ruby/internal/compiler_is.h +pinned.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +pinned.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +pinned.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +pinned.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +pinned.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +pinned.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +pinned.o: $(hdrdir)/ruby/internal/compiler_since.h +pinned.o: $(hdrdir)/ruby/internal/config.h +pinned.o: $(hdrdir)/ruby/internal/constant_p.h +pinned.o: $(hdrdir)/ruby/internal/core.h +pinned.o: $(hdrdir)/ruby/internal/core/rarray.h +pinned.o: $(hdrdir)/ruby/internal/core/rbasic.h +pinned.o: $(hdrdir)/ruby/internal/core/rbignum.h +pinned.o: $(hdrdir)/ruby/internal/core/rclass.h +pinned.o: $(hdrdir)/ruby/internal/core/rdata.h +pinned.o: $(hdrdir)/ruby/internal/core/rfile.h +pinned.o: $(hdrdir)/ruby/internal/core/rhash.h +pinned.o: $(hdrdir)/ruby/internal/core/robject.h +pinned.o: $(hdrdir)/ruby/internal/core/rregexp.h +pinned.o: $(hdrdir)/ruby/internal/core/rstring.h +pinned.o: $(hdrdir)/ruby/internal/core/rstruct.h +pinned.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +pinned.o: $(hdrdir)/ruby/internal/ctype.h +pinned.o: $(hdrdir)/ruby/internal/dllexport.h +pinned.o: $(hdrdir)/ruby/internal/dosish.h +pinned.o: $(hdrdir)/ruby/internal/error.h +pinned.o: $(hdrdir)/ruby/internal/eval.h +pinned.o: $(hdrdir)/ruby/internal/event.h +pinned.o: $(hdrdir)/ruby/internal/fl_type.h +pinned.o: $(hdrdir)/ruby/internal/gc.h +pinned.o: $(hdrdir)/ruby/internal/glob.h +pinned.o: $(hdrdir)/ruby/internal/globals.h +pinned.o: $(hdrdir)/ruby/internal/has/attribute.h +pinned.o: $(hdrdir)/ruby/internal/has/builtin.h +pinned.o: $(hdrdir)/ruby/internal/has/c_attribute.h +pinned.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +pinned.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +pinned.o: $(hdrdir)/ruby/internal/has/extension.h +pinned.o: $(hdrdir)/ruby/internal/has/feature.h +pinned.o: $(hdrdir)/ruby/internal/has/warning.h +pinned.o: $(hdrdir)/ruby/internal/intern/array.h +pinned.o: $(hdrdir)/ruby/internal/intern/bignum.h +pinned.o: $(hdrdir)/ruby/internal/intern/class.h +pinned.o: $(hdrdir)/ruby/internal/intern/compar.h +pinned.o: $(hdrdir)/ruby/internal/intern/complex.h +pinned.o: $(hdrdir)/ruby/internal/intern/cont.h +pinned.o: $(hdrdir)/ruby/internal/intern/dir.h +pinned.o: $(hdrdir)/ruby/internal/intern/enum.h +pinned.o: $(hdrdir)/ruby/internal/intern/enumerator.h +pinned.o: $(hdrdir)/ruby/internal/intern/error.h +pinned.o: $(hdrdir)/ruby/internal/intern/eval.h +pinned.o: $(hdrdir)/ruby/internal/intern/file.h +pinned.o: $(hdrdir)/ruby/internal/intern/gc.h +pinned.o: $(hdrdir)/ruby/internal/intern/hash.h +pinned.o: $(hdrdir)/ruby/internal/intern/io.h +pinned.o: $(hdrdir)/ruby/internal/intern/load.h +pinned.o: $(hdrdir)/ruby/internal/intern/marshal.h +pinned.o: $(hdrdir)/ruby/internal/intern/numeric.h +pinned.o: $(hdrdir)/ruby/internal/intern/object.h +pinned.o: $(hdrdir)/ruby/internal/intern/parse.h +pinned.o: $(hdrdir)/ruby/internal/intern/proc.h +pinned.o: $(hdrdir)/ruby/internal/intern/process.h +pinned.o: $(hdrdir)/ruby/internal/intern/random.h +pinned.o: $(hdrdir)/ruby/internal/intern/range.h +pinned.o: $(hdrdir)/ruby/internal/intern/rational.h +pinned.o: $(hdrdir)/ruby/internal/intern/re.h +pinned.o: $(hdrdir)/ruby/internal/intern/ruby.h +pinned.o: $(hdrdir)/ruby/internal/intern/select.h +pinned.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +pinned.o: $(hdrdir)/ruby/internal/intern/signal.h +pinned.o: $(hdrdir)/ruby/internal/intern/sprintf.h +pinned.o: $(hdrdir)/ruby/internal/intern/string.h +pinned.o: $(hdrdir)/ruby/internal/intern/struct.h +pinned.o: $(hdrdir)/ruby/internal/intern/thread.h +pinned.o: $(hdrdir)/ruby/internal/intern/time.h +pinned.o: $(hdrdir)/ruby/internal/intern/variable.h +pinned.o: $(hdrdir)/ruby/internal/intern/vm.h +pinned.o: $(hdrdir)/ruby/internal/interpreter.h +pinned.o: $(hdrdir)/ruby/internal/iterator.h +pinned.o: $(hdrdir)/ruby/internal/memory.h +pinned.o: $(hdrdir)/ruby/internal/method.h +pinned.o: $(hdrdir)/ruby/internal/module.h +pinned.o: $(hdrdir)/ruby/internal/newobj.h +pinned.o: $(hdrdir)/ruby/internal/rgengc.h +pinned.o: $(hdrdir)/ruby/internal/scan_args.h +pinned.o: $(hdrdir)/ruby/internal/special_consts.h +pinned.o: $(hdrdir)/ruby/internal/static_assert.h +pinned.o: $(hdrdir)/ruby/internal/stdalign.h +pinned.o: $(hdrdir)/ruby/internal/stdbool.h +pinned.o: $(hdrdir)/ruby/internal/symbol.h +pinned.o: $(hdrdir)/ruby/internal/value.h +pinned.o: $(hdrdir)/ruby/internal/value_type.h +pinned.o: $(hdrdir)/ruby/internal/variable.h +pinned.o: $(hdrdir)/ruby/internal/warning_push.h +pinned.o: $(hdrdir)/ruby/internal/xmalloc.h +pinned.o: $(hdrdir)/ruby/missing.h +pinned.o: $(hdrdir)/ruby/ruby.h +pinned.o: $(hdrdir)/ruby/st.h +pinned.o: $(hdrdir)/ruby/subst.h +pinned.o: closure.h +pinned.o: conversions.h +pinned.o: fiddle.h +pinned.o: function.h +pinned.o: pinned.c +pointer.o: $(RUBY_EXTCONF_H) +pointer.o: $(arch_hdrdir)/ruby/config.h +pointer.o: $(hdrdir)/ruby.h +pointer.o: $(hdrdir)/ruby/assert.h +pointer.o: $(hdrdir)/ruby/backward.h +pointer.o: $(hdrdir)/ruby/backward/2/assume.h +pointer.o: $(hdrdir)/ruby/backward/2/attributes.h +pointer.o: $(hdrdir)/ruby/backward/2/bool.h +pointer.o: $(hdrdir)/ruby/backward/2/inttypes.h +pointer.o: $(hdrdir)/ruby/backward/2/limits.h +pointer.o: $(hdrdir)/ruby/backward/2/long_long.h +pointer.o: $(hdrdir)/ruby/backward/2/stdalign.h +pointer.o: $(hdrdir)/ruby/backward/2/stdarg.h +pointer.o: $(hdrdir)/ruby/defines.h +pointer.o: $(hdrdir)/ruby/encoding.h +pointer.o: $(hdrdir)/ruby/intern.h +pointer.o: $(hdrdir)/ruby/internal/anyargs.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/char.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/double.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/int.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/long.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/short.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +pointer.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +pointer.o: $(hdrdir)/ruby/internal/assume.h +pointer.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +pointer.o: $(hdrdir)/ruby/internal/attr/artificial.h +pointer.o: $(hdrdir)/ruby/internal/attr/cold.h +pointer.o: $(hdrdir)/ruby/internal/attr/const.h +pointer.o: $(hdrdir)/ruby/internal/attr/constexpr.h +pointer.o: $(hdrdir)/ruby/internal/attr/deprecated.h +pointer.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +pointer.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +pointer.o: $(hdrdir)/ruby/internal/attr/error.h +pointer.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +pointer.o: $(hdrdir)/ruby/internal/attr/forceinline.h +pointer.o: $(hdrdir)/ruby/internal/attr/format.h +pointer.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +pointer.o: $(hdrdir)/ruby/internal/attr/noalias.h +pointer.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +pointer.o: $(hdrdir)/ruby/internal/attr/noexcept.h +pointer.o: $(hdrdir)/ruby/internal/attr/noinline.h +pointer.o: $(hdrdir)/ruby/internal/attr/nonnull.h +pointer.o: $(hdrdir)/ruby/internal/attr/noreturn.h +pointer.o: $(hdrdir)/ruby/internal/attr/pure.h +pointer.o: $(hdrdir)/ruby/internal/attr/restrict.h +pointer.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +pointer.o: $(hdrdir)/ruby/internal/attr/warning.h +pointer.o: $(hdrdir)/ruby/internal/attr/weakref.h +pointer.o: $(hdrdir)/ruby/internal/cast.h +pointer.o: $(hdrdir)/ruby/internal/compiler_is.h +pointer.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +pointer.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +pointer.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +pointer.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +pointer.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +pointer.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +pointer.o: $(hdrdir)/ruby/internal/compiler_since.h +pointer.o: $(hdrdir)/ruby/internal/config.h +pointer.o: $(hdrdir)/ruby/internal/constant_p.h +pointer.o: $(hdrdir)/ruby/internal/core.h +pointer.o: $(hdrdir)/ruby/internal/core/rarray.h +pointer.o: $(hdrdir)/ruby/internal/core/rbasic.h +pointer.o: $(hdrdir)/ruby/internal/core/rbignum.h +pointer.o: $(hdrdir)/ruby/internal/core/rclass.h +pointer.o: $(hdrdir)/ruby/internal/core/rdata.h +pointer.o: $(hdrdir)/ruby/internal/core/rfile.h +pointer.o: $(hdrdir)/ruby/internal/core/rhash.h +pointer.o: $(hdrdir)/ruby/internal/core/robject.h +pointer.o: $(hdrdir)/ruby/internal/core/rregexp.h +pointer.o: $(hdrdir)/ruby/internal/core/rstring.h +pointer.o: $(hdrdir)/ruby/internal/core/rstruct.h +pointer.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +pointer.o: $(hdrdir)/ruby/internal/ctype.h +pointer.o: $(hdrdir)/ruby/internal/dllexport.h +pointer.o: $(hdrdir)/ruby/internal/dosish.h +pointer.o: $(hdrdir)/ruby/internal/encoding/coderange.h +pointer.o: $(hdrdir)/ruby/internal/encoding/ctype.h +pointer.o: $(hdrdir)/ruby/internal/encoding/encoding.h +pointer.o: $(hdrdir)/ruby/internal/encoding/pathname.h +pointer.o: $(hdrdir)/ruby/internal/encoding/re.h +pointer.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +pointer.o: $(hdrdir)/ruby/internal/encoding/string.h +pointer.o: $(hdrdir)/ruby/internal/encoding/symbol.h +pointer.o: $(hdrdir)/ruby/internal/encoding/transcode.h +pointer.o: $(hdrdir)/ruby/internal/error.h +pointer.o: $(hdrdir)/ruby/internal/eval.h +pointer.o: $(hdrdir)/ruby/internal/event.h +pointer.o: $(hdrdir)/ruby/internal/fl_type.h +pointer.o: $(hdrdir)/ruby/internal/gc.h +pointer.o: $(hdrdir)/ruby/internal/glob.h +pointer.o: $(hdrdir)/ruby/internal/globals.h +pointer.o: $(hdrdir)/ruby/internal/has/attribute.h +pointer.o: $(hdrdir)/ruby/internal/has/builtin.h +pointer.o: $(hdrdir)/ruby/internal/has/c_attribute.h +pointer.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +pointer.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +pointer.o: $(hdrdir)/ruby/internal/has/extension.h +pointer.o: $(hdrdir)/ruby/internal/has/feature.h +pointer.o: $(hdrdir)/ruby/internal/has/warning.h +pointer.o: $(hdrdir)/ruby/internal/intern/array.h +pointer.o: $(hdrdir)/ruby/internal/intern/bignum.h +pointer.o: $(hdrdir)/ruby/internal/intern/class.h +pointer.o: $(hdrdir)/ruby/internal/intern/compar.h +pointer.o: $(hdrdir)/ruby/internal/intern/complex.h +pointer.o: $(hdrdir)/ruby/internal/intern/cont.h +pointer.o: $(hdrdir)/ruby/internal/intern/dir.h +pointer.o: $(hdrdir)/ruby/internal/intern/enum.h +pointer.o: $(hdrdir)/ruby/internal/intern/enumerator.h +pointer.o: $(hdrdir)/ruby/internal/intern/error.h +pointer.o: $(hdrdir)/ruby/internal/intern/eval.h +pointer.o: $(hdrdir)/ruby/internal/intern/file.h +pointer.o: $(hdrdir)/ruby/internal/intern/gc.h +pointer.o: $(hdrdir)/ruby/internal/intern/hash.h +pointer.o: $(hdrdir)/ruby/internal/intern/io.h +pointer.o: $(hdrdir)/ruby/internal/intern/load.h +pointer.o: $(hdrdir)/ruby/internal/intern/marshal.h +pointer.o: $(hdrdir)/ruby/internal/intern/numeric.h +pointer.o: $(hdrdir)/ruby/internal/intern/object.h +pointer.o: $(hdrdir)/ruby/internal/intern/parse.h +pointer.o: $(hdrdir)/ruby/internal/intern/proc.h +pointer.o: $(hdrdir)/ruby/internal/intern/process.h +pointer.o: $(hdrdir)/ruby/internal/intern/random.h +pointer.o: $(hdrdir)/ruby/internal/intern/range.h +pointer.o: $(hdrdir)/ruby/internal/intern/rational.h +pointer.o: $(hdrdir)/ruby/internal/intern/re.h +pointer.o: $(hdrdir)/ruby/internal/intern/ruby.h +pointer.o: $(hdrdir)/ruby/internal/intern/select.h +pointer.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +pointer.o: $(hdrdir)/ruby/internal/intern/signal.h +pointer.o: $(hdrdir)/ruby/internal/intern/sprintf.h +pointer.o: $(hdrdir)/ruby/internal/intern/string.h +pointer.o: $(hdrdir)/ruby/internal/intern/struct.h +pointer.o: $(hdrdir)/ruby/internal/intern/thread.h +pointer.o: $(hdrdir)/ruby/internal/intern/time.h +pointer.o: $(hdrdir)/ruby/internal/intern/variable.h +pointer.o: $(hdrdir)/ruby/internal/intern/vm.h +pointer.o: $(hdrdir)/ruby/internal/interpreter.h +pointer.o: $(hdrdir)/ruby/internal/iterator.h +pointer.o: $(hdrdir)/ruby/internal/memory.h +pointer.o: $(hdrdir)/ruby/internal/method.h +pointer.o: $(hdrdir)/ruby/internal/module.h +pointer.o: $(hdrdir)/ruby/internal/newobj.h +pointer.o: $(hdrdir)/ruby/internal/rgengc.h +pointer.o: $(hdrdir)/ruby/internal/scan_args.h +pointer.o: $(hdrdir)/ruby/internal/special_consts.h +pointer.o: $(hdrdir)/ruby/internal/static_assert.h +pointer.o: $(hdrdir)/ruby/internal/stdalign.h +pointer.o: $(hdrdir)/ruby/internal/stdbool.h +pointer.o: $(hdrdir)/ruby/internal/symbol.h +pointer.o: $(hdrdir)/ruby/internal/value.h +pointer.o: $(hdrdir)/ruby/internal/value_type.h +pointer.o: $(hdrdir)/ruby/internal/variable.h +pointer.o: $(hdrdir)/ruby/internal/warning_push.h +pointer.o: $(hdrdir)/ruby/internal/xmalloc.h +pointer.o: $(hdrdir)/ruby/io.h +pointer.o: $(hdrdir)/ruby/memory_view.h +pointer.o: $(hdrdir)/ruby/missing.h +pointer.o: $(hdrdir)/ruby/onigmo.h +pointer.o: $(hdrdir)/ruby/oniguruma.h +pointer.o: $(hdrdir)/ruby/ruby.h +pointer.o: $(hdrdir)/ruby/st.h +pointer.o: $(hdrdir)/ruby/subst.h +pointer.o: closure.h +pointer.o: conversions.h +pointer.o: fiddle.h +pointer.o: function.h +pointer.o: pointer.c +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/fiddle/extconf.rb b/ext/fiddle/extconf.rb new file mode 100644 index 0000000000..053456d534 --- /dev/null +++ b/ext/fiddle/extconf.rb @@ -0,0 +1,267 @@ +# frozen_string_literal: true +require 'mkmf' + +# :stopdoc: + +def gcc? + RbConfig::CONFIG["GCC"] == "yes" +end + +def disable_optimization_build_flag(flags) + if gcc? + expanded_flags = RbConfig.expand(flags.dup) + optimization_option_pattern = /(^|\s)?-O\d(\s|$)?/ + if optimization_option_pattern.match?(expanded_flags) + expanded_flags.gsub(optimization_option_pattern, '\\1-Og\\2') + else + flags + " -Og" + end + else + flags + end +end + +def enable_debug_build_flag(flags) + if gcc? + expanded_flags = RbConfig.expand(flags.dup) + debug_option_pattern = /(^|\s)-g(?:gdb)?\d?(\s|$)/ + if debug_option_pattern.match?(expanded_flags) + expanded_flags.gsub(debug_option_pattern, '\\1-ggdb3\\2') + else + flags + " -ggdb3" + end + else + flags + end +end + +checking_for(checking_message("--enable-debug-build option")) do + enable_debug_build = enable_config("debug-build", false) + if enable_debug_build + $CFLAGS = disable_optimization_build_flag($CFLAGS) + $CFLAGS = enable_debug_build_flag($CFLAGS) + end + enable_debug_build +end + +libffi_version = nil +have_libffi = false +bundle = enable_config('bundled-libffi') +unless bundle + dir_config 'libffi' + + if pkg_config("libffi") + libffi_version = pkg_config("libffi", "modversion") + end + + have_ffi_header = false + if have_header(ffi_header = 'ffi.h') + have_ffi_header = true + elsif have_header(ffi_header = 'ffi/ffi.h') + $defs.push('-DUSE_HEADER_HACKS') + have_ffi_header = true + end + if have_ffi_header && (have_library('ffi') || have_library('libffi')) + have_libffi = true + end +end + +unless have_libffi + # for https://github.com/ruby/fiddle + extlibs_rb = File.expand_path("../../bin/extlibs.rb", $srcdir) + if bundle && File.exist?(extlibs_rb) + require "fileutils" + require_relative "../../bin/extlibs" + extlibs = ExtLibs.new + cache_dir = File.expand_path("../../tmp/.download_cache", $srcdir) + ext_dir = File.expand_path("../../ext", $srcdir) + Dir.glob("#{$srcdir}/libffi-*/").each{|dir| FileUtils.rm_rf(dir)} + extlibs.run(["--cache=#{cache_dir}", ext_dir]) + end + if bundle != false + libffi_package_name = Dir.glob("#{$srcdir}/libffi-*/") + .map {|n| File.basename(n)} + .max_by {|n| n.scan(/\d+/).map(&:to_i)} + end + unless libffi_package_name + raise "missing libffi. Please install libffi." + end + + libffi_srcdir = "#{$srcdir}/#{libffi_package_name}" + ffi_header = 'ffi.h' + libffi = Struct.new(*%I[dir srcdir builddir include lib a cflags ldflags opt arch]).new + libffi.dir = libffi_package_name + if $srcdir == "." + libffi.builddir = libffi_package_name + libffi.srcdir = "." + else + libffi.builddir = libffi.dir + libffi.srcdir = relative_from(libffi_srcdir, "..") + end + libffi.include = "#{libffi.builddir}/include" + libffi.lib = "#{libffi.builddir}/.libs" + libffi.a = "#{libffi.lib}/libffi_convenience.#{$LIBEXT}" + nowarn = CONFIG.merge("warnflags"=>"") + libffi.cflags = RbConfig.expand("$(CFLAGS)".dup, nowarn) + libffi_version = libffi_package_name[/libffi-(.*)/, 1] + + FileUtils.mkdir_p(libffi.dir) + libffi.opt = CONFIG['configure_args'][/'(-C)'/, 1] + libffi.ldflags = RbConfig.expand("$(LDFLAGS) #{libpathflag([relative_from($topdir, "..")])} #{$LIBRUBYARG}".dup) + libffi.arch = RbConfig::CONFIG['host'] + if $mswin + unless find_executable(as = /x64/ =~ libffi.arch ? "ml64" : "ml") + raise "missing #{as} command." + end + $defs << "-DFFI_BUILDING" + libffi_config = "#{relative_from($srcdir, '..')}/win32/libffi-config.rb" + config = CONFIG.merge("top_srcdir" => $top_srcdir) + args = $ruby.gsub(/:\/=\\/, '') + args.gsub!(/\)\\/, ')/') + args = args.shellsplit + args.map! {|s| RbConfig.expand(s, config)} + args << '-C' << libffi.dir << libffi_config + opts = {} + else + args = %W[sh #{libffi.srcdir}/configure ] + opts = {chdir: libffi.dir} + end + cc = RbConfig::CONFIG['CC'] + cxx = RbConfig::CONFIG['CXX'] + ld = RbConfig::CONFIG['LD'] + args.concat %W[ + --srcdir=#{libffi.srcdir} + --host=#{libffi.arch} + ] + args << ($enable_shared || !$static ? '--enable-shared' : '--enable-static') + args << libffi.opt if libffi.opt + args.concat %W[ + CC=#{cc} CFLAGS=#{libffi.cflags} + CXX=#{cxx} CXXFLAGS=#{RbConfig.expand("$(CXXFLAGS)".dup, nowarn)} + LD=#{ld} LDFLAGS=#{libffi.ldflags} + ] + + FileUtils.rm_f("#{libffi.include}/ffitarget.h") + Logging::open do + Logging.message("%p in %p\n", args, opts) + unless system(*args, **opts) + begin + IO.copy_stream(libffi.dir + "/config.log", Logging.instance_variable_get(:@logfile)) + rescue SystemCallError => e + Logging.message("%s\n", e.message) + end + raise "failed to configure libffi. Please install libffi." + end + end + if $mswin && File.file?("#{libffi.include}/ffitarget.h") + FileUtils.rm_f("#{libffi.include}/ffitarget.h") + end + unless File.file?("#{libffi.include}/ffitarget.h") + FileUtils.cp("#{libffi_srcdir}/src/x86/ffitarget.h", libffi.include, preserve: true) + end + $INCFLAGS << " -I" << libffi.include +end + +if libffi_version + # If libffi_version contains rc version, just ignored. + libffi_version = libffi_version.gsub(/-rc\d+/, '') + libffi_version = (libffi_version.split('.').map(&:to_i) + [0,0])[0,3] + $defs.push(%{-DRUBY_LIBFFI_MODVERSION=#{ '%d%03d%03d' % libffi_version }}) + warn "libffi_version: #{libffi_version.join('.')}" +end + +case +when $mswin, $mingw, (libffi_version && (libffi_version <=> [3, 2]) >= 0) + $defs << "-DUSE_FFI_CLOSURE_ALLOC=1" +when (libffi_version && (libffi_version <=> [3, 2]) < 0) +else + have_func('ffi_closure_alloc', ffi_header) +end + +if libffi_version + if (libffi_version <=> [3, 0, 11]) >= 0 + $defs << "-DHAVE_FFI_PREP_CIF_VAR" + end +else + have_func('ffi_prep_cif_var', ffi_header) +end + +have_header 'sys/mman.h' +have_header 'link.h' + +if have_header "dlfcn.h" + have_library "dl" + + %w{ dlopen dlclose dlsym }.each do |func| + abort "missing function #{func}" unless have_func(func) + end + + have_func "dlerror" + have_func "dlinfo" + have_const("RTLD_DI_LINKMAP", "dlfcn.h") +elsif have_header "windows.h" + %w{ LoadLibrary FreeLibrary GetProcAddress GetModuleFileName }.each do |func| + abort "missing function #{func}" unless have_func(func) + end + + have_library "ws2_32" +end + +have_const('FFI_STDCALL', ffi_header) + +config = File.read(RbConfig.expand(File.join($arch_hdrdir, "ruby/config.h"))) +types = {"SIZE_T"=>"SSIZE_T", "PTRDIFF_T"=>nil, "INTPTR_T"=>nil} +types.each do |type, signed| + if /^\#define\s+SIZEOF_#{type}\s+(SIZEOF_(.+)|\d+)/ =~ config + if size = $2 and size != 'VOIDP' + size = types.fetch(size) {size} + $defs << "-DTYPE_#{signed||type}=TYPE_#{size}" + end + if signed + check_signedness(type.downcase, "stddef.h") + end + else + check_signedness(type.downcase, "stddef.h") + end +end + +if libffi + $LOCAL_LIBS.prepend("./#{libffi.a} ").strip! # to exts.mk + $INCFLAGS.gsub!(/-I#{libffi.dir}/, '-I$(LIBFFI_DIR)') +end +create_makefile 'fiddle' do |conf| + if !libffi + next conf << "LIBFFI_CLEAN = none\n" + elsif $gnumake && !$nmake + submake_arg = "-C $(LIBFFI_DIR)\n" + else + submake_pre = "cd $(LIBFFI_DIR) && #{config_string("exec")}".strip + end + if $nmake + cmd = "$(RUBY) -C $(LIBFFI_DIR) #{libffi_config} --srcdir=$(LIBFFI_SRCDIR)" + else + cmd = "cd $(LIBFFI_DIR) && #$exec $(LIBFFI_SRCDIR)/configure #{libffi.opt}" + end + sep = "/" + seprpl = config_string('BUILD_FILE_SEPARATOR') {|s| sep = s; ":/=#{s}" if s != "/"} || "" + conf << <<-MK.gsub(/^ +| +$/, '') + PWD = + LIBFFI_CONFIGURE = #{cmd} + LIBFFI_ARCH = #{libffi.arch} + LIBFFI_SRCDIR = #{libffi.srcdir.sub(libffi.dir, '$(LIBFFI_DIR)')} + LIBFFI_DIR = #{libffi.dir} + LIBFFI_A = #{libffi.a.sub(libffi.dir, '$(LIBFFI_DIR)')} + LIBFFI_CFLAGS = #{libffi.cflags} + LIBFFI_LDFLAGS = #{libffi.ldflags} + FFI_H = $(LIBFFI_DIR)/include/ffi.h + SUBMAKE_PRE = #{submake_pre} + SUBMAKE_ARG = #{submake_arg} + LIBFFI_CLEAN = libffi + MK +end + +if libffi + $LIBPATH.pop +end + +# :startdoc: diff --git a/ext/fiddle/extlibs b/ext/fiddle/extlibs new file mode 100644 index 0000000000..68dac46a95 --- /dev/null +++ b/ext/fiddle/extlibs @@ -0,0 +1,13 @@ +ver = 3.2.1 +pkg = libffi-$(ver) + +https://ftp.osuosl.org/pub/blfs/conglomeration/libffi/$(pkg).tar.gz \ + md5:83b89587607e3eb65c70d361f13bab43 \ + sha512:980ca30a8d76f963fca722432b1fe5af77d7a4e4d2eac5144fbc5374d4c596609a293440573f4294207e1bdd9fda80ad1e1cafb2ffb543df5a275bc3bd546483 \ + # + win32/$(pkg)-mswin.patch -p0 + +$(pkg)/config.guess -> /tool/config.guess +$(pkg)/config.sub -> /tool/config.sub + +! chdir: $(pkg)| autoconf || exit 0 diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c new file mode 100644 index 0000000000..a8b5123269 --- /dev/null +++ b/ext/fiddle/fiddle.c @@ -0,0 +1,553 @@ +#include <fiddle.h> + +VALUE mFiddle; +VALUE rb_eFiddleDLError; +VALUE rb_eFiddleError; + +void Init_fiddle_pointer(void); +void Init_fiddle_pinned(void); + +#ifdef HAVE_RUBY_MEMORY_VIEW_H +void Init_fiddle_memory_view(void); +#endif + +/* + * call-seq: Fiddle.malloc(size) + * + * Allocate +size+ bytes of memory and return the integer memory address + * for the allocated memory. + */ +static VALUE +rb_fiddle_malloc(VALUE self, VALUE size) +{ + void *ptr; + ptr = (void*)ruby_xcalloc(1, NUM2SIZET(size)); + return PTR2NUM(ptr); +} + +/* + * call-seq: Fiddle.realloc(addr, size) + * + * Change the size of the memory allocated at the memory location +addr+ to + * +size+ bytes. Returns the memory address of the reallocated memory, which + * may be different than the address passed in. + */ +static VALUE +rb_fiddle_realloc(VALUE self, VALUE addr, VALUE size) +{ + void *ptr = NUM2PTR(addr); + + ptr = (void*)ruby_xrealloc(ptr, NUM2SIZET(size)); + return PTR2NUM(ptr); +} + +/* + * call-seq: Fiddle.free(addr) + * + * Free the memory at address +addr+ + */ +VALUE +rb_fiddle_free(VALUE self, VALUE addr) +{ + void *ptr = NUM2PTR(addr); + + ruby_xfree(ptr); + return Qnil; +} + +/* + * call-seq: Fiddle.dlunwrap(addr) + * + * Returns the hexadecimal representation of a memory pointer address +addr+ + * + * Example: + * + * lib = Fiddle.dlopen('/lib64/libc-2.15.so') + * => #<Fiddle::Handle:0x00000001342460> + * + * lib['strcpy'].to_s(16) + * => "7f59de6dd240" + * + * Fiddle.dlunwrap(Fiddle.dlwrap(lib['strcpy'].to_s(16))) + * => "7f59de6dd240" + */ +VALUE +rb_fiddle_ptr2value(VALUE self, VALUE addr) +{ + return (VALUE)NUM2PTR(addr); +} + +/* + * call-seq: Fiddle.dlwrap(val) + * + * Returns a memory pointer of a function's hexadecimal address location +val+ + * + * Example: + * + * lib = Fiddle.dlopen('/lib64/libc-2.15.so') + * => #<Fiddle::Handle:0x00000001342460> + * + * Fiddle.dlwrap(lib['strcpy'].to_s(16)) + * => 25522520 + */ +static VALUE +rb_fiddle_value2ptr(VALUE self, VALUE val) +{ + return PTR2NUM((void*)val); +} + +void Init_fiddle_handle(void); + +void +Init_fiddle(void) +{ + /* + * Document-module: Fiddle + * + * A libffi wrapper for Ruby. + * + * == Description + * + * Fiddle is an extension to translate a foreign function interface (FFI) + * with ruby. + * + * It wraps {libffi}[http://sourceware.org/libffi/], a popular C library + * which provides a portable interface that allows code written in one + * language to call code written in another language. + * + * == Example + * + * Here we will use Fiddle::Function to wrap {floor(3) from + * libm}[http://linux.die.net/man/3/floor] + * + * require 'fiddle' + * + * libm = Fiddle.dlopen('/lib/libm.so.6') + * + * floor = Fiddle::Function.new( + * libm['floor'], + * [Fiddle::TYPE_DOUBLE], + * Fiddle::TYPE_DOUBLE + * ) + * + * puts floor.call(3.14159) #=> 3.0 + * + * + */ + mFiddle = rb_define_module("Fiddle"); + + /* + * Document-class: Fiddle::Error + * + * Generic error class for Fiddle + */ + rb_eFiddleError = rb_define_class_under(mFiddle, "Error", rb_eStandardError); + + /* + * Ruby installed by RubyInstaller for Windows always require + * bundled Fiddle because ruby_installer/runtime/dll_directory.rb + * requires Fiddle. It's used by + * rubygems/defaults/operating_system.rb. It means that the + * bundled Fiddle is always required on initialization. + * + * We just remove existing Fiddle::DLError here to override + * the bundled Fiddle. + */ + if (rb_const_defined(mFiddle, rb_intern("DLError"))) { + rb_const_remove(mFiddle, rb_intern("DLError")); + } + + /* + * Document-class: Fiddle::DLError + * + * standard dynamic load exception + */ + rb_eFiddleDLError = rb_define_class_under(mFiddle, "DLError", rb_eFiddleError); + + /* Document-const: TYPE_VOID + * + * C type - void + */ + rb_define_const(mFiddle, "TYPE_VOID", INT2NUM(TYPE_VOID)); + + /* Document-const: TYPE_VOIDP + * + * C type - void* + */ + rb_define_const(mFiddle, "TYPE_VOIDP", INT2NUM(TYPE_VOIDP)); + + /* Document-const: TYPE_CHAR + * + * C type - char + */ + rb_define_const(mFiddle, "TYPE_CHAR", INT2NUM(TYPE_CHAR)); + + /* Document-const: TYPE_SHORT + * + * C type - short + */ + rb_define_const(mFiddle, "TYPE_SHORT", INT2NUM(TYPE_SHORT)); + + /* Document-const: TYPE_INT + * + * C type - int + */ + rb_define_const(mFiddle, "TYPE_INT", INT2NUM(TYPE_INT)); + + /* Document-const: TYPE_LONG + * + * C type - long + */ + rb_define_const(mFiddle, "TYPE_LONG", INT2NUM(TYPE_LONG)); + +#if HAVE_LONG_LONG + /* Document-const: TYPE_LONG_LONG + * + * C type - long long + */ + rb_define_const(mFiddle, "TYPE_LONG_LONG", INT2NUM(TYPE_LONG_LONG)); +#endif + +#ifdef TYPE_INT8_T + /* Document-const: TYPE_INT8_T + * + * C type - int8_t + */ + rb_define_const(mFiddle, "TYPE_INT8_T", INT2NUM(TYPE_INT8_T)); +#endif + +#ifdef TYPE_INT16_T + /* Document-const: TYPE_INT16_T + * + * C type - int16_t + */ + rb_define_const(mFiddle, "TYPE_INT16_T", INT2NUM(TYPE_INT16_T)); +#endif + +#ifdef TYPE_INT32_T + /* Document-const: TYPE_INT32_T + * + * C type - int32_t + */ + rb_define_const(mFiddle, "TYPE_INT32_T", INT2NUM(TYPE_INT32_T)); +#endif + +#ifdef TYPE_INT64_T + /* Document-const: TYPE_INT64_T + * + * C type - int64_t + */ + rb_define_const(mFiddle, "TYPE_INT64_T", INT2NUM(TYPE_INT64_T)); +#endif + + /* Document-const: TYPE_FLOAT + * + * C type - float + */ + rb_define_const(mFiddle, "TYPE_FLOAT", INT2NUM(TYPE_FLOAT)); + + /* Document-const: TYPE_DOUBLE + * + * C type - double + */ + rb_define_const(mFiddle, "TYPE_DOUBLE", INT2NUM(TYPE_DOUBLE)); + +#ifdef HAVE_FFI_PREP_CIF_VAR + /* Document-const: TYPE_VARIADIC + * + * C type - ... + */ + rb_define_const(mFiddle, "TYPE_VARIADIC", INT2NUM(TYPE_VARIADIC)); +#endif + + /* Document-const: TYPE_CONST_STRING + * + * C type - const char* ('\0' terminated const char*) + */ + rb_define_const(mFiddle, "TYPE_CONST_STRING", INT2NUM(TYPE_CONST_STRING)); + + /* Document-const: TYPE_SIZE_T + * + * C type - size_t + */ + rb_define_const(mFiddle, "TYPE_SIZE_T", INT2NUM(TYPE_SIZE_T)); + + /* Document-const: TYPE_SSIZE_T + * + * C type - ssize_t + */ + rb_define_const(mFiddle, "TYPE_SSIZE_T", INT2NUM(TYPE_SSIZE_T)); + + /* Document-const: TYPE_PTRDIFF_T + * + * C type - ptrdiff_t + */ + rb_define_const(mFiddle, "TYPE_PTRDIFF_T", INT2NUM(TYPE_PTRDIFF_T)); + + /* Document-const: TYPE_INTPTR_T + * + * C type - intptr_t + */ + rb_define_const(mFiddle, "TYPE_INTPTR_T", INT2NUM(TYPE_INTPTR_T)); + + /* Document-const: TYPE_UINTPTR_T + * + * C type - uintptr_t + */ + rb_define_const(mFiddle, "TYPE_UINTPTR_T", INT2NUM(TYPE_UINTPTR_T)); + + /* Document-const: ALIGN_VOIDP + * + * The alignment size of a void* + */ + rb_define_const(mFiddle, "ALIGN_VOIDP", INT2NUM(ALIGN_VOIDP)); + + /* Document-const: ALIGN_CHAR + * + * The alignment size of a char + */ + rb_define_const(mFiddle, "ALIGN_CHAR", INT2NUM(ALIGN_CHAR)); + + /* Document-const: ALIGN_SHORT + * + * The alignment size of a short + */ + rb_define_const(mFiddle, "ALIGN_SHORT", INT2NUM(ALIGN_SHORT)); + + /* Document-const: ALIGN_INT + * + * The alignment size of an int + */ + rb_define_const(mFiddle, "ALIGN_INT", INT2NUM(ALIGN_INT)); + + /* Document-const: ALIGN_LONG + * + * The alignment size of a long + */ + rb_define_const(mFiddle, "ALIGN_LONG", INT2NUM(ALIGN_LONG)); + +#if HAVE_LONG_LONG + /* Document-const: ALIGN_LONG_LONG + * + * The alignment size of a long long + */ + rb_define_const(mFiddle, "ALIGN_LONG_LONG", INT2NUM(ALIGN_LONG_LONG)); +#endif + + /* Document-const: ALIGN_INT8_T + * + * The alignment size of a int8_t + */ + rb_define_const(mFiddle, "ALIGN_INT8_T", INT2NUM(ALIGN_INT8_T)); + + /* Document-const: ALIGN_INT16_T + * + * The alignment size of a int16_t + */ + rb_define_const(mFiddle, "ALIGN_INT16_T", INT2NUM(ALIGN_INT16_T)); + + /* Document-const: ALIGN_INT32_T + * + * The alignment size of a int32_t + */ + rb_define_const(mFiddle, "ALIGN_INT32_T", INT2NUM(ALIGN_INT32_T)); + + /* Document-const: ALIGN_INT64_T + * + * The alignment size of a int64_t + */ + rb_define_const(mFiddle, "ALIGN_INT64_T", INT2NUM(ALIGN_INT64_T)); + + /* Document-const: ALIGN_FLOAT + * + * The alignment size of a float + */ + rb_define_const(mFiddle, "ALIGN_FLOAT", INT2NUM(ALIGN_FLOAT)); + + /* Document-const: ALIGN_DOUBLE + * + * The alignment size of a double + */ + rb_define_const(mFiddle, "ALIGN_DOUBLE",INT2NUM(ALIGN_DOUBLE)); + + /* Document-const: ALIGN_SIZE_T + * + * The alignment size of a size_t + */ + rb_define_const(mFiddle, "ALIGN_SIZE_T", INT2NUM(ALIGN_OF(size_t))); + + /* Document-const: ALIGN_SSIZE_T + * + * The alignment size of a ssize_t + */ + rb_define_const(mFiddle, "ALIGN_SSIZE_T", INT2NUM(ALIGN_OF(size_t))); /* same as size_t */ + + /* Document-const: ALIGN_PTRDIFF_T + * + * The alignment size of a ptrdiff_t + */ + rb_define_const(mFiddle, "ALIGN_PTRDIFF_T", INT2NUM(ALIGN_OF(ptrdiff_t))); + + /* Document-const: ALIGN_INTPTR_T + * + * The alignment size of a intptr_t + */ + rb_define_const(mFiddle, "ALIGN_INTPTR_T", INT2NUM(ALIGN_OF(intptr_t))); + + /* Document-const: ALIGN_UINTPTR_T + * + * The alignment size of a uintptr_t + */ + rb_define_const(mFiddle, "ALIGN_UINTPTR_T", INT2NUM(ALIGN_OF(uintptr_t))); + + /* Document-const: WINDOWS + * + * Returns a boolean regarding whether the host is WIN32 + */ +#if defined(_WIN32) + rb_define_const(mFiddle, "WINDOWS", Qtrue); +#else + rb_define_const(mFiddle, "WINDOWS", Qfalse); +#endif + + /* Document-const: SIZEOF_VOIDP + * + * size of a void* + */ + rb_define_const(mFiddle, "SIZEOF_VOIDP", INT2NUM(sizeof(void*))); + + /* Document-const: SIZEOF_CHAR + * + * size of a char + */ + rb_define_const(mFiddle, "SIZEOF_CHAR", INT2NUM(sizeof(char))); + + /* Document-const: SIZEOF_SHORT + * + * size of a short + */ + rb_define_const(mFiddle, "SIZEOF_SHORT", INT2NUM(sizeof(short))); + + /* Document-const: SIZEOF_INT + * + * size of an int + */ + rb_define_const(mFiddle, "SIZEOF_INT", INT2NUM(sizeof(int))); + + /* Document-const: SIZEOF_LONG + * + * size of a long + */ + rb_define_const(mFiddle, "SIZEOF_LONG", INT2NUM(sizeof(long))); + +#if HAVE_LONG_LONG + /* Document-const: SIZEOF_LONG_LONG + * + * size of a long long + */ + rb_define_const(mFiddle, "SIZEOF_LONG_LONG", INT2NUM(sizeof(LONG_LONG))); +#endif + + /* Document-const: SIZEOF_INT8_T + * + * size of a int8_t + */ + rb_define_const(mFiddle, "SIZEOF_INT8_T", INT2NUM(sizeof(int8_t))); + + /* Document-const: SIZEOF_INT16_T + * + * size of a int16_t + */ + rb_define_const(mFiddle, "SIZEOF_INT16_T", INT2NUM(sizeof(int16_t))); + + /* Document-const: SIZEOF_INT32_T + * + * size of a int32_t + */ + rb_define_const(mFiddle, "SIZEOF_INT32_T", INT2NUM(sizeof(int32_t))); + + /* Document-const: SIZEOF_INT64_T + * + * size of a int64_t + */ + rb_define_const(mFiddle, "SIZEOF_INT64_T", INT2NUM(sizeof(int64_t))); + + /* Document-const: SIZEOF_FLOAT + * + * size of a float + */ + rb_define_const(mFiddle, "SIZEOF_FLOAT", INT2NUM(sizeof(float))); + + /* Document-const: SIZEOF_DOUBLE + * + * size of a double + */ + rb_define_const(mFiddle, "SIZEOF_DOUBLE",INT2NUM(sizeof(double))); + + /* Document-const: SIZEOF_SIZE_T + * + * size of a size_t + */ + rb_define_const(mFiddle, "SIZEOF_SIZE_T", INT2NUM(sizeof(size_t))); + + /* Document-const: SIZEOF_SSIZE_T + * + * size of a ssize_t + */ + rb_define_const(mFiddle, "SIZEOF_SSIZE_T", INT2NUM(sizeof(size_t))); /* same as size_t */ + + /* Document-const: SIZEOF_PTRDIFF_T + * + * size of a ptrdiff_t + */ + rb_define_const(mFiddle, "SIZEOF_PTRDIFF_T", INT2NUM(sizeof(ptrdiff_t))); + + /* Document-const: SIZEOF_INTPTR_T + * + * size of a intptr_t + */ + rb_define_const(mFiddle, "SIZEOF_INTPTR_T", INT2NUM(sizeof(intptr_t))); + + /* Document-const: SIZEOF_UINTPTR_T + * + * size of a uintptr_t + */ + rb_define_const(mFiddle, "SIZEOF_UINTPTR_T", INT2NUM(sizeof(uintptr_t))); + + /* Document-const: SIZEOF_CONST_STRING + * + * size of a const char* + */ + rb_define_const(mFiddle, "SIZEOF_CONST_STRING", INT2NUM(sizeof(const char*))); + + /* Document-const: RUBY_FREE + * + * Address of the ruby_xfree() function + */ + rb_define_const(mFiddle, "RUBY_FREE", PTR2NUM(ruby_xfree)); + + /* Document-const: BUILD_RUBY_PLATFORM + * + * Platform built against (i.e. "x86_64-linux", etc.) + * + * See also RUBY_PLATFORM + */ + rb_define_const(mFiddle, "BUILD_RUBY_PLATFORM", rb_str_new2(RUBY_PLATFORM)); + + rb_define_module_function(mFiddle, "dlwrap", rb_fiddle_value2ptr, 1); + rb_define_module_function(mFiddle, "dlunwrap", rb_fiddle_ptr2value, 1); + rb_define_module_function(mFiddle, "malloc", rb_fiddle_malloc, 1); + rb_define_module_function(mFiddle, "realloc", rb_fiddle_realloc, 2); + rb_define_module_function(mFiddle, "free", rb_fiddle_free, 1); + + Init_fiddle_function(); + Init_fiddle_closure(); + Init_fiddle_handle(); + Init_fiddle_pointer(); + Init_fiddle_pinned(); + +#ifdef HAVE_RUBY_MEMORY_VIEW_H + Init_fiddle_memory_view(); +#endif +} +/* vim: set noet sws=4 sw=4: */ diff --git a/ext/fiddle/fiddle.gemspec b/ext/fiddle/fiddle.gemspec new file mode 100644 index 0000000000..a9c0ec4026 --- /dev/null +++ b/ext/fiddle/fiddle.gemspec @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +version_module = Module.new do + version_rb = File.join(__dir__, "lib/fiddle/version.rb") + module_eval(File.read(version_rb), version_rb, __LINE__) +end + +Gem::Specification.new do |spec| + spec.name = "fiddle" + spec.version = version_module::Fiddle::VERSION + spec.authors = ["Aaron Patterson", "SHIBATA Hiroshi"] + spec.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org"] + + spec.summary = %q{A libffi wrapper for Ruby.} + spec.description = %q{A libffi wrapper for Ruby.} + spec.homepage = "https://github.com/ruby/fiddle" + spec.licenses = ["Ruby", "BSD-2-Clause"] + + spec.files = [ + "LICENSE.txt", + "README.md", + "Rakefile", + "bin/downloader.rb", + "bin/extlibs.rb", + "ext/fiddle/closure.c", + "ext/fiddle/closure.h", + "ext/fiddle/conversions.c", + "ext/fiddle/conversions.h", + "ext/fiddle/depend", + "ext/fiddle/extconf.rb", + "ext/fiddle/extlibs", + "ext/fiddle/fiddle.c", + "ext/fiddle/fiddle.h", + "ext/fiddle/function.c", + "ext/fiddle/function.h", + "ext/fiddle/handle.c", + "ext/fiddle/memory_view.c", + "ext/fiddle/pinned.c", + "ext/fiddle/pointer.c", + "ext/fiddle/win32/fficonfig.h", + "ext/fiddle/win32/libffi-3.2.1-mswin.patch", + "ext/fiddle/win32/libffi-config.rb", + "ext/fiddle/win32/libffi.mk.tmpl", + "fiddle.gemspec", + "lib/fiddle.rb", + "lib/fiddle/closure.rb", + "lib/fiddle/cparser.rb", + "lib/fiddle/function.rb", + "lib/fiddle/import.rb", + "lib/fiddle/pack.rb", + "lib/fiddle/struct.rb", + "lib/fiddle/types.rb", + "lib/fiddle/value.rb", + "lib/fiddle/version.rb", + ] + spec.require_paths = ["lib"] + spec.extensions = ["ext/fiddle/extconf.rb"] + + spec.required_ruby_version = ">= 2.5.0" + + spec.metadata["msys2_mingw_dependencies"] = "libffi" +end diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h new file mode 100644 index 0000000000..9de62a58cc --- /dev/null +++ b/ext/fiddle/fiddle.h @@ -0,0 +1,205 @@ +#ifndef FIDDLE_H +#define FIDDLE_H + +#include <ruby.h> +#include <errno.h> + +#if defined(_WIN32) +#include <windows.h> +#endif + +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif + +#if defined(HAVE_LINK_H) +# include <link.h> +#endif + +#if defined(HAVE_DLFCN_H) +# include <dlfcn.h> +# /* some stranger systems may not define all of these */ +#ifndef RTLD_LAZY +#define RTLD_LAZY 0 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif +#ifndef RTLD_NOW +#define RTLD_NOW 0 +#endif +#else +# if defined(_WIN32) +# include <windows.h> +# define dlopen(name,flag) ((void*)LoadLibrary(name)) +# define dlerror() strerror(rb_w32_map_errno(GetLastError())) +# define dlsym(handle,name) ((void*)GetProcAddress((handle),(name))) +# define RTLD_LAZY -1 +# define RTLD_NOW -1 +# define RTLD_GLOBAL -1 +# endif +#endif + +#ifdef USE_HEADER_HACKS +#include <ffi/ffi.h> +#else +#include <ffi.h> +#endif + +#undef ffi_type_uchar +#undef ffi_type_schar +#undef ffi_type_ushort +#undef ffi_type_sshort +#undef ffi_type_uint +#undef ffi_type_sint +#undef ffi_type_ulong +#undef ffi_type_slong + +#if CHAR_BIT == 8 +# define ffi_type_uchar ffi_type_uint8 +# define ffi_type_schar ffi_type_sint8 +#else +# error "CHAR_BIT not supported" +#endif + +#if SIZEOF_SHORT == 2 +# define ffi_type_ushort ffi_type_uint16 +# define ffi_type_sshort ffi_type_sint16 +#elif SIZEOF_SHORT == 4 +# define ffi_type_ushort ffi_type_uint32 +# define ffi_type_sshort ffi_type_sint32 +#else +# error "short size not supported" +#endif + +#if SIZEOF_INT == 2 +# define ffi_type_uint ffi_type_uint16 +# define ffi_type_sint ffi_type_sint16 +#elif SIZEOF_INT == 4 +# define ffi_type_uint ffi_type_uint32 +# define ffi_type_sint ffi_type_sint32 +#elif SIZEOF_INT == 8 +# define ffi_type_uint ffi_type_uint64 +# define ffi_type_sint ffi_type_sint64 +#else +# error "int size not supported" +#endif + +#if SIZEOF_LONG == 4 +# define ffi_type_ulong ffi_type_uint32 +# define ffi_type_slong ffi_type_sint32 +#elif SIZEOF_LONG == 8 +# define ffi_type_ulong ffi_type_uint64 +# define ffi_type_slong ffi_type_sint64 +#else +# error "long size not supported" +#endif + +#if HAVE_LONG_LONG +# if SIZEOF_LONG_LONG == 8 +# define ffi_type_slong_long ffi_type_sint64 +# define ffi_type_ulong_long ffi_type_uint64 +# else +# error "long long size not supported" +# endif +#endif + +#include <closure.h> +#include <conversions.h> +#include <function.h> + +#define TYPE_VOID 0 +#define TYPE_VOIDP 1 +#define TYPE_CHAR 2 +#define TYPE_SHORT 3 +#define TYPE_INT 4 +#define TYPE_LONG 5 +#if HAVE_LONG_LONG +#define TYPE_LONG_LONG 6 +#endif +#define TYPE_FLOAT 7 +#define TYPE_DOUBLE 8 +#define TYPE_VARIADIC 9 +#define TYPE_CONST_STRING 10 + +#define TYPE_INT8_T TYPE_CHAR +#if SIZEOF_SHORT == 2 +# define TYPE_INT16_T TYPE_SHORT +#elif SIZEOF_INT == 2 +# define TYPE_INT16_T TYPE_INT +#endif +#if SIZEOF_SHORT == 4 +# define TYPE_INT32_T TYPE_SHORT +#elif SIZEOF_INT == 4 +# define TYPE_INT32_T TYPE_INT +#elif SIZEOF_LONG == 4 +# define TYPE_INT32_T TYPE_LONG +#endif +#if SIZEOF_INT == 8 +# define TYPE_INT64_T TYPE_INT +#elif SIZEOF_LONG == 8 +# define TYPE_INT64_T TYPE_LONG +#elif defined(TYPE_LONG_LONG) +# define TYPE_INT64_T TYPE_LONG_LONG +#endif + +#ifndef TYPE_SSIZE_T +# if SIZEOF_SIZE_T == SIZEOF_INT +# define TYPE_SSIZE_T TYPE_INT +# elif SIZEOF_SIZE_T == SIZEOF_LONG +# define TYPE_SSIZE_T TYPE_LONG +# elif defined HAVE_LONG_LONG && SIZEOF_SIZE_T == SIZEOF_LONG_LONG +# define TYPE_SSIZE_T TYPE_LONG_LONG +# endif +#endif +#define TYPE_SIZE_T (-1*SIGNEDNESS_OF_SIZE_T*TYPE_SSIZE_T) + +#ifndef TYPE_PTRDIFF_T +# if SIZEOF_PTRDIFF_T == SIZEOF_INT +# define TYPE_PTRDIFF_T TYPE_INT +# elif SIZEOF_PTRDIFF_T == SIZEOF_LONG +# define TYPE_PTRDIFF_T TYPE_LONG +# elif defined HAVE_LONG_LONG && SIZEOF_PTRDIFF_T == SIZEOF_LONG_LONG +# define TYPE_PTRDIFF_T TYPE_LONG_LONG +# endif +#endif + +#ifndef TYPE_INTPTR_T +# if SIZEOF_INTPTR_T == SIZEOF_INT +# define TYPE_INTPTR_T TYPE_INT +# elif SIZEOF_INTPTR_T == SIZEOF_LONG +# define TYPE_INTPTR_T TYPE_LONG +# elif defined HAVE_LONG_LONG && SIZEOF_INTPTR_T == SIZEOF_LONG_LONG +# define TYPE_INTPTR_T TYPE_LONG_LONG +# endif +#endif +#define TYPE_UINTPTR_T (-TYPE_INTPTR_T) + +#define ALIGN_OF(type) offsetof(struct {char align_c; type align_x;}, align_x) + +#define ALIGN_VOIDP ALIGN_OF(void*) +#define ALIGN_CHAR ALIGN_OF(char) +#define ALIGN_SHORT ALIGN_OF(short) +#define ALIGN_INT ALIGN_OF(int) +#define ALIGN_LONG ALIGN_OF(long) +#if HAVE_LONG_LONG +#define ALIGN_LONG_LONG ALIGN_OF(LONG_LONG) +#endif +#define ALIGN_FLOAT ALIGN_OF(float) +#define ALIGN_DOUBLE ALIGN_OF(double) + +#define ALIGN_INT8_T ALIGN_OF(int8_t) +#define ALIGN_INT16_T ALIGN_OF(int16_t) +#define ALIGN_INT32_T ALIGN_OF(int32_t) +#define ALIGN_INT64_T ALIGN_OF(int64_t) + +extern VALUE mFiddle; +extern VALUE rb_eFiddleDLError; + +VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type); + +typedef void (*rb_fiddle_freefunc_t)(void*); +VALUE rb_fiddle_ptr_new_wrap(void *ptr, long size, rb_fiddle_freefunc_t func, VALUE wrap0, VALUE wrap1); + +#endif +/* vim: set noet sws=4 sw=4: */ diff --git a/ext/fiddle/function.c b/ext/fiddle/function.c new file mode 100644 index 0000000000..274d181d17 --- /dev/null +++ b/ext/fiddle/function.c @@ -0,0 +1,491 @@ +#include <fiddle.h> +#include <ruby/thread.h> + +#include <stdbool.h> + +#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) \ + Check_Max_Args_(name, len, "") +#define Check_Max_Args_Long(name, len) \ + Check_Max_Args_(name, len, "l") +#define Check_Max_Args_(name, len, fmt) \ + do { \ + if ((size_t)(len) >= MAX_ARGS) { \ + rb_raise(rb_eTypeError, \ + "%s is so large " \ + "that it can cause integer overflow (%"fmt"d)", \ + (name), (len)); \ + } \ + } while (0) + +static void +deallocate(void *p) +{ + ffi_cif *cif = p; + if (cif->arg_types) xfree(cif->arg_types); + xfree(cif); +} + +static size_t +function_memsize(const void *p) +{ + /* const */ffi_cif *ptr = (ffi_cif *)p; + size_t size = 0; + + 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 VALUE +normalize_argument_types(const char *name, + VALUE arg_types, + bool *is_variadic) +{ + VALUE normalized_arg_types; + int i; + int n_arg_types; + *is_variadic = false; + + Check_Type(arg_types, T_ARRAY); + n_arg_types = RARRAY_LENINT(arg_types); + Check_Max_Args(name, n_arg_types); + + normalized_arg_types = rb_ary_new_capa(n_arg_types); + for (i = 0; i < n_arg_types; i++) { + VALUE arg_type = RARRAY_AREF(arg_types, i); + int c_arg_type; + arg_type = rb_fiddle_type_ensure(arg_type); + c_arg_type = NUM2INT(arg_type); + if (c_arg_type == TYPE_VARIADIC) { + if (i != n_arg_types - 1) { + rb_raise(rb_eArgError, + "Fiddle::TYPE_VARIADIC must be the last argument type: " + "%"PRIsVALUE, + arg_types); + } + *is_variadic = true; + break; + } + else { + (void)INT2FFI_TYPE(c_arg_type); /* raise */ + } + rb_ary_push(normalized_arg_types, INT2FIX(c_arg_type)); + } + + /* freeze to prevent inconsistency at calling #to_int later */ + OBJ_FREEZE(normalized_arg_types); + return normalized_arg_types; +} + +static VALUE +initialize(int argc, VALUE argv[], VALUE self) +{ + ffi_cif * cif; + VALUE ptr, arg_types, ret_type, abi, kwargs; + VALUE name = Qnil; + VALUE need_gvl = Qfalse; + int c_ret_type; + bool is_variadic = false; + ffi_abi c_ffi_abi; + void *cfunc; + + rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwargs); + rb_iv_set(self, "@closure", ptr); + + if (!NIL_P(kwargs)) { + enum { + kw_name, + kw_need_gvl, + kw_max_, + }; + static ID kw[kw_max_]; + VALUE args[kw_max_]; + if (!kw[0]) { + kw[kw_name] = rb_intern_const("name"); + kw[kw_need_gvl] = rb_intern_const("need_gvl"); + } + rb_get_kwargs(kwargs, kw, 0, kw_max_, args); + if (args[kw_name] != Qundef) { + name = args[kw_name]; + } + if (args[kw_need_gvl] != Qundef) { + need_gvl = args[kw_need_gvl]; + } + } + rb_iv_set(self, "@name", name); + rb_iv_set(self, "@need_gvl", need_gvl); + + ptr = rb_Integer(ptr); + cfunc = NUM2PTR(ptr); + PTR2NUM(cfunc); + c_ffi_abi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi); + abi = INT2FIX(c_ffi_abi); + ret_type = rb_fiddle_type_ensure(ret_type); + c_ret_type = NUM2INT(ret_type); + (void)INT2FFI_TYPE(c_ret_type); /* raise */ + ret_type = INT2FIX(c_ret_type); + + arg_types = normalize_argument_types("argument types", + arg_types, + &is_variadic); +#ifndef HAVE_FFI_PREP_CIF_VAR + if (is_variadic) { + rb_raise(rb_eNotImpError, + "ffi_prep_cif_var() is required in libffi " + "for variadic arguments"); + } +#endif + + rb_iv_set(self, "@ptr", ptr); + rb_iv_set(self, "@argument_types", arg_types); + rb_iv_set(self, "@return_type", ret_type); + rb_iv_set(self, "@abi", abi); + rb_iv_set(self, "@is_variadic", is_variadic ? Qtrue : Qfalse); + + TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); + cif->arg_types = NULL; + + return self; +} + +struct nogvl_ffi_call_args { + ffi_cif *cif; + void (*fn)(void); + void **values; + fiddle_generic retval; +}; + +static void * +nogvl_ffi_call(void *ptr) +{ + struct nogvl_ffi_call_args *args = ptr; + + ffi_call(args->cif, args->fn, &args->retval, args->values); + + return NULL; +} + +static VALUE +function_call(int argc, VALUE argv[], VALUE self) +{ + struct nogvl_ffi_call_args args = { 0 }; + fiddle_generic *generic_args; + VALUE cfunc; + VALUE abi; + VALUE arg_types; + VALUE cPointer; + VALUE is_variadic; + VALUE need_gvl; + int n_arg_types; + int n_fixed_args = 0; + int n_call_args = 0; + int i; + int i_call; + VALUE converted_args = Qnil; + VALUE alloc_buffer = 0; + + cfunc = rb_iv_get(self, "@ptr"); + abi = rb_iv_get(self, "@abi"); + arg_types = rb_iv_get(self, "@argument_types"); + cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); + is_variadic = rb_iv_get(self, "@is_variadic"); + need_gvl = rb_iv_get(self, "@need_gvl"); + + n_arg_types = RARRAY_LENINT(arg_types); + n_fixed_args = n_arg_types; + if (RTEST(is_variadic)) { + if (argc < n_arg_types) { + rb_error_arity(argc, n_arg_types, UNLIMITED_ARGUMENTS); + } + if (((argc - n_arg_types) % 2) != 0) { + rb_raise(rb_eArgError, + "variadic arguments must be type and value pairs: " + "%"PRIsVALUE, + rb_ary_new_from_values(argc, argv)); + } + n_call_args = n_arg_types + ((argc - n_arg_types) / 2); + } + else { + if (argc != n_arg_types) { + rb_error_arity(argc, n_arg_types, n_arg_types); + } + n_call_args = n_arg_types; + } + Check_Max_Args("the number of arguments", n_call_args); + + TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif); + + if (is_variadic && args.cif->arg_types) { + xfree(args.cif->arg_types); + args.cif->arg_types = NULL; + } + + if (!args.cif->arg_types) { + VALUE fixed_arg_types = arg_types; + VALUE return_type; + int c_return_type; + ffi_type *ffi_return_type; + ffi_type **ffi_arg_types; + ffi_status result; + + arg_types = rb_ary_dup(fixed_arg_types); + for (i = n_fixed_args; i < argc; i += 2) { + VALUE arg_type = argv[i]; + int c_arg_type; + arg_type = rb_fiddle_type_ensure(arg_type); + c_arg_type = NUM2INT(arg_type); + (void)INT2FFI_TYPE(c_arg_type); /* raise */ + rb_ary_push(arg_types, INT2FIX(c_arg_type)); + } + + return_type = rb_iv_get(self, "@return_type"); + c_return_type = FIX2INT(return_type); + ffi_return_type = INT2FFI_TYPE(c_return_type); + + ffi_arg_types = xcalloc(n_call_args + 1, sizeof(ffi_type *)); + for (i_call = 0; i_call < n_call_args; i_call++) { + VALUE arg_type; + int c_arg_type; + arg_type = RARRAY_AREF(arg_types, i_call); + c_arg_type = FIX2INT(arg_type); + ffi_arg_types[i_call] = INT2FFI_TYPE(c_arg_type); + } + ffi_arg_types[i_call] = NULL; + + if (is_variadic) { +#ifdef HAVE_FFI_PREP_CIF_VAR + result = ffi_prep_cif_var(args.cif, + FIX2INT(abi), + n_fixed_args, + n_call_args, + ffi_return_type, + ffi_arg_types); +#else + /* This code is never used because ffi_prep_cif_var() + * availability check is done in #initialize. */ + result = FFI_BAD_TYPEDEF; +#endif + } + else { + result = ffi_prep_cif(args.cif, + FIX2INT(abi), + n_call_args, + ffi_return_type, + ffi_arg_types); + } + if (result != FFI_OK) { + xfree(ffi_arg_types); + args.cif->arg_types = NULL; + rb_raise(rb_eRuntimeError, "error creating CIF %d", result); + } + } + + generic_args = ALLOCV(alloc_buffer, + sizeof(fiddle_generic) * n_call_args + + sizeof(void *) * (n_call_args + 1)); + args.values = (void **)((char *)generic_args + + sizeof(fiddle_generic) * n_call_args); + + for (i = 0, i_call = 0; + i < argc && i_call < n_call_args; + i++, i_call++) { + VALUE arg_type; + int c_arg_type; + VALUE original_src; + VALUE src; + arg_type = RARRAY_AREF(arg_types, i_call); + c_arg_type = FIX2INT(arg_type); + if (i >= n_fixed_args) { + i++; + } + src = argv[i]; + + if (c_arg_type == TYPE_VOIDP) { + if (NIL_P(src)) { + src = INT2FIX(0); + } + else if (cPointer != CLASS_OF(src)) { + src = rb_funcall(cPointer, rb_intern("[]"), 1, src); + if (NIL_P(converted_args)) { + converted_args = rb_ary_new(); + } + rb_ary_push(converted_args, src); + } + src = rb_Integer(src); + } + + original_src = src; + VALUE2GENERIC(c_arg_type, src, &generic_args[i_call]); + if (src != original_src) { + if (NIL_P(converted_args)) { + converted_args = rb_ary_new(); + } + rb_ary_push(converted_args, src); + } + args.values[i_call] = (void *)&generic_args[i_call]; + } + args.values[i_call] = NULL; + args.fn = (void(*)(void))NUM2PTR(cfunc); + + if (RTEST(need_gvl)) { + ffi_call(args.cif, args.fn, &(args.retval), args.values); + } + else { + (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0); + } + + { + int errno_keep = errno; +#if defined(_WIN32) + DWORD error = WSAGetLastError(); + int socket_error = WSAGetLastError(); + rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, + ULONG2NUM(error)); + rb_funcall(mFiddle, rb_intern("win32_last_socket_error="), 1, + INT2NUM(socket_error)); +#endif + rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno_keep)); + } + + ALLOCV_END(alloc_buffer); + + return GENERIC2VALUE(rb_iv_get(self, "@return_type"), args.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" + * #=> #<Fiddle::Handle:0x00000001d7a8d8> + * f = Fiddle::Function.new( + * @libc['strcpy'], + * [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], + * Fiddle::TYPE_VOIDP) + * #=> #<Fiddle::Function:0x00000001d8ee00> + * buff = "000" + * #=> "000" + * str = f.call(buff, "123") + * #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000> + * str.to_s + * => "123" + * + * === ABI check + * + * @libc = Fiddle.dlopen "/lib/libc.so.6" + * #=> #<Fiddle::Handle:0x00000001d7a8d8> + * f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP) + * #=> #<Fiddle::Function:0x00000001d8ee00> + * 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+. + * Caller must ensure the underlying function is called in a + * thread-safe manner if running in a multi-threaded process. + * + * Note that it is not thread-safe to use this method to + * directly or indirectly call many Ruby C-extension APIs unless + * you don't pass +need_gvl: true+ to Fiddle::Function#new. + * + * 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, + * name: nil, + * need_gvl: false) + * + * 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 + * * +name+ is the name of the function + * * +need_gvl+ is whether GVL is needed to call the function + * + */ + rb_define_method(cFiddleFunction, "initialize", initialize, -1); +} +/* vim: set noet sws=4 sw=4: */ diff --git a/ext/fiddle/function.h b/ext/fiddle/function.h new file mode 100644 index 0000000000..829e592c8a --- /dev/null +++ b/ext/fiddle/function.h @@ -0,0 +1,8 @@ +#ifndef FIDDLE_FUNCTION_H +#define FIDDLE_FUNCTION_H + +#include <fiddle.h> + +void Init_fiddle_function(void); + +#endif diff --git a/ext/fiddle/handle.c b/ext/fiddle/handle.c new file mode 100644 index 0000000000..76b90909d3 --- /dev/null +++ b/ext/fiddle/handle.c @@ -0,0 +1,535 @@ +#include <ruby.h> +#include <fiddle.h> + +VALUE rb_cHandle; + +struct dl_handle { + void *ptr; + int open; + int enable_close; +}; + +#ifdef _WIN32 +# ifndef _WIN32_WCE +static void * +w32_coredll(void) +{ + MEMORY_BASIC_INFORMATION m; + memset(&m, 0, sizeof(m)); + if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL; + return m.AllocationBase; +} +# endif + +static int +w32_dlclose(void *ptr) +{ +# ifndef _WIN32_WCE + if( ptr == w32_coredll() ) return 0; +# endif + if( FreeLibrary((HMODULE)ptr) ) return 0; + return errno = rb_w32_map_errno(GetLastError()); +} +#define dlclose(ptr) w32_dlclose(ptr) +#endif + +static void +fiddle_handle_free(void *ptr) +{ + struct dl_handle *fiddle_handle = ptr; + if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){ + dlclose(fiddle_handle->ptr); + } + xfree(ptr); +} + +static size_t +fiddle_handle_memsize(const void *ptr) +{ + return sizeof(struct dl_handle); +} + +static const rb_data_type_t fiddle_handle_data_type = { + "fiddle/handle", + {0, fiddle_handle_free, fiddle_handle_memsize,}, +}; + +/* + * call-seq: close + * + * Close this handle. + * + * Calling close more than once will raise a Fiddle::DLError exception. + */ +static VALUE +rb_fiddle_handle_close(VALUE self) +{ + struct dl_handle *fiddle_handle; + + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + if(fiddle_handle->open) { + int ret = dlclose(fiddle_handle->ptr); + fiddle_handle->open = 0; + + /* Check dlclose for successful return value */ + if(ret) { +#if defined(HAVE_DLERROR) + rb_raise(rb_eFiddleDLError, "%s", dlerror()); +#else + rb_raise(rb_eFiddleDLError, "could not close handle"); +#endif + } + return INT2NUM(ret); + } + rb_raise(rb_eFiddleDLError, "dlclose() called too many times"); + + UNREACHABLE; +} + +static VALUE +rb_fiddle_handle_s_allocate(VALUE klass) +{ + VALUE obj; + struct dl_handle *fiddle_handle; + + obj = TypedData_Make_Struct(rb_cHandle, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + fiddle_handle->ptr = 0; + fiddle_handle->open = 0; + fiddle_handle->enable_close = 0; + + return obj; +} + +static VALUE +predefined_fiddle_handle(void *handle) +{ + VALUE obj = rb_fiddle_handle_s_allocate(rb_cHandle); + struct dl_handle *fiddle_handle = DATA_PTR(obj); + + fiddle_handle->ptr = handle; + fiddle_handle->open = 1; + OBJ_FREEZE(obj); + return obj; +} + +/* + * call-seq: + * new(library = nil, flags = Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL) + * + * Create a new handler that opens +library+ with +flags+. + * + * If no +library+ is specified or +nil+ is given, DEFAULT is used, which is + * the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more. + * + * lib = Fiddle::Handle.new + * + * The default is dependent on OS, and provide a handle for all libraries + * already loaded. For example, in most cases you can use this to access +libc+ + * functions, or ruby functions like +rb_str_new+. + */ +static VALUE +rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self) +{ + void *ptr; + struct dl_handle *fiddle_handle; + VALUE lib, flag; + char *clib; + int cflag; + const char *err; + + switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){ + case 0: + clib = NULL; + cflag = RTLD_LAZY | RTLD_GLOBAL; + break; + case 1: + clib = NIL_P(lib) ? NULL : StringValueCStr(lib); + cflag = RTLD_LAZY | RTLD_GLOBAL; + break; + case 2: + clib = NIL_P(lib) ? NULL : StringValueCStr(lib); + cflag = NUM2INT(flag); + break; + default: + rb_bug("rb_fiddle_handle_new"); + } + +#if defined(_WIN32) + if( !clib ){ + HANDLE rb_libruby_handle(void); + ptr = rb_libruby_handle(); + } + else if( STRCASECMP(clib, "libc") == 0 +# ifdef RUBY_COREDLL + || STRCASECMP(clib, RUBY_COREDLL) == 0 + || STRCASECMP(clib, RUBY_COREDLL".dll") == 0 +# endif + ){ +# ifdef _WIN32_WCE + ptr = dlopen("coredll.dll", cflag); +# else + (void)cflag; + ptr = w32_coredll(); +# endif + } + else +#endif + ptr = dlopen(clib, cflag); +#if defined(HAVE_DLERROR) + if( !ptr && (err = dlerror()) ){ + rb_raise(rb_eFiddleDLError, "%s", err); + } +#else + if( !ptr ){ + err = dlerror(); + rb_raise(rb_eFiddleDLError, "%s", err); + } +#endif + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){ + dlclose(fiddle_handle->ptr); + } + fiddle_handle->ptr = ptr; + fiddle_handle->open = 1; + fiddle_handle->enable_close = 0; + + if( rb_block_given_p() ){ + rb_ensure(rb_yield, self, rb_fiddle_handle_close, self); + } + + return Qnil; +} + +/* + * call-seq: enable_close + * + * Enable a call to dlclose() when this handle is garbage collected. + */ +static VALUE +rb_fiddle_handle_enable_close(VALUE self) +{ + struct dl_handle *fiddle_handle; + + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + fiddle_handle->enable_close = 1; + return Qnil; +} + +/* + * call-seq: disable_close + * + * Disable a call to dlclose() when this handle is garbage collected. + */ +static VALUE +rb_fiddle_handle_disable_close(VALUE self) +{ + struct dl_handle *fiddle_handle; + + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + fiddle_handle->enable_close = 0; + return Qnil; +} + +/* + * call-seq: close_enabled? + * + * Returns +true+ if dlclose() will be called when this handle is garbage collected. + * + * See man(3) dlclose() for more info. + */ +static VALUE +rb_fiddle_handle_close_enabled_p(VALUE self) +{ + struct dl_handle *fiddle_handle; + + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + + if(fiddle_handle->enable_close) return Qtrue; + return Qfalse; +} + +/* + * call-seq: to_i + * + * Returns the memory address for this handle. + */ +static VALUE +rb_fiddle_handle_to_i(VALUE self) +{ + struct dl_handle *fiddle_handle; + + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + return PTR2NUM(fiddle_handle->ptr); +} + +/* + * call-seq: to_ptr + * + * Returns the Fiddle::Pointer of this handle. + */ +static VALUE +rb_fiddle_handle_to_ptr(VALUE self) +{ + struct dl_handle *fiddle_handle; + + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + return rb_fiddle_ptr_new_wrap(fiddle_handle->ptr, 0, 0, self, 0); +} + +static VALUE fiddle_handle_sym(void *handle, VALUE symbol); + +/* + * Document-method: sym + * + * call-seq: sym(name) + * + * Get the address as an Integer for the function named +name+. + */ +static VALUE +rb_fiddle_handle_sym(VALUE self, VALUE sym) +{ + struct dl_handle *fiddle_handle; + + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + if( ! fiddle_handle->open ){ + rb_raise(rb_eFiddleDLError, "closed handle"); + } + + return fiddle_handle_sym(fiddle_handle->ptr, sym); +} + +#ifndef RTLD_NEXT +#define RTLD_NEXT NULL +#endif +#ifndef RTLD_DEFAULT +#define RTLD_DEFAULT NULL +#endif + +/* + * Document-method: sym + * + * call-seq: sym(name) + * + * Get the address as an Integer for the function named +name+. The function + * is searched via dlsym on RTLD_NEXT. + * + * See man(3) dlsym() for more info. + */ +static VALUE +rb_fiddle_handle_s_sym(VALUE self, VALUE sym) +{ + return fiddle_handle_sym(RTLD_NEXT, sym); +} + +static VALUE +fiddle_handle_sym(void *handle, VALUE symbol) +{ +#if defined(HAVE_DLERROR) + const char *err; +# define CHECK_DLERROR if ((err = dlerror()) != 0) { func = 0; } +#else +# define CHECK_DLERROR +#endif + void (*func)(); + const char *name = StringValueCStr(symbol); + +#ifdef HAVE_DLERROR + dlerror(); +#endif + func = (void (*)())(VALUE)dlsym(handle, name); + CHECK_DLERROR; +#if defined(FUNC_STDCALL) + if( !func ){ + int i; + int len = (int)strlen(name); + char *name_n; +#if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__) + { + char *name_a = (char*)xmalloc(len+2); + strcpy(name_a, name); + name_n = name_a; + name_a[len] = 'A'; + name_a[len+1] = '\0'; + func = dlsym(handle, name_a); + CHECK_DLERROR; + if( func ) goto found; + name_n = xrealloc(name_a, len+6); + } +#else + name_n = (char*)xmalloc(len+6); +#endif + memcpy(name_n, name, len); + name_n[len++] = '@'; + for( i = 0; i < 256; i += 4 ){ + sprintf(name_n + len, "%d", i); + func = dlsym(handle, name_n); + CHECK_DLERROR; + if( func ) break; + } + if( func ) goto found; + name_n[len-1] = 'A'; + name_n[len++] = '@'; + for( i = 0; i < 256; i += 4 ){ + sprintf(name_n + len, "%d", i); + func = dlsym(handle, name_n); + CHECK_DLERROR; + if( func ) break; + } + found: + xfree(name_n); + } +#endif + if( !func ){ + rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol); + } + + return PTR2NUM(func); +} + +/* + * call-seq: file_name + * + * Returns the file name of this handle. + */ +static VALUE +rb_fiddle_handle_file_name(VALUE self) +{ + struct dl_handle *fiddle_handle; + + TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle); + +#if defined(HAVE_DLINFO) && defined(HAVE_CONST_RTLD_DI_LINKMAP) + { + struct link_map *lm = NULL; + int res = dlinfo(fiddle_handle->ptr, RTLD_DI_LINKMAP, &lm); + if (res == 0 && lm != NULL) { + return rb_str_new_cstr(lm->l_name); + } + else { +#if defined(HAVE_DLERROR) + rb_raise(rb_eFiddleDLError, "could not get handle file name: %s", dlerror()); +#else + rb_raise(rb_eFiddleDLError, "could not get handle file name"); +#endif + } + } +#elif defined(HAVE_GETMODULEFILENAME) + { + char filename[MAX_PATH]; + DWORD res = GetModuleFileName(fiddle_handle->ptr, filename, MAX_PATH); + if (res == 0) { + rb_raise(rb_eFiddleDLError, "could not get handle file name: %s", dlerror()); + } + return rb_str_new_cstr(filename); + } +#else + (void)fiddle_handle; + return Qnil; +#endif +} + +void +Init_fiddle_handle(void) +{ + /* + * Document-class: Fiddle::Handle + * + * The Fiddle::Handle is the manner to access the dynamic library + * + * == Example + * + * === Setup + * + * libc_so = "/lib64/libc.so.6" + * => "/lib64/libc.so.6" + * @handle = Fiddle::Handle.new(libc_so) + * => #<Fiddle::Handle:0x00000000d69ef8> + * + * === Setup, with flags + * + * libc_so = "/lib64/libc.so.6" + * => "/lib64/libc.so.6" + * @handle = Fiddle::Handle.new(libc_so, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL) + * => #<Fiddle::Handle:0x00000000d69ef8> + * + * See RTLD_LAZY and RTLD_GLOBAL + * + * === Addresses to symbols + * + * strcpy_addr = @handle['strcpy'] + * => 140062278451968 + * + * or + * + * strcpy_addr = @handle.sym('strcpy') + * => 140062278451968 + * + */ + rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject); + rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate); + rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1); + rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1); + + /* Document-const: NEXT + * + * A predefined pseudo-handle of RTLD_NEXT + * + * Which will find the next occurrence of a function in the search order + * after the current library. + */ + rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT)); + + /* Document-const: DEFAULT + * + * A predefined pseudo-handle of RTLD_DEFAULT + * + * Which will find the first occurrence of the desired symbol using the + * default library search order + */ + rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT)); + + /* Document-const: RTLD_GLOBAL + * + * rtld Fiddle::Handle flag. + * + * The symbols defined by this library will be made available for symbol + * resolution of subsequently loaded libraries. + */ + rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL)); + + /* Document-const: RTLD_LAZY + * + * rtld Fiddle::Handle flag. + * + * Perform lazy binding. Only resolve symbols as the code that references + * them is executed. If the symbol is never referenced, then it is never + * resolved. (Lazy binding is only performed for function references; + * references to variables are always immediately bound when the library + * is loaded.) + */ + rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY)); + + /* Document-const: RTLD_NOW + * + * rtld Fiddle::Handle flag. + * + * If this value is specified or the environment variable LD_BIND_NOW is + * set to a nonempty string, all undefined symbols in the library are + * resolved before Fiddle.dlopen returns. If this cannot be done an error + * is returned. + */ + rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW)); + + rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1); + rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0); + rb_define_method(rb_cHandle, "to_ptr", rb_fiddle_handle_to_ptr, 0); + rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0); + rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1); + rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1); + rb_define_method(rb_cHandle, "file_name", rb_fiddle_handle_file_name, 0); + rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0); + rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0); + rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0); +} + +/* vim: set noet sws=4 sw=4: */ diff --git a/ext/fiddle/lib/fiddle.rb b/ext/fiddle/lib/fiddle.rb new file mode 100644 index 0000000000..4512989310 --- /dev/null +++ b/ext/fiddle/lib/fiddle.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'fiddle.so' +require 'fiddle/closure' +require 'fiddle/function' +require 'fiddle/version' + +module Fiddle + if WINDOWS + # Returns the last win32 +Error+ of the current executing +Thread+ or nil + # if none + def self.win32_last_error + Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] + end + + # Sets the last win32 +Error+ of the current executing +Thread+ to +error+ + def self.win32_last_error= error + Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error + end + + # Returns the last win32 socket +Error+ of the current executing + # +Thread+ or nil if none + def self.win32_last_socket_error + Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] + end + + # Sets the last win32 socket +Error+ of the current executing + # +Thread+ to +error+ + def self.win32_last_socket_error= error + Thread.current[:__FIDDLE_WIN32_LAST_SOCKET_ERROR__] = error + end + end + + # Returns the last +Error+ of the current executing +Thread+ or nil if none + def self.last_error + Thread.current[:__FIDDLE_LAST_ERROR__] + end + + # Sets the last +Error+ of the current executing +Thread+ to +error+ + def self.last_error= error + Thread.current[:__DL2_LAST_ERROR__] = error + Thread.current[:__FIDDLE_LAST_ERROR__] = error + end + + # call-seq: dlopen(library) => Fiddle::Handle + # + # Creates a new handler that opens +library+, and returns an instance of + # Fiddle::Handle. + # + # If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which + # is the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more. + # + # lib = Fiddle.dlopen(nil) + # + # The default is dependent on OS, and provide a handle for all libraries + # already loaded. For example, in most cases you can use this to access + # +libc+ functions, or ruby functions like +rb_str_new+. + # + # See Fiddle::Handle.new for more. + def dlopen library + Fiddle::Handle.new library + end + module_function :dlopen + + # Add constants for backwards compat + + RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc: + RTLD_LAZY = Handle::RTLD_LAZY # :nodoc: + RTLD_NOW = Handle::RTLD_NOW # :nodoc: +end diff --git a/ext/fiddle/lib/fiddle/closure.rb b/ext/fiddle/lib/fiddle/closure.rb new file mode 100644 index 0000000000..c865a63c20 --- /dev/null +++ b/ext/fiddle/lib/fiddle/closure.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true +module Fiddle + class Closure + + # the C type of the return of the FFI closure + attr_reader :ctype + + # arguments of the FFI closure + attr_reader :args + + # Extends Fiddle::Closure to allow for building the closure in a block + class BlockCaller < Fiddle::Closure + + # == Description + # + # Construct a new BlockCaller object. + # + # * +ctype+ is the C type to be returned + # * +args+ are passed the callback + # * +abi+ is the abi of the closure + # + # If there is an error in preparing the +ffi_cif+ or +ffi_prep_closure+, + # then a RuntimeError will be raised. + # + # == Example + # + # include Fiddle + # + # cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one| + # one + # end + # + # func = Function.new(cb, [TYPE_INT], TYPE_INT) + # + def initialize ctype, args, abi = Fiddle::Function::DEFAULT, &block + super(ctype, args, abi) + @block = block + end + + # Calls the constructed BlockCaller, with +args+ + # + # For an example see Fiddle::Closure::BlockCaller.new + # + def call *args + @block.call(*args) + end + end + end +end diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb new file mode 100644 index 0000000000..93a05513c9 --- /dev/null +++ b/ext/fiddle/lib/fiddle/cparser.rb @@ -0,0 +1,264 @@ +# frozen_string_literal: true +module Fiddle + # A mixin that provides methods for parsing C struct and prototype signatures. + # + # == Example + # require 'fiddle/import' + # + # include Fiddle::CParser + # #=> Object + # + # parse_ctype('int') + # #=> Fiddle::TYPE_INT + # + # parse_struct_signature(['int i', 'char c']) + # #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]] + # + # parse_signature('double sum(double, double)') + # #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]] + # + module CParser + # Parses a C struct's members + # + # Example: + # require 'fiddle/import' + # + # include Fiddle::CParser + # #=> Object + # + # parse_struct_signature(['int i', 'char c']) + # #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]] + # + # parse_struct_signature(['char buffer[80]']) + # #=> [[[Fiddle::TYPE_CHAR, 80]], ["buffer"]] + # + def parse_struct_signature(signature, tymap=nil) + if signature.is_a?(String) + signature = split_arguments(signature, /[,;]/) + elsif signature.is_a?(Hash) + signature = [signature] + end + mems = [] + tys = [] + signature.each{|msig| + msig = compact(msig) if msig.is_a?(String) + case msig + when Hash + msig.each do |struct_name, struct_signature| + struct_name = struct_name.to_s if struct_name.is_a?(Symbol) + struct_name = compact(struct_name) + struct_count = nil + if struct_name =~ /^([\w\*\s]+)\[(\d+)\]$/ + struct_count = $2.to_i + struct_name = $1 + end + if struct_signature.respond_to?(:entity_class) + struct_type = struct_signature + else + parsed_struct = parse_struct_signature(struct_signature, tymap) + struct_type = CStructBuilder.create(CStruct, *parsed_struct) + end + if struct_count + ty = [struct_type, struct_count] + else + ty = struct_type + end + mems.push([struct_name, struct_type.members]) + tys.push(ty) + end + when /^[\w\*\s]+[\*\s](\w+)$/ + mems.push($1) + tys.push(parse_ctype(msig, tymap)) + when /^[\w\*\s]+\(\*(\w+)\)\(.*?\)$/ + mems.push($1) + tys.push(parse_ctype(msig, tymap)) + when /^([\w\*\s]+[\*\s])(\w+)\[(\d+)\]$/ + mems.push($2) + tys.push([parse_ctype($1.strip, tymap), $3.to_i]) + when /^([\w\*\s]+)\[(\d+)\](\w+)$/ + mems.push($3) + tys.push([parse_ctype($1.strip, tymap), $2.to_i]) + else + raise(RuntimeError,"can't parse the struct member: #{msig}") + end + } + return tys, mems + end + + # Parses a C prototype signature + # + # If Hash +tymap+ is provided, the return value and the arguments from the + # +signature+ are expected to be keys, and the value will be the C type to + # be looked up. + # + # Example: + # require 'fiddle/import' + # + # include Fiddle::CParser + # #=> Object + # + # parse_signature('double sum(double, double)') + # #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]] + # + # parse_signature('void update(void (*cb)(int code))') + # #=> ["update", Fiddle::TYPE_VOID, [Fiddle::TYPE_VOIDP]] + # + # parse_signature('char (*getbuffer(void))[80]') + # #=> ["getbuffer", Fiddle::TYPE_VOIDP, []] + # + def parse_signature(signature, tymap=nil) + tymap ||= {} + case compact(signature) + when /^(?:[\w\*\s]+)\(\*(\w+)\((.*?)\)\)(?:\[\w*\]|\(.*?\));?$/ + func, args = $1, $2 + return [func, TYPE_VOIDP, split_arguments(args).collect {|arg| parse_ctype(arg, tymap)}] + when /^([\w\*\s]+[\*\s])(\w+)\((.*?)\);?$/ + ret, func, args = $1.strip, $2, $3 + return [func, parse_ctype(ret, tymap), split_arguments(args).collect {|arg| parse_ctype(arg, tymap)}] + else + raise(RuntimeError,"can't parse the function prototype: #{signature}") + end + end + + # Given a String of C type +ty+, returns the corresponding Fiddle constant. + # + # +ty+ can also accept an Array of C type Strings, and will be returned in + # a corresponding Array. + # + # If Hash +tymap+ is provided, +ty+ is expected to be the key, and the + # value will be the C type to be looked up. + # + # Example: + # require 'fiddle/import' + # + # include Fiddle::CParser + # #=> Object + # + # parse_ctype('int') + # #=> Fiddle::TYPE_INT + # + # parse_ctype('double diff') + # #=> Fiddle::TYPE_DOUBLE + # + # parse_ctype('unsigned char byte') + # #=> -Fiddle::TYPE_CHAR + # + # parse_ctype('const char* const argv[]') + # #=> -Fiddle::TYPE_VOIDP + # + def parse_ctype(ty, tymap=nil) + tymap ||= {} + if ty.is_a?(Array) + return [parse_ctype(ty[0], tymap), ty[1]] + end + ty = ty.gsub(/\Aconst\s+/, "") + case ty + when 'void' + return TYPE_VOID + when /\A(?:(?:signed\s+)?long\s+long(?:\s+int\s+)?|int64_t)(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_LONG_LONG) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_LONG_LONG + when /\A(?:unsigned\s+long\s+long(?:\s+int\s+)?|uint64_t)(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_LONG_LONG) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_LONG_LONG + when /\A(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?\z/ + return TYPE_LONG + when /\Aunsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?\z/ + return -TYPE_LONG + when /\A(?:signed\s+)?int(?:\s+\w+)?\z/ + return TYPE_INT + when /\A(?:unsigned\s+int|uint)(?:\s+\w+)?\z/ + return -TYPE_INT + when /\A(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?\z/ + return TYPE_SHORT + when /\Aunsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?\z/ + return -TYPE_SHORT + when /\A(?:signed\s+)?char(?:\s+\w+)?\z/ + return TYPE_CHAR + when /\Aunsigned\s+char(?:\s+\w+)?\z/ + return -TYPE_CHAR + when /\Aint8_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT8_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_INT8_T + when /\Auint8_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT8_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_INT8_T + when /\Aint16_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT16_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_INT16_T + when /\Auint16_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT16_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_INT16_T + when /\Aint32_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT32_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_INT32_T + when /\Auint32_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT32_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_INT32_T + when /\Aint64_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT64_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return TYPE_INT64_T + when /\Auint64_t(?:\s+\w+)?\z/ + unless Fiddle.const_defined?(:TYPE_INT64_T) + raise(RuntimeError, "unsupported type: #{ty}") + end + return -TYPE_INT64_T + when /\Afloat(?:\s+\w+)?\z/ + return TYPE_FLOAT + when /\Adouble(?:\s+\w+)?\z/ + return TYPE_DOUBLE + when /\Asize_t(?:\s+\w+)?\z/ + return TYPE_SIZE_T + when /\Assize_t(?:\s+\w+)?\z/ + return TYPE_SSIZE_T + when /\Aptrdiff_t(?:\s+\w+)?\z/ + return TYPE_PTRDIFF_T + when /\Aintptr_t(?:\s+\w+)?\z/ + return TYPE_INTPTR_T + when /\Auintptr_t(?:\s+\w+)?\z/ + return TYPE_UINTPTR_T + when /\*/, /\[[\s\d]*\]/ + return TYPE_VOIDP + when "..." + return TYPE_VARIADIC + else + ty = ty.split(' ', 2)[0] + if( tymap[ty] ) + return parse_ctype(tymap[ty], tymap) + else + raise(DLError, "unknown type: #{ty}") + end + end + end + + private + + def split_arguments(arguments, sep=',') + return [] if arguments.strip == 'void' + arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]+|\.\.\.)(?:#{sep}\s*|\z)/).collect {|m| m[0]} + end + + def compact(signature) + signature.gsub(/\s+/, ' ').gsub(/\s*([\(\)\[\]\*,;])\s*/, '\1').strip + end + + end +end diff --git a/ext/fiddle/lib/fiddle/function.rb b/ext/fiddle/lib/fiddle/function.rb new file mode 100644 index 0000000000..dc2e3e6bf5 --- /dev/null +++ b/ext/fiddle/lib/fiddle/function.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true +module Fiddle + class Function + # The ABI of the Function. + attr_reader :abi + + # The address of this function + attr_reader :ptr + + # The name of this function + attr_reader :name + + # Whether GVL is needed to call this function + def need_gvl? + @need_gvl + end + + # The integer memory location of this function + def to_i + ptr.to_i + end + + # Turn this function in to a proc + def to_proc + this = self + lambda { |*args| this.call(*args) } + end + end +end diff --git a/ext/fiddle/lib/fiddle/import.rb b/ext/fiddle/lib/fiddle/import.rb new file mode 100644 index 0000000000..09ffcef544 --- /dev/null +++ b/ext/fiddle/lib/fiddle/import.rb @@ -0,0 +1,320 @@ +# frozen_string_literal: true +require 'fiddle' +require 'fiddle/struct' +require 'fiddle/cparser' + +module Fiddle + + # Used internally by Fiddle::Importer + class CompositeHandler + # Create a new handler with the open +handlers+ + # + # Used internally by Fiddle::Importer.dlload + def initialize(handlers) + @handlers = handlers + end + + # Array of the currently loaded libraries. + def handlers() + @handlers + end + + # Returns the address as an Integer from any handlers with the function + # named +symbol+. + # + # Raises a DLError if the handle is closed. + def sym(symbol) + @handlers.each{|handle| + if( handle ) + begin + addr = handle.sym(symbol) + return addr + rescue DLError + end + end + } + return nil + end + + # See Fiddle::CompositeHandler.sym + def [](symbol) + sym(symbol) + end + end + + # A DSL that provides the means to dynamically load libraries and build + # modules around them including calling extern functions within the C + # library that has been loaded. + # + # == Example + # + # require 'fiddle' + # require 'fiddle/import' + # + # module LibSum + # extend Fiddle::Importer + # dlload './libsum.so' + # extern 'double sum(double*, int)' + # extern 'double split(double)' + # end + # + module Importer + include Fiddle + include CParser + extend Importer + + attr_reader :type_alias + private :type_alias + + # Creates an array of handlers for the given +libs+, can be an instance of + # Fiddle::Handle, Fiddle::Importer, or will create a new instance of + # Fiddle::Handle using Fiddle.dlopen + # + # Raises a DLError if the library cannot be loaded. + # + # See Fiddle.dlopen + def dlload(*libs) + handles = libs.collect{|lib| + case lib + when nil + nil + when Handle + lib + when Importer + lib.handlers + else + Fiddle.dlopen(lib) + end + }.flatten() + @handler = CompositeHandler.new(handles) + @func_map = {} + @type_alias = {} + end + + # Sets the type alias for +alias_type+ as +orig_type+ + def typealias(alias_type, orig_type) + @type_alias[alias_type] = orig_type + end + + # Returns the sizeof +ty+, using Fiddle::Importer.parse_ctype to determine + # the C type and the appropriate Fiddle constant. + def sizeof(ty) + case ty + when String + ty = parse_ctype(ty, type_alias).abs() + case ty + when TYPE_CHAR + return SIZEOF_CHAR + when TYPE_SHORT + return SIZEOF_SHORT + when TYPE_INT + return SIZEOF_INT + when TYPE_LONG + return SIZEOF_LONG + when TYPE_FLOAT + return SIZEOF_FLOAT + when TYPE_DOUBLE + return SIZEOF_DOUBLE + when TYPE_VOIDP + return SIZEOF_VOIDP + when TYPE_CONST_STRING + return SIZEOF_CONST_STRING + else + if defined?(TYPE_LONG_LONG) and + ty == TYPE_LONG_LONG + return SIZEOF_LONG_LONG + else + raise(DLError, "unknown type: #{ty}") + end + end + when Class + if( ty.instance_methods().include?(:to_ptr) ) + return ty.size() + end + end + return Pointer[ty].size() + end + + def parse_bind_options(opts) + h = {} + while( opt = opts.shift() ) + case opt + when :stdcall, :cdecl + h[:call_type] = opt + when :carried, :temp, :temporal, :bind + h[:callback_type] = opt + h[:carrier] = opts.shift() + else + h[opt] = true + end + end + h + end + private :parse_bind_options + + # :stopdoc: + CALL_TYPE_TO_ABI = Hash.new { |h, k| + raise RuntimeError, "unsupported call type: #{k}" + }.merge({ :stdcall => Function.const_defined?(:STDCALL) ? Function::STDCALL : + Function::DEFAULT, + :cdecl => Function::DEFAULT, + nil => Function::DEFAULT + }).freeze + private_constant :CALL_TYPE_TO_ABI + # :startdoc: + + # Creates a global method from the given C +signature+. + def extern(signature, *opts) + symname, ctype, argtype = parse_signature(signature, type_alias) + opt = parse_bind_options(opts) + f = import_function(symname, ctype, argtype, opt[:call_type]) + name = symname.gsub(/@.+/,'') + @func_map[name] = f + # define_method(name){|*args,&block| f.call(*args,&block)} + begin + /^(.+?):(\d+)/ =~ caller.first + file, line = $1, $2.to_i + rescue + file, line = __FILE__, __LINE__+3 + end + module_eval(<<-EOS, file, line) + def #{name}(*args, &block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + # Creates a global method from the given C +signature+ using the given + # +opts+ as bind parameters with the given block. + def bind(signature, *opts, &blk) + name, ctype, argtype = parse_signature(signature, type_alias) + h = parse_bind_options(opts) + case h[:callback_type] + when :bind, nil + f = bind_function(name, ctype, argtype, h[:call_type], &blk) + else + raise(RuntimeError, "unknown callback type: #{h[:callback_type]}") + end + @func_map[name] = f + #define_method(name){|*args,&block| f.call(*args,&block)} + begin + /^(.+?):(\d+)/ =~ caller.first + file, line = $1, $2.to_i + rescue + file, line = __FILE__, __LINE__+3 + end + module_eval(<<-EOS, file, line) + def #{name}(*args,&block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + # Creates a class to wrap the C struct described by +signature+. + # + # MyStruct = struct ['int i', 'char c'] + def struct(signature) + tys, mems = parse_struct_signature(signature, type_alias) + Fiddle::CStructBuilder.create(CStruct, tys, mems) + end + + # Creates a class to wrap the C union described by +signature+. + # + # MyUnion = union ['int i', 'char c'] + def union(signature) + tys, mems = parse_struct_signature(signature, type_alias) + Fiddle::CStructBuilder.create(CUnion, tys, mems) + end + + # Returns the function mapped to +name+, that was created by either + # Fiddle::Importer.extern or Fiddle::Importer.bind + def [](name) + @func_map[name] + end + + # Creates a class to wrap the C struct with the value +ty+ + # + # See also Fiddle::Importer.struct + def create_value(ty, val=nil) + s = struct([ty + " value"]) + ptr = s.malloc() + if( val ) + ptr.value = val + end + return ptr + end + alias value create_value + + # Returns a new instance of the C struct with the value +ty+ at the +addr+ + # address. + def import_value(ty, addr) + s = struct([ty + " value"]) + ptr = s.new(addr) + return ptr + end + + + # The Fiddle::CompositeHandler instance + # + # Will raise an error if no handlers are open. + def handler + (@handler ||= nil) or raise "call dlload before importing symbols and functions" + end + + # Returns a new Fiddle::Pointer instance at the memory address of the given + # +name+ symbol. + # + # Raises a DLError if the +name+ doesn't exist. + # + # See Fiddle::CompositeHandler.sym and Fiddle::Handle.sym + def import_symbol(name) + addr = handler.sym(name) + if( !addr ) + raise(DLError, "cannot find the symbol: #{name}") + end + Pointer.new(addr) + end + + # Returns a new Fiddle::Function instance at the memory address of the given + # +name+ function. + # + # Raises a DLError if the +name+ doesn't exist. + # + # * +argtype+ is an Array of arguments, passed to the +name+ function. + # * +ctype+ is the return type of the function + # * +call_type+ is the ABI of the function + # + # See also Fiddle:Function.new + # + # See Fiddle::CompositeHandler.sym and Fiddle::Handler.sym + def import_function(name, ctype, argtype, call_type = nil) + addr = handler.sym(name) + if( !addr ) + raise(DLError, "cannot find the function: #{name}()") + end + Function.new(addr, argtype, ctype, CALL_TYPE_TO_ABI[call_type], + name: name) + end + + # Returns a new closure wrapper for the +name+ function. + # + # * +ctype+ is the return type of the function + # * +argtype+ is an Array of arguments, passed to the callback function + # * +call_type+ is the abi of the closure + # * +block+ is passed to the callback + # + # See Fiddle::Closure + def bind_function(name, ctype, argtype, call_type = nil, &block) + abi = CALL_TYPE_TO_ABI[call_type] + closure = Class.new(Fiddle::Closure) { + define_method(:call, block) + }.new(ctype, argtype, abi) + + Function.new(closure, argtype, ctype, abi, name: name) + end + end +end diff --git a/ext/fiddle/lib/fiddle/pack.rb b/ext/fiddle/lib/fiddle/pack.rb new file mode 100644 index 0000000000..22eccedb76 --- /dev/null +++ b/ext/fiddle/lib/fiddle/pack.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true +require 'fiddle' + +module Fiddle + module PackInfo # :nodoc: all + ALIGN_MAP = { + TYPE_VOIDP => ALIGN_VOIDP, + TYPE_CHAR => ALIGN_CHAR, + TYPE_SHORT => ALIGN_SHORT, + TYPE_INT => ALIGN_INT, + TYPE_LONG => ALIGN_LONG, + TYPE_FLOAT => ALIGN_FLOAT, + TYPE_DOUBLE => ALIGN_DOUBLE, + -TYPE_CHAR => ALIGN_CHAR, + -TYPE_SHORT => ALIGN_SHORT, + -TYPE_INT => ALIGN_INT, + -TYPE_LONG => ALIGN_LONG, + } + + PACK_MAP = { + TYPE_VOIDP => "l!", + TYPE_CHAR => "c", + TYPE_SHORT => "s!", + TYPE_INT => "i!", + TYPE_LONG => "l!", + TYPE_FLOAT => "f", + TYPE_DOUBLE => "d", + -TYPE_CHAR => "c", + -TYPE_SHORT => "s!", + -TYPE_INT => "i!", + -TYPE_LONG => "l!", + } + + SIZE_MAP = { + TYPE_VOIDP => SIZEOF_VOIDP, + TYPE_CHAR => SIZEOF_CHAR, + TYPE_SHORT => SIZEOF_SHORT, + TYPE_INT => SIZEOF_INT, + TYPE_LONG => SIZEOF_LONG, + TYPE_FLOAT => SIZEOF_FLOAT, + TYPE_DOUBLE => SIZEOF_DOUBLE, + -TYPE_CHAR => SIZEOF_CHAR, + -TYPE_SHORT => SIZEOF_SHORT, + -TYPE_INT => SIZEOF_INT, + -TYPE_LONG => SIZEOF_LONG, + } + if defined?(TYPE_LONG_LONG) + ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG + PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q" + SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG + PACK_MAP[TYPE_VOIDP] = "q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP + end + + def align(addr, align) + d = addr % align + if( d == 0 ) + addr + else + addr + (align - d) + end + end + module_function :align + end + + class Packer # :nodoc: all + include PackInfo + + def self.[](*types) + new(types) + end + + def initialize(types) + parse_types(types) + end + + def size() + @size + end + + def pack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.pack(@template) + else + if defined?(TYPE_LONG_LONG) and + SIZEOF_VOIDP == SIZEOF_LONG_LONG + ary.pack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + end + + def unpack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.join().unpack(@template) + else + if defined?(TYPE_LONG_LONG) and + SIZEOF_VOIDP == SIZEOF_LONG_LONG + ary.join().unpack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + end + + private + + def parse_types(types) + @template = "".dup + addr = 0 + types.each{|t| + orig_addr = addr + if( t.is_a?(Array) ) + addr = align(orig_addr, ALIGN_MAP[TYPE_VOIDP]) + else + addr = align(orig_addr, ALIGN_MAP[t]) + end + d = addr - orig_addr + if( d > 0 ) + @template << "x#{d}" + end + if( t.is_a?(Array) ) + @template << (PACK_MAP[t[0]] * t[1]) + addr += (SIZE_MAP[t[0]] * t[1]) + else + @template << PACK_MAP[t] + addr += SIZE_MAP[t] + end + } + addr = align(addr, ALIGN_MAP[TYPE_VOIDP]) + @size = addr + end + end +end diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb new file mode 100644 index 0000000000..6d05bbd742 --- /dev/null +++ b/ext/fiddle/lib/fiddle/struct.rb @@ -0,0 +1,539 @@ +# frozen_string_literal: true +require 'fiddle' +require 'fiddle/value' +require 'fiddle/pack' + +module Fiddle + # A base class for objects representing a C structure + class CStruct + include Enumerable + + # accessor to Fiddle::CStructEntity + def CStruct.entity_class + CStructEntity + end + + def self.offsetof(name, members, types) # :nodoc: + offset = 0 + worklist = name.split('.') + this_type = self + while search_name = worklist.shift + index = 0 + member_index = members.index(search_name) + + unless member_index + # Possibly a sub-structure + member_index = members.index { |member_name, _| + member_name == search_name + } + return unless member_index + end + + types.each { |type, count = 1| + orig_offset = offset + if type.respond_to?(:entity_class) + align = type.alignment + type_size = type.size + else + align = PackInfo::ALIGN_MAP[type] + type_size = PackInfo::SIZE_MAP[type] + end + + # Unions shouldn't advance the offset + if this_type.entity_class == CUnionEntity + type_size = 0 + end + + offset = PackInfo.align(orig_offset, align) + + if worklist.empty? + return offset if index == member_index + else + if index == member_index + subtype = types[member_index] + members = subtype.members + types = subtype.types + this_type = subtype + break + end + end + + offset += (type_size * count) + index += 1 + } + end + nil + end + + def each + return enum_for(__function__) unless block_given? + + self.class.members.each do |name,| + yield(self[name]) + end + end + + def each_pair + return enum_for(__function__) unless block_given? + + self.class.members.each do |name,| + yield(name, self[name]) + end + end + + def to_h + hash = {} + each_pair do |name, value| + hash[name] = unstruct(value) + end + hash + end + + def replace(another) + if another.nil? + self.class.members.each do |name,| + self[name] = nil + end + elsif another.respond_to?(:each_pair) + another.each_pair do |name, value| + self[name] = value + end + else + another.each do |name, value| + self[name] = value + end + end + self + end + + private + def unstruct(value) + case value + when CStruct + value.to_h + when Array + value.collect do |v| + unstruct(v) + end + else + value + end + end + end + + # A base class for objects representing a C union + class CUnion + # accessor to Fiddle::CUnionEntity + def CUnion.entity_class + CUnionEntity + end + + def self.offsetof(name, members, types) # :nodoc: + 0 + end + end + + # Wrapper for arrays within a struct + class StructArray < Array + include ValueUtil + + def initialize(ptr, type, initial_values) + @ptr = ptr + @type = type + @is_struct = @type.respond_to?(:entity_class) + if @is_struct + super(initial_values) + else + @size = Fiddle::PackInfo::SIZE_MAP[type] + @pack_format = Fiddle::PackInfo::PACK_MAP[type] + super(initial_values.collect { |v| unsigned_value(v, type) }) + end + end + + def to_ptr + @ptr + end + + def []=(index, value) + if index < 0 || index >= size + raise IndexError, 'index %d outside of array bounds 0...%d' % [index, size] + end + + if @is_struct + self[index].replace(value) + else + to_ptr[index * @size, @size] = [value].pack(@pack_format) + super(index, value) + end + end + end + + # Used to construct C classes (CUnion, CStruct, etc) + # + # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an + # easy-to-use manner. + module CStructBuilder + # Construct a new class given a C: + # * class +klass+ (CUnion, CStruct, or other that provide an + # #entity_class) + # * +types+ (Fiddle::TYPE_INT, Fiddle::TYPE_SIZE_T, etc., see the C types + # constants) + # * corresponding +members+ + # + # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an + # easy-to-use manner. + # + # Examples: + # + # require 'fiddle/struct' + # require 'fiddle/cparser' + # + # include Fiddle::CParser + # + # types, members = parse_struct_signature(['int i','char c']) + # + # MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members) + # + # MyStruct.malloc(Fiddle::RUBY_FREE) do |obj| + # ... + # end + # + # obj = MyStruct.malloc(Fiddle::RUBY_FREE) + # begin + # ... + # ensure + # obj.call_free + # end + # + # obj = MyStruct.malloc + # begin + # ... + # ensure + # Fiddle.free obj.to_ptr + # end + # + def create(klass, types, members) + new_class = Class.new(klass){ + define_method(:initialize){|addr, func = nil| + if addr.is_a?(self.class.entity_class) + @entity = addr + else + @entity = self.class.entity_class.new(addr, types, func) + end + @entity.assign_names(members) + } + define_method(:[]) { |*args| @entity.send(:[], *args) } + define_method(:[]=) { |*args| @entity.send(:[]=, *args) } + define_method(:to_ptr){ @entity } + define_method(:to_i){ @entity.to_i } + define_singleton_method(:types) { types } + define_singleton_method(:members) { members } + + # Return the offset of a struct member given its name. + # For example: + # + # MyStruct = struct [ + # "int64_t i", + # "char c", + # ] + # + # MyStruct.offsetof("i") # => 0 + # MyStruct.offsetof("c") # => 8 + # + define_singleton_method(:offsetof) { |name| + klass.offsetof(name, members, types) + } + members.each{|name| + name = name[0] if name.is_a?(Array) # name is a nested struct + next if method_defined?(name) + define_method(name){ @entity[name] } + define_method(name + "="){|val| @entity[name] = val } + } + entity_class = klass.entity_class + alignment = entity_class.alignment(types) + size = entity_class.size(types) + define_singleton_method(:alignment) { alignment } + define_singleton_method(:size) { size } + define_singleton_method(:malloc) do |func=nil, &block| + if block + entity_class.malloc(types, func, size) do |entity| + block.call(new(entity)) + end + else + new(entity_class.malloc(types, func, size)) + end + end + } + return new_class + end + module_function :create + end + + # A pointer to a C structure + class CStructEntity < Fiddle::Pointer + include PackInfo + include ValueUtil + + def CStructEntity.alignment(types) + max = 1 + types.each do |type, count = 1| + if type.respond_to?(:entity_class) + n = type.alignment + else + n = ALIGN_MAP[type] + end + max = n if n > max + end + max + end + + # Allocates a C struct with the +types+ provided. + # + # See Fiddle::Pointer.malloc for memory management issues. + def CStructEntity.malloc(types, func = nil, size = size(types), &block) + if block_given? + super(size, func) do |struct| + struct.set_ctypes types + yield struct + end + else + struct = super(size, func) + struct.set_ctypes types + struct + end + end + + # Returns the offset for the packed sizes for the given +types+. + # + # Fiddle::CStructEntity.size( + # [ Fiddle::TYPE_DOUBLE, + # Fiddle::TYPE_INT, + # Fiddle::TYPE_CHAR, + # Fiddle::TYPE_VOIDP ]) #=> 24 + def CStructEntity.size(types) + offset = 0 + + max_align = types.map { |type, count = 1| + last_offset = offset + + if type.respond_to?(:entity_class) + align = type.alignment + type_size = type.size + else + align = PackInfo::ALIGN_MAP[type] + type_size = PackInfo::SIZE_MAP[type] + end + offset = PackInfo.align(last_offset, align) + + (type_size * count) + + align + }.max + + PackInfo.align(offset, max_align) + end + + # Wraps the C pointer +addr+ as a C struct with the given +types+. + # + # When the instance is garbage collected, the C function +func+ is called. + # + # See also Fiddle::Pointer.new + def initialize(addr, types, func = nil) + if func && addr.is_a?(Pointer) && addr.free + raise ArgumentError, 'free function specified on both underlying struct Pointer and when creating a CStructEntity - who do you want to free this?' + end + set_ctypes(types) + super(addr, @size, func) + end + + # Set the names of the +members+ in this C struct + def assign_names(members) + @members = [] + @nested_structs = {} + members.each_with_index do |member, index| + if member.is_a?(Array) # nested struct + member_name = member[0] + struct_type, struct_count = @ctypes[index] + if struct_count.nil? + struct = struct_type.new(to_i + @offset[index]) + else + structs = struct_count.times.map do |i| + struct_type.new(to_i + @offset[index] + i * struct_type.size) + end + struct = StructArray.new(to_i + @offset[index], + struct_type, + structs) + end + @nested_structs[member_name] = struct + else + member_name = member + end + @members << member_name + end + end + + # Calculates the offsets and sizes for the given +types+ in the struct. + def set_ctypes(types) + @ctypes = types + @offset = [] + offset = 0 + + max_align = types.map { |type, count = 1| + orig_offset = offset + if type.respond_to?(:entity_class) + align = type.alignment + type_size = type.size + else + align = ALIGN_MAP[type] + type_size = SIZE_MAP[type] + end + offset = PackInfo.align(orig_offset, align) + + @offset << offset + + offset += (type_size * count) + + align + }.max + + @size = PackInfo.align(offset, max_align) + end + + # Fetch struct member +name+ if only one argument is specified. If two + # arguments are specified, the first is an offset and the second is a + # length and this method returns the string of +length+ bytes beginning at + # +offset+. + # + # Examples: + # + # my_struct = struct(['int id']).malloc + # my_struct.id = 1 + # my_struct['id'] # => 1 + # my_struct[0, 4] # => "\x01\x00\x00\x00".b + # + def [](*args) + return super(*args) if args.size > 1 + name = args[0] + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + if( ty.is_a?(Array) ) + if ty.first.respond_to?(:entity_class) + return @nested_structs[name] + else + r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) + end + elsif ty.respond_to?(:entity_class) + return @nested_structs[name] + else + r = super(@offset[idx], SIZE_MAP[ty.abs]) + end + packer = Packer.new([ty]) + val = packer.unpack([r]) + case ty + when Array + case ty[0] + when TYPE_VOIDP + val = val.collect{|v| Pointer.new(v)} + end + when TYPE_VOIDP + val = Pointer.new(val[0]) + else + val = val[0] + end + if( ty.is_a?(Integer) && (ty < 0) ) + return unsigned_value(val, ty) + elsif( ty.is_a?(Array) && (ty[0] < 0) ) + return StructArray.new(self + @offset[idx], ty[0], val) + else + return val + end + end + + # Set struct member +name+, to value +val+. If more arguments are + # specified, writes the string of bytes to the memory at the given + # +offset+ and +length+. + # + # Examples: + # + # my_struct = struct(['int id']).malloc + # my_struct['id'] = 1 + # my_struct[0, 4] = "\x01\x00\x00\x00".b + # my_struct.id # => 1 + # + def []=(*args) + return super(*args) if args.size > 2 + name, val = *args + name = name.to_s if name.is_a?(Symbol) + nested_struct = @nested_structs[name] + if nested_struct + if nested_struct.is_a?(StructArray) + if val.nil? + nested_struct.each do |s| + s.replace(nil) + end + else + val.each_with_index do |v, i| + nested_struct[i] = v + end + end + else + nested_struct.replace(val) + end + return val + end + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + packer = Packer.new([ty]) + val = wrap_arg(val, ty, []) + buff = packer.pack([val].flatten()) + super(@offset[idx], buff.size, buff) + if( ty.is_a?(Integer) && (ty < 0) ) + return unsigned_value(val, ty) + elsif( ty.is_a?(Array) && (ty[0] < 0) ) + return val.collect{|v| unsigned_value(v,ty[0])} + else + return val + end + end + + undef_method :size= + def to_s() # :nodoc: + super(@size) + end + end + + # A pointer to a C union + class CUnionEntity < CStructEntity + include PackInfo + + # Returns the size needed for the union with the given +types+. + # + # Fiddle::CUnionEntity.size( + # [ Fiddle::TYPE_DOUBLE, + # Fiddle::TYPE_INT, + # Fiddle::TYPE_CHAR, + # Fiddle::TYPE_VOIDP ]) #=> 8 + def CUnionEntity.size(types) + types.map { |type, count = 1| + if type.respond_to?(:entity_class) + type.size * count + else + PackInfo::SIZE_MAP[type] * count + end + }.max + end + + # Calculate the necessary offset and for each union member with the given + # +types+ + def set_ctypes(types) + @ctypes = types + @offset = Array.new(types.length, 0) + @size = self.class.size types + end + end +end diff --git a/ext/fiddle/lib/fiddle/types.rb b/ext/fiddle/lib/fiddle/types.rb new file mode 100644 index 0000000000..7baf31ec9e --- /dev/null +++ b/ext/fiddle/lib/fiddle/types.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true +module Fiddle + # Adds Windows type aliases to the including class for use with + # Fiddle::Importer. + # + # The aliases added are: + # * ATOM + # * BOOL + # * BYTE + # * DWORD + # * DWORD32 + # * DWORD64 + # * HANDLE + # * HDC + # * HINSTANCE + # * HWND + # * LPCSTR + # * LPSTR + # * PBYTE + # * PDWORD + # * PHANDLE + # * PVOID + # * PWORD + # * UCHAR + # * UINT + # * ULONG + # * WORD + module Win32Types + def included(m) # :nodoc: + # https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types + m.module_eval{ + typealias "ATOM", "WORD" + typealias "BOOL", "int" + typealias "BYTE", "unsigned char" + typealias "DWORD", "unsigned long" + typealias "DWORD32", "uint32_t" + typealias "DWORD64", "uint64_t" + typealias "HANDLE", "PVOID" + typealias "HDC", "HANDLE" + typealias "HINSTANCE", "HANDLE" + typealias "HWND", "HANDLE" + typealias "LPCSTR", "const char *" + typealias "LPSTR", "char *" + typealias "PBYTE", "BYTE *" + typealias "PDWORD", "DWORD *" + typealias "PHANDLE", "HANDLE *" + typealias "PVOID", "void *" + typealias "PWORD", "WORD *" + typealias "UCHAR", "unsigned char" + typealias "UINT", "unsigned int" + typealias "ULONG", "unsigned long" + typealias "WORD", "unsigned short" + } + end + module_function :included + end + + # Adds basic type aliases to the including class for use with Fiddle::Importer. + # + # The aliases added are +uint+ and +u_int+ (<tt>unsigned int</tt>) and + # +ulong+ and +u_long+ (<tt>unsigned long</tt>) + module BasicTypes + def included(m) # :nodoc: + m.module_eval{ + typealias "uint", "unsigned int" + typealias "u_int", "unsigned int" + typealias "ulong", "unsigned long" + typealias "u_long", "unsigned long" + } + end + module_function :included + end +end diff --git a/ext/fiddle/lib/fiddle/value.rb b/ext/fiddle/lib/fiddle/value.rb new file mode 100644 index 0000000000..01fec1c206 --- /dev/null +++ b/ext/fiddle/lib/fiddle/value.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true +require 'fiddle' + +module Fiddle + module ValueUtil #:nodoc: all + def unsigned_value(val, ty) + case ty.abs + when TYPE_CHAR + [val].pack("c").unpack("C")[0] + when TYPE_SHORT + [val].pack("s!").unpack("S!")[0] + when TYPE_INT + [val].pack("i!").unpack("I!")[0] + when TYPE_LONG + [val].pack("l!").unpack("L!")[0] + else + if defined?(TYPE_LONG_LONG) and + ty.abs == TYPE_LONG_LONG + [val].pack("q").unpack("Q")[0] + else + val + end + end + end + + def signed_value(val, ty) + case ty.abs + when TYPE_CHAR + [val].pack("C").unpack("c")[0] + when TYPE_SHORT + [val].pack("S!").unpack("s!")[0] + when TYPE_INT + [val].pack("I!").unpack("i!")[0] + when TYPE_LONG + [val].pack("L!").unpack("l!")[0] + else + if defined?(TYPE_LONG_LONG) and + ty.abs == TYPE_LONG_LONG + [val].pack("Q").unpack("q")[0] + else + val + end + end + end + + def wrap_args(args, tys, funcs, &block) + result = [] + tys ||= [] + args.each_with_index{|arg, idx| + result.push(wrap_arg(arg, tys[idx], funcs, &block)) + } + result + end + + def wrap_arg(arg, ty, funcs = [], &block) + funcs ||= [] + case arg + when nil + return 0 + when Pointer + return arg.to_i + when IO + case ty + when TYPE_VOIDP + return Pointer[arg].to_i + else + return arg.to_i + end + when Function + if( block ) + arg.bind_at_call(&block) + funcs.push(arg) + elsif !arg.bound? + raise(RuntimeError, "block must be given.") + end + return arg.to_i + when String + if( ty.is_a?(Array) ) + return arg.unpack('C*') + else + case SIZEOF_VOIDP + when SIZEOF_LONG + return [arg].pack("p").unpack("l!")[0] + else + if defined?(SIZEOF_LONG_LONG) and + SIZEOF_VOIDP == SIZEOF_LONG_LONG + return [arg].pack("p").unpack("q")[0] + else + raise(RuntimeError, "sizeof(void*)?") + end + end + end + when Float, Integer + return arg + when Array + if( ty.is_a?(Array) ) # used only by struct + case ty[0] + when TYPE_VOIDP + return arg.collect{|v| Integer(v)} + when TYPE_CHAR + if( arg.is_a?(String) ) + return val.unpack('C*') + end + end + return arg + else + return arg + end + else + if( arg.respond_to?(:to_ptr) ) + return arg.to_ptr.to_i + else + begin + return Integer(arg) + rescue + raise(ArgumentError, "unknown argument type: #{arg.class}") + end + end + end + end + end +end diff --git a/ext/fiddle/lib/fiddle/version.rb b/ext/fiddle/lib/fiddle/version.rb new file mode 100644 index 0000000000..db6504b650 --- /dev/null +++ b/ext/fiddle/lib/fiddle/version.rb @@ -0,0 +1,3 @@ +module Fiddle + VERSION = "1.1.0" +end diff --git a/ext/fiddle/memory_view.c b/ext/fiddle/memory_view.c new file mode 100644 index 0000000000..fa66fc2c7b --- /dev/null +++ b/ext/fiddle/memory_view.c @@ -0,0 +1,321 @@ +#include <fiddle.h> + +#ifdef HAVE_RUBY_MEMORY_VIEW_H + +#include <stdbool.h> +#include <ruby/ruby.h> +#include <ruby/encoding.h> +#include <ruby/memory_view.h> + +#if SIZEOF_INTPTR_T == SIZEOF_LONG_LONG +# define INTPTR2NUM LL2NUM +# define UINTPTR2NUM ULL2NUM +#elif SIZEOF_INTPTR_T == SIZEOF_LONG +# define INTPTR2NUM LONG2NUM +# define UINTPTR2NUM ULONG2NUM +#else +# define INTPTR2NUM INT2NUM +# define UINTPTR2NUM UINT2NUM +#endif + +VALUE rb_cMemoryView = Qnil; + +struct memview_data { + rb_memory_view_t view; + rb_memory_view_item_component_t *members; + size_t n_members; +}; + +static void +fiddle_memview_mark(void *ptr) +{ + const struct memview_data *data = ptr; + rb_gc_mark(data->view.obj); +} + +static void +fiddle_memview_release(struct memview_data *data) +{ + if (NIL_P(data->view.obj)) return; + + rb_memory_view_release(&data->view); + data->view.obj = Qnil; + data->view.byte_size = 0; + if (data->members) { + xfree(data->members); + data->members = NULL; + data->n_members = 0; + } +} + +static void +fiddle_memview_free(void *ptr) +{ + struct memview_data *data = ptr; + fiddle_memview_release(data); + xfree(ptr); +} + +static size_t +fiddle_memview_memsize(const void *ptr) +{ + const struct memview_data *data = ptr; + return sizeof(*data) + sizeof(rb_memory_view_item_component_t)*data->n_members + (size_t)data->view.byte_size; +} + +static const rb_data_type_t fiddle_memview_data_type = { + "fiddle/memory_view", + {fiddle_memview_mark, fiddle_memview_free, fiddle_memview_memsize,}, +}; + +static VALUE +rb_fiddle_memview_s_allocate(VALUE klass) +{ + struct memview_data *data; + VALUE obj = TypedData_Make_Struct(klass, struct memview_data, &fiddle_memview_data_type, data); + data->view.obj = Qnil; + data->view.byte_size = 0; + data->members = NULL; + data->n_members = 0; + return obj; +} + +static VALUE +rb_fiddle_memview_release(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + fiddle_memview_release(data); + return Qnil; +} + +static VALUE +rb_fiddle_memview_s_export(VALUE klass, VALUE target) +{ + ID id_new; + CONST_ID(id_new, "new"); + VALUE memview = rb_funcall(klass, id_new, 1, target); + return rb_ensure(rb_yield, memview, rb_fiddle_memview_release, memview); +} + +static VALUE +rb_fiddle_memview_initialize(VALUE obj, VALUE target) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (!rb_memory_view_get(target, &data->view, 0)) { + data->view.obj = Qnil; + rb_raise(rb_eArgError, "Unable to get a memory view from %+"PRIsVALUE, target); + } + + return Qnil; +} + +static VALUE +rb_fiddle_memview_get_obj(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + return data->view.obj; +} + +static VALUE +rb_fiddle_memview_get_byte_size(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + return SSIZET2NUM(data->view.byte_size); +} + +static VALUE +rb_fiddle_memview_get_readonly(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + return data->view.readonly ? Qtrue : Qfalse; +} + +static VALUE +rb_fiddle_memview_get_format(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + return data->view.format == NULL ? Qnil : rb_str_new_cstr(data->view.format); +} + +static VALUE +rb_fiddle_memview_get_item_size(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + return SSIZET2NUM(data->view.item_size); +} + +static VALUE +rb_fiddle_memview_get_ndim(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + return SSIZET2NUM(data->view.ndim); +} + +static VALUE +rb_fiddle_memview_get_shape(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + if (data->view.shape == NULL) return Qnil; + + const ssize_t ndim = data->view.ndim; + VALUE shape = rb_ary_new_capa(ndim); + ssize_t i; + for (i = 0; i < ndim; ++i) { + rb_ary_push(shape, SSIZET2NUM(data->view.shape[i])); + } + return shape; +} + +static VALUE +rb_fiddle_memview_get_strides(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + if (data->view.strides == NULL) return Qnil; + + const ssize_t ndim = data->view.ndim; + VALUE strides = rb_ary_new_capa(ndim); + ssize_t i; + for (i = 0; i < ndim; ++i) { + rb_ary_push(strides, SSIZET2NUM(data->view.strides[i])); + } + return strides; +} + +static VALUE +rb_fiddle_memview_get_sub_offsets(VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + if (data->view.sub_offsets == NULL) return Qnil; + + const ssize_t ndim = data->view.ndim; + VALUE sub_offsets = rb_ary_new_capa(ndim); + ssize_t i; + for (i = 0; i < ndim; ++i) { + rb_ary_push(sub_offsets, SSIZET2NUM(data->view.sub_offsets[i])); + } + return sub_offsets; +} + +static VALUE +rb_fiddle_memview_aref(int argc, VALUE *argv, VALUE obj) +{ + struct memview_data *data; + TypedData_Get_Struct(obj, struct memview_data, &fiddle_memview_data_type, data); + + if (NIL_P(data->view.obj)) return Qnil; + + const ssize_t ndim = data->view.ndim; + if (argc != ndim) { + rb_raise(rb_eIndexError, "wrong number of index (%d for %"PRIdSIZE")", argc, ndim); + } + + VALUE indices_v = 0; + ssize_t *indices = ALLOCV_N(ssize_t, indices_v, ndim); + + ssize_t i; + for (i = 0; i < ndim; ++i) { + ssize_t x = NUM2SSIZET(argv[i]); + indices[i] = x; + } + + uint8_t *ptr = rb_memory_view_get_item_pointer(&data->view, indices); + ALLOCV_END(indices_v); + + if (data->view.format == NULL) { + return INT2FIX(*ptr); + } + + if (!data->members) { + const char *err; + if (rb_memory_view_parse_item_format(data->view.format, &data->members, &data->n_members, &err) < 0) { + rb_raise(rb_eRuntimeError, "Unable to recognize item format at %"PRIdSIZE" in \"%s\"", + err - data->view.format, data->view.format); + } + } + + return rb_memory_view_extract_item_members(ptr, data->members, data->n_members); +} + +static VALUE +rb_fiddle_memview_to_s(VALUE self) +{ + struct memview_data *data; + const char *raw_data; + long byte_size; + VALUE string; + + TypedData_Get_Struct(self, + struct memview_data, + &fiddle_memview_data_type, + data); + + if (NIL_P(data->view.obj)) { + raw_data = NULL; + byte_size = 0; + } else { + raw_data = data->view.data; + byte_size = data->view.byte_size; + } + + string = rb_enc_str_new_static(raw_data, byte_size, rb_ascii8bit_encoding()); + { + ID id_memory_view; + CONST_ID(id_memory_view, "memory_view"); + rb_ivar_set(string, id_memory_view, self); + } + return rb_obj_freeze(string); +} + +void +Init_fiddle_memory_view(void) +{ + rb_cMemoryView = rb_define_class_under(mFiddle, "MemoryView", rb_cObject); + rb_define_alloc_func(rb_cMemoryView, rb_fiddle_memview_s_allocate); + rb_define_singleton_method(rb_cMemoryView, "export", rb_fiddle_memview_s_export, 1); + rb_define_method(rb_cMemoryView, "initialize", rb_fiddle_memview_initialize, 1); + rb_define_method(rb_cMemoryView, "release", rb_fiddle_memview_release, 0); + rb_define_method(rb_cMemoryView, "obj", rb_fiddle_memview_get_obj, 0); + rb_define_method(rb_cMemoryView, "byte_size", rb_fiddle_memview_get_byte_size, 0); + rb_define_method(rb_cMemoryView, "readonly?", rb_fiddle_memview_get_readonly, 0); + rb_define_method(rb_cMemoryView, "format", rb_fiddle_memview_get_format, 0); + rb_define_method(rb_cMemoryView, "item_size", rb_fiddle_memview_get_item_size, 0); + rb_define_method(rb_cMemoryView, "ndim", rb_fiddle_memview_get_ndim, 0); + rb_define_method(rb_cMemoryView, "shape", rb_fiddle_memview_get_shape, 0); + rb_define_method(rb_cMemoryView, "strides", rb_fiddle_memview_get_strides, 0); + rb_define_method(rb_cMemoryView, "sub_offsets", rb_fiddle_memview_get_sub_offsets, 0); + rb_define_method(rb_cMemoryView, "[]", rb_fiddle_memview_aref, -1); + rb_define_method(rb_cMemoryView, "to_s", rb_fiddle_memview_to_s, 0); +} + +#endif /* HAVE_RUBY_MEMORY_VIEW_H */ diff --git a/ext/fiddle/pinned.c b/ext/fiddle/pinned.c new file mode 100644 index 0000000000..019a3020e2 --- /dev/null +++ b/ext/fiddle/pinned.c @@ -0,0 +1,123 @@ +#include <fiddle.h> + +VALUE rb_cPinned; +VALUE rb_eFiddleClearedReferenceError; + +struct pinned_data { + VALUE ptr; +}; + +static void +pinned_mark(void *ptr) +{ + struct pinned_data *data = (struct pinned_data*)ptr; + /* Ensure reference is pinned */ + if (data->ptr) { + rb_gc_mark(data->ptr); + } +} + +static size_t +pinned_memsize(const void *ptr) +{ + return sizeof(struct pinned_data); +} + +static const rb_data_type_t pinned_data_type = { + "fiddle/pinned", + {pinned_mark, xfree, pinned_memsize, }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED +}; + +static VALUE +allocate(VALUE klass) +{ + struct pinned_data *data; + VALUE obj = TypedData_Make_Struct(klass, struct pinned_data, &pinned_data_type, data); + data->ptr = 0; + return obj; +} + +/* + * call-seq: + * Fiddle::Pinned.new(object) => pinned_object + * + * Create a new pinned object reference. The Fiddle::Pinned instance will + * prevent the GC from moving +object+. + */ +static VALUE +initialize(VALUE self, VALUE ref) +{ + struct pinned_data *data; + TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data); + RB_OBJ_WRITE(self, &data->ptr, ref); + return self; +} + +/* + * call-seq: ref + * + * Return the object that this pinned instance references. + */ +static VALUE +ref(VALUE self) +{ + struct pinned_data *data; + TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data); + if (data->ptr) { + return data->ptr; + } else { + rb_raise(rb_eFiddleClearedReferenceError, "`ref` called on a cleared object"); + } +} + +/* + * call-seq: clear + * + * Clear the reference to the object this is pinning. + */ +static VALUE +clear(VALUE self) +{ + struct pinned_data *data; + TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data); + data->ptr = 0; + return self; +} + +/* + * call-seq: cleared? + * + * Returns true if the reference has been cleared, otherwise returns false. + */ +static VALUE +cleared_p(VALUE self) +{ + struct pinned_data *data; + TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data); + if (data->ptr) { + return Qfalse; + } else { + return Qtrue; + } +} + +extern VALUE rb_eFiddleError; + +void +Init_fiddle_pinned(void) +{ + rb_cPinned = rb_define_class_under(mFiddle, "Pinned", rb_cObject); + rb_define_alloc_func(rb_cPinned, allocate); + rb_define_method(rb_cPinned, "initialize", initialize, 1); + rb_define_method(rb_cPinned, "ref", ref, 0); + rb_define_method(rb_cPinned, "clear", clear, 0); + rb_define_method(rb_cPinned, "cleared?", cleared_p, 0); + + /* + * Document-class: Fiddle::ClearedReferenceError + * + * Cleared reference exception + */ + rb_eFiddleClearedReferenceError = rb_define_class_under(mFiddle, "ClearedReferenceError", rb_eFiddleError); +} diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c new file mode 100644 index 0000000000..15107e3862 --- /dev/null +++ b/ext/fiddle/pointer.c @@ -0,0 +1,853 @@ +/* -*- C -*- + * $Id$ + */ + +#include <stdbool.h> +#include <ruby/ruby.h> +#include <ruby/io.h> + +#include <ctype.h> +#include <fiddle.h> + +#ifdef HAVE_RUBY_MEMORY_VIEW_H +# include <ruby/memory_view.h> +#endif + +#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 rb_cPointer; + +typedef rb_fiddle_freefunc_t freefunc_t; + +struct ptr_data { + void *ptr; + long size; + freefunc_t free; + bool freed; + VALUE wrap[2]; +}; + +#define RPTR_DATA(obj) ((struct ptr_data *)(DATA_PTR(obj))) + +static inline freefunc_t +get_freefunc(VALUE func, volatile VALUE *wrap) +{ + VALUE addrnum; + if (NIL_P(func)) { + *wrap = 0; + return NULL; + } + addrnum = rb_Integer(func); + *wrap = (addrnum != func) ? func : 0; + return (freefunc_t)(VALUE)NUM2PTR(addrnum); +} + +static ID id_to_ptr; + +static void +fiddle_ptr_mark(void *ptr) +{ + struct ptr_data *data = ptr; + if (data->wrap[0]) { + rb_gc_mark(data->wrap[0]); + } + if (data->wrap[1]) { + rb_gc_mark(data->wrap[1]); + } +} + +static void +fiddle_ptr_free_ptr(void *ptr) +{ + struct ptr_data *data = ptr; + if (data->ptr && data->free && !data->freed) { + data->freed = true; + (*(data->free))(data->ptr); + } +} + +static void +fiddle_ptr_free(void *ptr) +{ + fiddle_ptr_free_ptr(ptr); + xfree(ptr); +} + +static size_t +fiddle_ptr_memsize(const void *ptr) +{ + const struct ptr_data *data = ptr; + return sizeof(*data) + data->size; +} + +static const rb_data_type_t fiddle_ptr_data_type = { + "fiddle/pointer", + {fiddle_ptr_mark, fiddle_ptr_free, fiddle_ptr_memsize,}, +}; + +#ifdef HAVE_RUBY_MEMORY_VIEW_H +static struct ptr_data * +fiddle_ptr_check_memory_view(VALUE obj) +{ + struct ptr_data *data; + TypedData_Get_Struct(obj, struct ptr_data, &fiddle_ptr_data_type, data); + if (data->ptr == NULL || data->size == 0) return NULL; + return data; +} + +static bool +fiddle_ptr_memory_view_available_p(VALUE obj) +{ + return fiddle_ptr_check_memory_view(obj) != NULL; +} + +static bool +fiddle_ptr_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags) +{ + struct ptr_data *data = fiddle_ptr_check_memory_view(obj); + rb_memory_view_init_as_byte_array(view, obj, data->ptr, data->size, true); + + return true; +} + +static const rb_memory_view_entry_t fiddle_ptr_memory_view_entry = { + fiddle_ptr_get_memory_view, + NULL, + fiddle_ptr_memory_view_available_p +}; +#endif + +static VALUE +rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func, VALUE wrap0, VALUE wrap1) +{ + struct ptr_data *data; + VALUE val; + + val = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data); + data->ptr = ptr; + data->free = func; + data->freed = false; + data->size = size; + data->wrap[0] = wrap0; + data->wrap[1] = wrap1; + + return val; +} + +VALUE +rb_fiddle_ptr_new_wrap(void *ptr, long size, freefunc_t func, VALUE wrap0, VALUE wrap1) +{ + return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func, wrap0, wrap1); +} + +static VALUE +rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func) +{ + return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func, 0, 0); +} + +static VALUE +rb_fiddle_ptr_malloc(VALUE klass, long size, freefunc_t func) +{ + void *ptr; + + ptr = ruby_xmalloc((size_t)size); + memset(ptr,0,(size_t)size); + return rb_fiddle_ptr_new2(klass, ptr, size, func, 0, 0); +} + +static void * +rb_fiddle_ptr2cptr(VALUE val) +{ + struct ptr_data *data; + void *ptr; + + if (rb_obj_is_kind_of(val, rb_cPointer)) { + TypedData_Get_Struct(val, struct ptr_data, &fiddle_ptr_data_type, data); + ptr = data->ptr; + } + else if (val == Qnil) { + ptr = NULL; + } + else{ + rb_raise(rb_eTypeError, "Fiddle::Pointer was expected"); + } + + return ptr; +} + +static VALUE +rb_fiddle_ptr_s_allocate(VALUE klass) +{ + VALUE obj; + struct ptr_data *data; + + obj = TypedData_Make_Struct(klass, struct ptr_data, &fiddle_ptr_data_type, data); + data->ptr = 0; + data->size = 0; + data->free = 0; + data->freed = false; + + return obj; +} + +/* + * call-seq: + * Fiddle::Pointer.new(address) => fiddle_cptr + * new(address, size) => fiddle_cptr + * new(address, size, freefunc) => fiddle_cptr + * + * Create a new pointer to +address+ with an optional +size+ and +freefunc+. + * + * +freefunc+ will be called when the instance is garbage collected. + */ +static VALUE +rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self) +{ + VALUE ptr, sym, size, wrap = 0, funcwrap = 0; + struct ptr_data *data; + void *p = NULL; + freefunc_t f = NULL; + long s = 0; + + if (rb_scan_args(argc, argv, "12", &ptr, &size, &sym) >= 1) { + VALUE addrnum = rb_Integer(ptr); + if (addrnum != ptr) wrap = ptr; + p = NUM2PTR(addrnum); + } + if (argc >= 2) { + s = NUM2LONG(size); + } + if (argc >= 3) { + f = get_freefunc(sym, &funcwrap); + } + + if (p) { + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + if (data->ptr && data->free) { + /* Free previous memory. Use of inappropriate initialize may cause SEGV. */ + (*(data->free))(data->ptr); + } + data->wrap[0] = wrap; + data->wrap[1] = funcwrap; + data->ptr = p; + data->size = s; + data->free = f; + } + + return Qnil; +} + +static VALUE +rb_fiddle_ptr_call_free(VALUE self); + +/* + * call-seq: + * Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance + * Fiddle::Pointer.malloc(size, freefunc) { |pointer| ... } => ... + * + * == Examples + * + * # Automatically freeing the pointer when the block is exited - recommended + * Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) do |pointer| + * ... + * end + * + * # Manually freeing but relying on the garbage collector otherwise + * pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) + * ... + * pointer.call_free + * + * # Relying on the garbage collector - may lead to unlimited memory allocated before freeing any, but safe + * pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE) + * ... + * + * # Only manually freeing + * pointer = Fiddle::Pointer.malloc(size) + * begin + * ... + * ensure + * Fiddle.free pointer + * end + * + * # No free function and no call to free - the native memory will leak if the pointer is garbage collected + * pointer = Fiddle::Pointer.malloc(size) + * ... + * + * Allocate +size+ bytes of memory and associate it with an optional + * +freefunc+. + * + * If a block is supplied, the pointer will be yielded to the block instead of + * being returned, and the return value of the block will be returned. A + * +freefunc+ must be supplied if a block is. + * + * If a +freefunc+ is supplied it will be called once, when the pointer is + * garbage collected or when the block is left if a block is supplied or + * when the user calls +call_free+, whichever happens first. +freefunc+ must be + * an address pointing to a function or an instance of +Fiddle::Function+. + */ +static VALUE +rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass) +{ + VALUE size, sym, obj, wrap = 0; + long s; + freefunc_t f; + + switch (rb_scan_args(argc, argv, "11", &size, &sym)) { + case 1: + s = NUM2LONG(size); + f = NULL; + break; + case 2: + s = NUM2LONG(size); + f = get_freefunc(sym, &wrap); + break; + default: + rb_bug("rb_fiddle_ptr_s_malloc"); + } + + obj = rb_fiddle_ptr_malloc(klass, s,f); + if (wrap) RPTR_DATA(obj)->wrap[1] = wrap; + + if (rb_block_given_p()) { + if (!f) { + rb_raise(rb_eArgError, "a free function must be supplied to Fiddle::Pointer.malloc when it is called with a block"); + } + return rb_ensure(rb_yield, obj, rb_fiddle_ptr_call_free, obj); + } else { + return obj; + } +} + +/* + * call-seq: to_i + * + * Returns the integer memory location of this pointer. + */ +static VALUE +rb_fiddle_ptr_to_i(VALUE self) +{ + struct ptr_data *data; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + return PTR2NUM(data->ptr); +} + +/* + * call-seq: to_value + * + * Cast this pointer to a ruby object. + */ +static VALUE +rb_fiddle_ptr_to_value(VALUE self) +{ + struct ptr_data *data; + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + return (VALUE)(data->ptr); +} + +/* + * call-seq: ptr + * + * Returns a new Fiddle::Pointer instance that is a dereferenced pointer for + * this pointer. + * + * Analogous to the star operator in C. + */ +static VALUE +rb_fiddle_ptr_ptr(VALUE self) +{ + struct ptr_data *data; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + return rb_fiddle_ptr_new(*((void**)(data->ptr)),0,0); +} + +/* + * call-seq: ref + * + * Returns a new Fiddle::Pointer instance that is a reference pointer for this + * pointer. + * + * Analogous to the ampersand operator in C. + */ +static VALUE +rb_fiddle_ptr_ref(VALUE self) +{ + struct ptr_data *data; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + return rb_fiddle_ptr_new(&(data->ptr),0,0); +} + +/* + * call-seq: null? + * + * Returns +true+ if this is a null pointer. + */ +static VALUE +rb_fiddle_ptr_null_p(VALUE self) +{ + struct ptr_data *data; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + return data->ptr ? Qfalse : Qtrue; +} + +/* + * call-seq: free=(function) + * + * Set the free function for this pointer to +function+ in the given + * Fiddle::Function. + */ +static VALUE +rb_fiddle_ptr_free_set(VALUE self, VALUE val) +{ + struct ptr_data *data; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + data->free = get_freefunc(val, &data->wrap[1]); + + return Qnil; +} + +/* + * call-seq: free => Fiddle::Function + * + * Get the free function for this pointer. + * + * Returns a new instance of Fiddle::Function. + * + * See Fiddle::Function.new + */ +static VALUE +rb_fiddle_ptr_free_get(VALUE self) +{ + struct ptr_data *pdata; + VALUE address; + VALUE arg_types; + VALUE ret_type; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); + + if (!pdata->free) + return Qnil; + + address = PTR2NUM(pdata->free); + ret_type = INT2NUM(TYPE_VOID); + arg_types = rb_ary_new(); + rb_ary_push(arg_types, INT2NUM(TYPE_VOIDP)); + + return rb_fiddle_new_function(address, arg_types, ret_type); +} + +/* + * call-seq: call_free => nil + * + * Call the free function for this pointer. Calling more than once will do + * nothing. Does nothing if there is no free function attached. + */ +static VALUE +rb_fiddle_ptr_call_free(VALUE self) +{ + struct ptr_data *pdata; + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); + fiddle_ptr_free_ptr(pdata); + return Qnil; +} + +/* + * call-seq: freed? => bool + * + * Returns if the free function for this pointer has been called. + */ +static VALUE +rb_fiddle_ptr_freed_p(VALUE self) +{ + struct ptr_data *pdata; + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, pdata); + return pdata->freed ? Qtrue : Qfalse; +} + +/* + * call-seq: + * + * ptr.to_s => string + * ptr.to_s(len) => string + * + * Returns the pointer contents as a string. + * + * When called with no arguments, this method will return the contents until + * the first NULL byte. + * + * When called with +len+, a string of +len+ bytes will be returned. + * + * See to_str + */ +static VALUE +rb_fiddle_ptr_to_s(int argc, VALUE argv[], VALUE self) +{ + struct ptr_data *data; + VALUE arg1, val; + int len; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + switch (rb_scan_args(argc, argv, "01", &arg1)) { + case 0: + val = rb_str_new2((char*)(data->ptr)); + break; + case 1: + len = NUM2INT(arg1); + val = rb_str_new((char*)(data->ptr), len); + break; + default: + rb_bug("rb_fiddle_ptr_to_s"); + } + + return val; +} + +/* + * call-seq: + * + * ptr.to_str => string + * ptr.to_str(len) => string + * + * Returns the pointer contents as a string. + * + * When called with no arguments, this method will return the contents with the + * length of this pointer's +size+. + * + * When called with +len+, a string of +len+ bytes will be returned. + * + * See to_s + */ +static VALUE +rb_fiddle_ptr_to_str(int argc, VALUE argv[], VALUE self) +{ + struct ptr_data *data; + VALUE arg1, val; + int len; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + switch (rb_scan_args(argc, argv, "01", &arg1)) { + case 0: + val = rb_str_new((char*)(data->ptr),data->size); + break; + case 1: + len = NUM2INT(arg1); + val = rb_str_new((char*)(data->ptr), len); + break; + default: + rb_bug("rb_fiddle_ptr_to_str"); + } + + return val; +} + +/* + * call-seq: inspect + * + * Returns a string formatted with an easily readable representation of the + * internal state of the pointer. + */ +static VALUE +rb_fiddle_ptr_inspect(VALUE self) +{ + struct ptr_data *data; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + return rb_sprintf("#<%"PRIsVALUE":%p ptr=%p size=%ld free=%p>", + RB_OBJ_CLASSNAME(self), (void *)data, data->ptr, data->size, (void *)data->free); +} + +/* + * call-seq: + * ptr == other => true or false + * ptr.eql?(other) => true or false + * + * Returns true if +other+ wraps the same pointer, otherwise returns + * false. + */ +static VALUE +rb_fiddle_ptr_eql(VALUE self, VALUE other) +{ + void *ptr1, *ptr2; + + if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qfalse; + + ptr1 = rb_fiddle_ptr2cptr(self); + ptr2 = rb_fiddle_ptr2cptr(other); + + return ptr1 == ptr2 ? Qtrue : Qfalse; +} + +/* + * call-seq: + * ptr <=> other => -1, 0, 1, or nil + * + * Returns -1 if less than, 0 if equal to, 1 if greater than +other+. + * + * Returns nil if +ptr+ cannot be compared to +other+. + */ +static VALUE +rb_fiddle_ptr_cmp(VALUE self, VALUE other) +{ + void *ptr1, *ptr2; + SIGNED_VALUE diff; + + if(!rb_obj_is_kind_of(other, rb_cPointer)) return Qnil; + + ptr1 = rb_fiddle_ptr2cptr(self); + ptr2 = rb_fiddle_ptr2cptr(other); + diff = (SIGNED_VALUE)ptr1 - (SIGNED_VALUE)ptr2; + if (!diff) return INT2FIX(0); + return diff > 0 ? INT2NUM(1) : INT2NUM(-1); +} + +/* + * call-seq: + * ptr + n => new cptr + * + * Returns a new pointer instance that has been advanced +n+ bytes. + */ +static VALUE +rb_fiddle_ptr_plus(VALUE self, VALUE other) +{ + void *ptr; + long num, size; + + ptr = rb_fiddle_ptr2cptr(self); + size = RPTR_DATA(self)->size; + num = NUM2LONG(other); + return rb_fiddle_ptr_new((char *)ptr + num, size - num, 0); +} + +/* + * call-seq: + * ptr - n => new cptr + * + * Returns a new pointer instance that has been moved back +n+ bytes. + */ +static VALUE +rb_fiddle_ptr_minus(VALUE self, VALUE other) +{ + void *ptr; + long num, size; + + ptr = rb_fiddle_ptr2cptr(self); + size = RPTR_DATA(self)->size; + num = NUM2LONG(other); + return rb_fiddle_ptr_new((char *)ptr - num, size + num, 0); +} + +/* + * call-seq: + * ptr[index] -> an_integer + * ptr[start, length] -> a_string + * + * Returns integer stored at _index_. + * + * If _start_ and _length_ are given, a string containing the bytes from + * _start_ of _length_ will be returned. + */ +static VALUE +rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self) +{ + VALUE arg0, arg1; + VALUE retval = Qnil; + size_t offset, len; + struct ptr_data *data; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference"); + switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){ + case 1: + offset = NUM2ULONG(arg0); + retval = INT2NUM(*((char *)data->ptr + offset)); + break; + case 2: + offset = NUM2ULONG(arg0); + len = NUM2ULONG(arg1); + retval = rb_str_new((char *)data->ptr + offset, len); + break; + default: + rb_bug("rb_fiddle_ptr_aref()"); + } + return retval; +} + +/* + * call-seq: + * ptr[index] = int -> int + * ptr[start, length] = string or cptr or addr -> string or dl_cptr or addr + * + * Set the value at +index+ to +int+. + * + * Or, set the memory at +start+ until +length+ with the contents of +string+, + * the memory from +dl_cptr+, or the memory pointed at by the memory address + * +addr+. + */ +static VALUE +rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self) +{ + VALUE arg0, arg1, arg2; + VALUE retval = Qnil; + size_t offset, len; + void *mem; + struct ptr_data *data; + + TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data); + if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference"); + switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){ + case 2: + offset = NUM2ULONG(arg0); + ((char*)data->ptr)[offset] = NUM2UINT(arg1); + retval = arg1; + break; + case 3: + offset = NUM2ULONG(arg0); + len = NUM2ULONG(arg1); + if (RB_TYPE_P(arg2, T_STRING)) { + mem = StringValuePtr(arg2); + } + else if( rb_obj_is_kind_of(arg2, rb_cPointer) ){ + mem = rb_fiddle_ptr2cptr(arg2); + } + else{ + mem = NUM2PTR(arg2); + } + memcpy((char *)data->ptr + offset, mem, len); + retval = arg2; + break; + default: + rb_bug("rb_fiddle_ptr_aset()"); + } + return retval; +} + +/* + * call-seq: size=(size) + * + * Set the size of this pointer to +size+ + */ +static VALUE +rb_fiddle_ptr_size_set(VALUE self, VALUE size) +{ + RPTR_DATA(self)->size = NUM2LONG(size); + return size; +} + +/* + * call-seq: size + * + * Get the size of this pointer. + */ +static VALUE +rb_fiddle_ptr_size_get(VALUE self) +{ + return LONG2NUM(RPTR_DATA(self)->size); +} + +/* + * call-seq: + * Fiddle::Pointer[val] => cptr + * to_ptr(val) => cptr + * + * Get the underlying pointer for ruby object +val+ and return it as a + * Fiddle::Pointer object. + */ +static VALUE +rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val) +{ + VALUE ptr, wrap = val, vptr; + + if (RTEST(rb_obj_is_kind_of(val, rb_cIO))){ + rb_io_t *fptr; + FILE *fp; + GetOpenFile(val, fptr); + fp = rb_io_stdio_file(fptr); + ptr = rb_fiddle_ptr_new(fp, 0, NULL); + } + else if (RTEST(rb_obj_is_kind_of(val, rb_cString))){ + char *str = StringValuePtr(val); + wrap = val; + ptr = rb_fiddle_ptr_new(str, RSTRING_LEN(val), NULL); + } + else if ((vptr = rb_check_funcall(val, id_to_ptr, 0, 0)) != Qundef){ + if (rb_obj_is_kind_of(vptr, rb_cPointer)){ + ptr = vptr; + wrap = 0; + } + else{ + rb_raise(rb_eFiddleDLError, "to_ptr should return a Fiddle::Pointer object"); + } + } + else{ + VALUE num = rb_Integer(val); + if (num == val) wrap = 0; + ptr = rb_fiddle_ptr_new(NUM2PTR(num), 0, NULL); + } + if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap; + return ptr; +} + +void +Init_fiddle_pointer(void) +{ +#undef rb_intern + id_to_ptr = rb_intern("to_ptr"); + + /* Document-class: Fiddle::Pointer + * + * Fiddle::Pointer is a class to handle C pointers + * + */ + rb_cPointer = rb_define_class_under(mFiddle, "Pointer", rb_cObject); + rb_define_alloc_func(rb_cPointer, rb_fiddle_ptr_s_allocate); + rb_define_singleton_method(rb_cPointer, "malloc", rb_fiddle_ptr_s_malloc, -1); + rb_define_singleton_method(rb_cPointer, "to_ptr", rb_fiddle_ptr_s_to_ptr, 1); + rb_define_singleton_method(rb_cPointer, "[]", rb_fiddle_ptr_s_to_ptr, 1); + rb_define_method(rb_cPointer, "initialize", rb_fiddle_ptr_initialize, -1); + rb_define_method(rb_cPointer, "free=", rb_fiddle_ptr_free_set, 1); + rb_define_method(rb_cPointer, "free", rb_fiddle_ptr_free_get, 0); + rb_define_method(rb_cPointer, "call_free", rb_fiddle_ptr_call_free, 0); + rb_define_method(rb_cPointer, "freed?", rb_fiddle_ptr_freed_p, 0); + rb_define_method(rb_cPointer, "to_i", rb_fiddle_ptr_to_i, 0); + rb_define_method(rb_cPointer, "to_int", rb_fiddle_ptr_to_i, 0); + rb_define_method(rb_cPointer, "to_value", rb_fiddle_ptr_to_value, 0); + rb_define_method(rb_cPointer, "ptr", rb_fiddle_ptr_ptr, 0); + rb_define_method(rb_cPointer, "+@", rb_fiddle_ptr_ptr, 0); + rb_define_method(rb_cPointer, "ref", rb_fiddle_ptr_ref, 0); + rb_define_method(rb_cPointer, "-@", rb_fiddle_ptr_ref, 0); + rb_define_method(rb_cPointer, "null?", rb_fiddle_ptr_null_p, 0); + rb_define_method(rb_cPointer, "to_s", rb_fiddle_ptr_to_s, -1); + rb_define_method(rb_cPointer, "to_str", rb_fiddle_ptr_to_str, -1); + rb_define_method(rb_cPointer, "inspect", rb_fiddle_ptr_inspect, 0); + rb_define_method(rb_cPointer, "<=>", rb_fiddle_ptr_cmp, 1); + rb_define_method(rb_cPointer, "==", rb_fiddle_ptr_eql, 1); + rb_define_method(rb_cPointer, "eql?", rb_fiddle_ptr_eql, 1); + rb_define_method(rb_cPointer, "+", rb_fiddle_ptr_plus, 1); + rb_define_method(rb_cPointer, "-", rb_fiddle_ptr_minus, 1); + rb_define_method(rb_cPointer, "[]", rb_fiddle_ptr_aref, -1); + rb_define_method(rb_cPointer, "[]=", rb_fiddle_ptr_aset, -1); + rb_define_method(rb_cPointer, "size", rb_fiddle_ptr_size_get, 0); + rb_define_method(rb_cPointer, "size=", rb_fiddle_ptr_size_set, 1); + +#ifdef HAVE_RUBY_MEMORY_VIEW_H + rb_memory_view_register(rb_cPointer, &fiddle_ptr_memory_view_entry); +#endif + + /* Document-const: NULL + * + * A NULL pointer + */ + rb_define_const(mFiddle, "NULL", rb_fiddle_ptr_new(0, 0, 0)); +} diff --git a/ext/fiddle/win32/fficonfig.h b/ext/fiddle/win32/fficonfig.h new file mode 100644 index 0000000000..776808159c --- /dev/null +++ b/ext/fiddle/win32/fficonfig.h @@ -0,0 +1,29 @@ +#define HAVE_ALLOCA 1 +#define HAVE_MEMCPY 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#if _MSC_VER >= 1600 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#endif + +#define SIZEOF_DOUBLE 8 +#if defined(X86_WIN64) +#define SIZEOF_SIZE_T 8 +#else +#define SIZEOF_SIZE_T 4 +#endif + +#define STACK_DIRECTION -1 + +#define STDC_HEADERS 1 + +#ifdef LIBFFI_ASM +#define FFI_HIDDEN(name) +#else +#define FFI_HIDDEN +#endif + diff --git a/ext/fiddle/win32/libffi-3.2.1-mswin.patch b/ext/fiddle/win32/libffi-3.2.1-mswin.patch new file mode 100644 index 0000000000..f9100e703d --- /dev/null +++ b/ext/fiddle/win32/libffi-3.2.1-mswin.patch @@ -0,0 +1,191 @@ +diff -ru libffi-3.2.1/src/x86/ffi.c libffi-3.2.1/src/x86/ffi.c +--- libffi-3.2.1/src/x86/ffi.c 2014-11-08 21:47:24.000000000 +0900 ++++ libffi-3.2.1/src/x86/ffi.c 2014-12-25 18:46:14.806761900 +0900 +@@ -99,11 +99,13 @@ + i != 0; + i--, p_arg += dir, p_argv += dir) + { ++ size_t z; ++ + /* Align if necessary */ + if ((sizeof(void*) - 1) & (size_t) argp) + argp = (char *) ALIGN(argp, sizeof(void*)); + +- size_t z = (*p_arg)->size; ++ z = (*p_arg)->size; + + #ifdef X86_WIN64 + if (z > FFI_SIZEOF_ARG +@@ -202,6 +204,7 @@ + on top of stack, so that those can be moved to registers by call-handler. */ + if (stack_args_count > 0) + { ++ int i; + if (dir < 0 && stack_args_count > 1) + { + /* Reverse order if iterating arguments backwards */ +@@ -210,7 +213,6 @@ + *(ffi_arg*) p_stack_data[stack_args_count - 1] = tmp; + } + +- int i; + for (i = 0; i < stack_args_count; i++) + { + if (p_stack_data[i] != argp2) +@@ -569,11 +571,12 @@ + i < cif->nargs && passed_regs < max_stack_count; + i++, p_arg++) + { ++ size_t sz; + if ((*p_arg)->type == FFI_TYPE_FLOAT + || (*p_arg)->type == FFI_TYPE_STRUCT) + continue; + +- size_t sz = (*p_arg)->size; ++ sz = (*p_arg)->size; + if(sz == 0 || sz > FFI_SIZEOF_ARG) + continue; + +@@ -599,11 +602,13 @@ + i != 0; + i--, p_arg += dir, p_argv += dir) + { ++ size_t z; ++ + /* Align if necessary */ + if ((sizeof(void*) - 1) & (size_t) argp) + argp = (char *) ALIGN(argp, sizeof(void*)); + +- size_t z = (*p_arg)->size; ++ z = (*p_arg)->size; + + #ifdef X86_WIN64 + if (z > FFI_SIZEOF_ARG +@@ -642,7 +647,7 @@ + #endif + } + +- return (size_t)argp - (size_t)stack; ++ return (int)((size_t)argp - (size_t)stack); + } + + #define FFI_INIT_TRAMPOLINE_WIN64(TRAMP,FUN,CTX,MASK) \ +@@ -855,11 +860,12 @@ + + for (i = 0; i < cif->nargs && passed_regs <= max_regs; i++) + { ++ size_t sz; + if (cif->arg_types[i]->type == FFI_TYPE_FLOAT + || cif->arg_types[i]->type == FFI_TYPE_STRUCT) + continue; + +- size_t sz = cif->arg_types[i]->size; ++ sz = cif->arg_types[i]->size; + if (sz == 0 || sz > FFI_SIZEOF_ARG) + continue; + +diff -ru libffi-3.2.1/src/x86/ffitarget.h libffi-3.2.1/src/x86/ffitarget.h +--- libffi-3.2.1/src/x86/ffitarget.h 2014-11-08 21:47:24.000000000 +0900 ++++ libffi-3.2.1/src/x86/ffitarget.h 2014-12-22 15:45:54.000000000 +0900 +@@ -50,7 +50,9 @@ + #endif + + #define FFI_TARGET_SPECIFIC_STACK_SPACE_ALLOCATION ++#ifndef _MSC_VER + #define FFI_TARGET_HAS_COMPLEX_TYPE ++#endif + + /* ---- Generic type definitions ----------------------------------------- */ + +diff -ru libffi-3.2.1/src/x86/win64.S libffi-3.2.1/src/x86/win64.S +--- libffi-3.2.1/src/x86/win64.S 2014-11-08 21:47:24.000000000 +0900 ++++ libffi-3.2.1/src/x86/win64.S 2014-12-22 16:14:40.000000000 +0900 +@@ -127,7 +127,7 @@ + + mov rcx, QWORD PTR RVALUE[rbp] + mov DWORD PTR [rcx], eax +- jmp ret_void$ ++ jmp SHORT ret_void$ + + ret_struct2b$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SMALL_STRUCT_2B +@@ -135,7 +135,7 @@ + + mov rcx, QWORD PTR RVALUE[rbp] + mov WORD PTR [rcx], ax +- jmp ret_void$ ++ jmp SHORT ret_void$ + + ret_struct1b$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SMALL_STRUCT_1B +@@ -143,7 +143,7 @@ + + mov rcx, QWORD PTR RVALUE[rbp] + mov BYTE PTR [rcx], al +- jmp ret_void$ ++ jmp SHORT ret_void$ + + ret_uint8$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_UINT8 +@@ -152,7 +152,7 @@ + mov rcx, QWORD PTR RVALUE[rbp] + movzx rax, al + mov QWORD PTR [rcx], rax +- jmp ret_void$ ++ jmp SHORT ret_void$ + + ret_sint8$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SINT8 +@@ -161,7 +161,7 @@ + mov rcx, QWORD PTR RVALUE[rbp] + movsx rax, al + mov QWORD PTR [rcx], rax +- jmp ret_void$ ++ jmp SHORT ret_void$ + + ret_uint16$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_UINT16 +@@ -188,7 +188,13 @@ + mov rcx, QWORD PTR RVALUE[rbp] + mov eax, eax + mov QWORD PTR [rcx], rax +- jmp SHORT ret_void$ ++ ++ret_void$: ++ xor rax, rax ++ ++ lea rsp, QWORD PTR [rbp+16] ++ pop rbp ++ ret 0 + + ret_sint32$: + cmp DWORD PTR CIF_FLAGS[rbp], FFI_TYPE_SINT32 +@@ -247,13 +253,6 @@ + cdqe + mov QWORD PTR [rcx], rax + jmp SHORT ret_void$ +- +-ret_void$: +- xor rax, rax +- +- lea rsp, QWORD PTR [rbp+16] +- pop rbp +- ret 0 + ffi_call_win64 ENDP + _TEXT ENDS + END +diff -ru libffi-3.2.1/include/ffi.h.in libffi-3.2.1/include/ffi.h.in +--- libffi-3.2.1/include/ffi.h.in 2014-11-08 21:47:24.000000000 +0900 ++++ libffi-3.2.1/include/ffi.h.in 2015-01-11 12:35:30.000000000 +0900 +@@ -103,6 +103,11 @@ + # undef FFI_64_BIT_MAX + # define FFI_64_BIT_MAX 9223372036854775807LL + # endif ++# ifdef _MSC_VER ++# define FFI_LONG_LONG_MAX _I64_MAX ++# undef FFI_64_BIT_MAX ++# define FFI_64_BIT_MAX 9223372036854775807I64 ++# endif + # endif + #endif + diff --git a/ext/fiddle/win32/libffi-config.rb b/ext/fiddle/win32/libffi-config.rb new file mode 100755 index 0000000000..985fc29d36 --- /dev/null +++ b/ext/fiddle/win32/libffi-config.rb @@ -0,0 +1,48 @@ +#!/usr/bin/ruby +# frozen_string_literal: true +require 'fileutils' + +basedir = File.dirname(__FILE__) +conf = {} +enable = {} +until ARGV.empty? + arg = ARGV.shift + case arg + when '-C' + # ignore + when /\A--srcdir=(.*)/ + conf['SRCDIR'] = srcdir = $1 + when /\A(CC|CFLAGS|CXX|CXXFLAGS|LD|LDFLAGS)=(.*)/ + conf[$1] = $2 + when /\A--host=(.*)/ + host = $1 + when /\A--enable-([^=]+)(?:=(.*))?/ + enable[$1] = $2 || true + when /\A--disable-([^=]+)/ + enable[$1] = false + end +end + +IO.foreach("#{srcdir}/configure.ac") do |line| + if /^AC_INIT\((.*)\)/ =~ line + version = $1.split(/,\s*/)[1] + version.gsub!(/\A\[|\]\z/, '') + conf['VERSION'] = version + break + end +end + +builddir = srcdir == "." ? (enable['builddir'] || ".") : "." +conf['TARGET'] = /^x64/ =~ host ? "X86_WIN64" : "X86_WIN32" + +FileUtils.mkdir_p([builddir, "#{builddir}/include", "#{builddir}/src/x86"]) +FileUtils.cp("#{basedir}/fficonfig.h", ".", preserve: true) + +hdr = IO.binread("#{srcdir}/include/ffi.h.in") +hdr.gsub!(/@(\w+)@/) {conf[$1] || $&} +hdr.gsub!(/^(#if\s+)@\w+@/, '\10') +IO.binwrite("#{builddir}/include/ffi.h", hdr) + +mk = IO.binread("#{basedir}/libffi.mk.tmpl") +mk.gsub!(/@(\w+)@/) {conf[$1] || $&} +IO.binwrite("Makefile", mk) diff --git a/ext/fiddle/win32/libffi.mk.tmpl b/ext/fiddle/win32/libffi.mk.tmpl new file mode 100644 index 0000000000..2a16e8efec --- /dev/null +++ b/ext/fiddle/win32/libffi.mk.tmpl @@ -0,0 +1,96 @@ +# -*- makefile -*- +# ==================================================================== +# +# libffi Windows Makefile +# +# +# ==================================================================== +# +NAME = ffi +TARGET = @TARGET@ +CC = cl +!if "$(TARGET)" == "X86_WIN64" +AS = ml64 +!else +AS = ml +!endif +AR = link +DLEXT = dll +OBJEXT = obj +LIBEXT = lib +TOPDIR = @SRCDIR@ +CPP = $(CC) -EP +CFLAGS = @CFLAGS@ +ARFLAGS = -lib +ASFLAGS = -coff -W3 -Cx +INCLUDES= -I. -I./include -I./src/x86 \ + -I$(TOPDIR)/include -I$(TOPDIR)/include/src/x86 + +SRCDIR = $(TOPDIR)/src +WORKDIR = ./.libs +BUILDDIR= ./src +LIBNAME = lib$(NAME) +STATICLIB= $(WORKDIR)/$(LIBNAME)_convenience.$(LIBEXT) + +HEADERS = \ + ./fficonfig.h +FFI_HEADERS = \ + ./include/ffi.h \ + ./include/ffitarget.h + +!if "$(TARGET)" == "X86_WIN32" +OSSRC = win32 +!else if "$(TARGET)" == "X86_WIN64" +OSSRC = win64 +!else +! error unknown target: $(TARGET) +!endif + +OBJECTS = \ + $(BUILDDIR)/closures.$(OBJEXT) \ + $(BUILDDIR)/debug.$(OBJEXT) \ + $(BUILDDIR)/java_raw_api.$(OBJEXT) \ + $(BUILDDIR)/prep_cif.$(OBJEXT) \ + $(BUILDDIR)/raw_api.$(OBJEXT) \ + $(BUILDDIR)/types.$(OBJEXT) \ + $(BUILDDIR)/x86/ffi.$(OBJEXT) \ + $(BUILDDIR)/x86/$(OSSRC).$(OBJEXT) +ASMSRCS = \ + $(BUILDDIR)/x86/$(OSSRC).asm + +.SUFFIXES : .S .asm + +all: $(WORKDIR) $(STATICLIB) + +{$(SRCDIR)}.c{$(BUILDDIR)}.$(OBJEXT): + $(CC) -c $(CFLAGS) $(INCLUDES) -Fo$(@:\=/) -Fd$(WORKDIR)/$(NAME)-src $(<:\=/) + +{$(SRCDIR)/x86}.c{$(BUILDDIR)/x86}.$(OBJEXT): + $(CC) -c $(CFLAGS) $(INCLUDES) -Fo$(@:\=/) -Fd$(WORKDIR)/$(NAME)-src $(<:\=/) + +{$(SRCDIR)/x86}.S{$(BUILDDIR)/x86}.asm: + $(CPP) $(CFLAGS) $(INCLUDES) $(<:\=/) >$(@:\=/) + +{$(BUILDDIR)/x86}.asm{$(BUILDDIR)/x86}.$(OBJEXT): + cd $(@D) && $(AS) -c $(ASFLAGS) -Fo $(@F) $(<F) + +$(BUILDDIR)/x86/$(OSSRC).asm: $(SRCDIR)/x86/$(OSSRC).S + +$(OBJECTS): $(FFI_HEADERS) $(HEADERS) + +$(WORKDIR): + -@if not exist "$(WORKDIR:/=\)\$(NULL)" mkdir $(WORKDIR:/=\) + +$(STATICLIB): $(WORKDIR) $(OBJECTS) + $(AR) $(ARFLAGS) -out:$(STATICLIB) @<< + $(OBJECTS) +<< + +clean: + -@del /Q $(OBJECTS:/=\) 2>NUL + -@del /Q $(ASMSRCS:/=\) 2>NUL + -@del /Q /S $(WORKDIR:/=\) 2>NUL + +distclean: clean + -@del /Q $(HEADERS:/=\) $(FFI_HEADERS:/=\) 2>NUL + -@del /Q Makefile 2>NUL |
