diff options
author | usa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-12-14 15:09:35 +0000 |
---|---|---|
committer | usa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-12-14 15:09:35 +0000 |
commit | 1a74fa4b04da04bd2bb33103dd3cf431438df38e (patch) | |
tree | f4a1d6c2961339e0c1d653c0f8427a53315080f0 /ruby_2_2/ext/fiddle | |
parent | a5b755e50e2d9aabf28ba24bf58644ca22b01a4f (diff) |
add tag v2_2_9
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v2_2_9@61257 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'ruby_2_2/ext/fiddle')
26 files changed, 4284 insertions, 0 deletions
diff --git a/ruby_2_2/ext/fiddle/closure.c b/ruby_2_2/ext/fiddle/closure.c new file mode 100644 index 0000000000..d857bfe870 --- /dev/null +++ b/ruby_2_2/ext/fiddle/closure.c @@ -0,0 +1,316 @@ +#include <fiddle.h> + +VALUE cFiddleClosure; + +typedef struct { + void * code; + ffi_closure *pcl; + ffi_cif cif; + int argc; + ffi_type **argv; +} fiddle_closure; + +#if defined(USE_FFI_CLOSURE_ALLOC) +#elif defined(__OpenBSD__) || defined(__APPLE__) || defined(__linux__) +# define USE_FFI_CLOSURE_ALLOC 0 +#elif defined(RUBY_LIBFFI_MODVERSION) && RUBY_LIBFFI_MODVERSION < 3000005 && \ + (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64)) +# 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; + + if (ptr) { + 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,}, +}; + +void +callback(ffi_cif *cif, void *resp, void **args, void *ctx) +{ + VALUE self = (VALUE)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_PTR(rbargs)[i]); + switch (type) { + case TYPE_VOID: + argc = 0; + break; + case TYPE_INT: + rb_ary_push(params, INT2NUM(*(int *)args[i])); + break; + case -TYPE_INT: + rb_ary_push(params, UINT2NUM(*(unsigned int *)args[i])); + break; + case TYPE_VOIDP: + rb_ary_push(params, + rb_funcall(cPointer, rb_intern("[]"), 1, + PTR2NUM(*(void **)args[i]))); + break; + case TYPE_LONG: + rb_ary_push(params, LONG2NUM(*(long *)args[i])); + break; + case -TYPE_LONG: + rb_ary_push(params, ULONG2NUM(*(unsigned long *)args[i])); + break; + case TYPE_CHAR: + rb_ary_push(params, INT2NUM(*(signed char *)args[i])); + break; + case -TYPE_CHAR: + rb_ary_push(params, UINT2NUM(*(unsigned char *)args[i])); + break; + case TYPE_SHORT: + rb_ary_push(params, INT2NUM(*(signed short *)args[i])); + break; + case -TYPE_SHORT: + rb_ary_push(params, UINT2NUM(*(unsigned short *)args[i])); + break; + case TYPE_DOUBLE: + rb_ary_push(params, rb_float_new(*(double *)args[i])); + break; + case TYPE_FLOAT: + rb_ary_push(params, rb_float_new(*(float *)args[i])); + break; +#if HAVE_LONG_LONG + case TYPE_LONG_LONG: + rb_ary_push(params, LL2NUM(*(LONG_LONG *)args[i])); + break; + case -TYPE_LONG_LONG: + rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)args[i])); + break; +#endif + default: + rb_raise(rb_eRuntimeError, "closure args: %d", type); + } + } + + ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_PTR(params)); + RB_GC_GUARD(params); + + type = NUM2INT(ctype); + switch (type) { + case TYPE_VOID: + break; + case TYPE_LONG: + *(long *)resp = NUM2LONG(ret); + break; + case -TYPE_LONG: + *(unsigned long *)resp = NUM2ULONG(ret); + break; + case TYPE_CHAR: + case TYPE_SHORT: + case TYPE_INT: + *(ffi_sarg *)resp = NUM2INT(ret); + break; + case -TYPE_CHAR: + case -TYPE_SHORT: + case -TYPE_INT: + *(ffi_arg *)resp = NUM2UINT(ret); + break; + case TYPE_VOIDP: + *(void **)resp = NUM2PTR(ret); + break; + case TYPE_DOUBLE: + *(double *)resp = NUM2DBL(ret); + break; + case TYPE_FLOAT: + *(float *)resp = (float)NUM2DBL(ret); + break; +#if HAVE_LONG_LONG + case TYPE_LONG_LONG: + *(LONG_LONG *)resp = NUM2LL(ret); + break; + case -TYPE_LONG_LONG: + *(unsigned LONG_LONG *)resp = NUM2ULL(ret); + break; +#endif + default: + rb_raise(rb_eRuntimeError, "closure retval: %d", type); + } +} + +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; +} + +static VALUE +initialize(int rbargc, VALUE argv[], VALUE self) +{ + VALUE ret; + VALUE args; + VALUE abi; + fiddle_closure * cl; + ffi_cif * cif; + ffi_closure *pcl; + ffi_status result; + int i, argc; + + if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi)) + abi = INT2NUM(FFI_DEFAULT_ABI); + + Check_Type(args, T_ARRAY); + + argc = RARRAY_LENINT(args); + + TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl); + + cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *)); + + for (i = 0; i < argc; i++) { + int type = NUM2INT(RARRAY_PTR(args)[i]); + cl->argv[i] = INT2FFI_TYPE(type); + } + cl->argv[argc] = NULL; + + rb_iv_set(self, "@ctype", ret); + rb_iv_set(self, "@args", args); + + cif = &cl->cif; + pcl = cl->pcl; + + result = ffi_prep_cif(cif, NUM2INT(abi), argc, + INT2FFI_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 *)self, cl->code); +#else + result = ffi_prep_closure(pcl, cif, callback, (void *)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 self; +} + +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/ruby_2_2/ext/fiddle/closure.h b/ruby_2_2/ext/fiddle/closure.h new file mode 100644 index 0000000000..d0a8be6180 --- /dev/null +++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/conversions.c b/ruby_2_2/ext/fiddle/conversions.c new file mode 100644 index 0000000000..d40ddc1f38 --- /dev/null +++ b/ruby_2_2/ext/fiddle/conversions.c @@ -0,0 +1,141 @@ +#include <fiddle.h> + +ffi_type * +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; + default: + rb_raise(rb_eRuntimeError, "unknown type %d", type); + } + return &ffi_type_pointer; +} + +void +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; + default: + rb_raise(rb_eRuntimeError, "unknown type %d", type); + } +} + +VALUE +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); + default: + rb_raise(rb_eRuntimeError, "unknown type %d", type); + } + + UNREACHABLE; +} + +/* vim: set noet sw=4 sts=4 */ diff --git a/ruby_2_2/ext/fiddle/conversions.h b/ruby_2_2/ext/fiddle/conversions.h new file mode 100644 index 0000000000..d0a08d6bc0 --- /dev/null +++ b/ruby_2_2/ext/fiddle/conversions.h @@ -0,0 +1,44 @@ +#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; + +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) value_to_generic((_type), (_src), (_dst)) +#define INT2FFI_TYPE(_type) int_to_ffi_type(_type) +#define GENERIC2VALUE(_type, _retval) generic_to_value((_type), (_retval)) + +#if SIZEOF_VOIDP == SIZEOF_LONG +# define PTR2NUM(x) (ULONG2NUM((unsigned long)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULONG(x))) +#else +/* # error --->> Ruby/DL2 requires sizeof(void*) == sizeof(long) to be compiled. <<--- */ +# define PTR2NUM(x) (ULL2NUM((unsigned long long)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULL(x))) +#endif + +#endif diff --git a/ruby_2_2/ext/fiddle/depend b/ruby_2_2/ext/fiddle/depend new file mode 100644 index 0000000000..47ba44e9a6 --- /dev/null +++ b/ruby_2_2/ext/fiddle/depend @@ -0,0 +1,55 @@ +PWD = + +CONFIGURE_LIBFFI = \ + $(LIBFFI_CONFIGURE) --disable-shared \ + --host=$(LIBFFI_ARCH) --enable-builddir=$(arch) \ + CC="$(CC)" CFLAGS="$(LIBFFI_CFLAGS)" \ + LD="$(LD)" LDFLAGS="$(LIBFFI_LDFLAGS)" + +$(OBJS): $(HDRS) $(ruby_headers) \ + $(hdrdir)/ruby/io.h \ + $(hdrdir)/ruby/encoding.h \ + $(hdrdir)/ruby/oniguruma.h + +$(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) +build-libffi $(LIBFFI_A): + $(Q) $(SUBMAKE_LIBFFI) + +clean-libffi: + $(Q) $(SUBMAKE_LIBFFI) clean + +distclean-libffi: + $(Q) $(SUBMAKE_LIBFFI) distclean + $(Q) $(RM) $(LIBFFI_DIR)/local.exp + $(Q) $(RUBY) -rfileutils -e "FileUtils.rmdir(Dir.glob(ARGV[0]+'/**/'), :parents=>true)" $(LIBFFI_DIR) + +realclean-libffi: + $(Q) $(RMALL) $(LIBFFI_DIR) + +.PHONY clean-libffi distclean-libffi realclean-libffi: .FORCE +.PHONY clean-none distclean-none realclean-none: .FORCE + +clean: clean-$(LIBFFI_CLEAN) +distclean: distclean-$(LIBFFI_CLEAN) +realclean: realclean-$(LIBFFI_CLEAN) + +.PHONY configure: configure-libffi diff --git a/ruby_2_2/ext/fiddle/extconf.rb b/ruby_2_2/ext/fiddle/extconf.rb new file mode 100644 index 0000000000..5688086f44 --- /dev/null +++ b/ruby_2_2/ext/fiddle/extconf.rb @@ -0,0 +1,168 @@ +require 'mkmf' + +# :stopdoc: + +if ! enable_config('bundled-libffi', false) + dir_config 'libffi' + + pkg_config("libffi") and + ver = pkg_config("libffi", "modversion") + + if have_header(ffi_header = 'ffi.h') + true + elsif have_header(ffi_header = 'ffi/ffi.h') + $defs.push(format('-DUSE_HEADER_HACKS')) + true + end and (have_library('ffi') || have_library('libffi')) +end or +begin + ver = Dir.glob("#{$srcdir}/libffi-*/") + .map {|n| File.basename(n)} + .max_by {|n| n.scan(/\d+/).map(&:to_i)} + unless ver + raise "missing libffi. Please install libffi." + end + + srcdir = "#{$srcdir}/#{ver}" + ffi_header = 'ffi.h' + libffi = Struct.new(*%I[dir srcdir builddir include lib a cflags ldflags opt arch]).new + libffi.dir = ver + if $srcdir == "." + libffi.builddir = "#{ver}/#{RUBY_PLATFORM}" + libffi.srcdir = "." + else + libffi.builddir = libffi.dir + libffi.srcdir = relative_from(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)", nowarn) + ver = ver[/libffi-(.*)/, 1] + + FileUtils.mkdir_p(libffi.dir) + libffi.opt = CONFIG['configure_args'][/'(-C)'/, 1] + libffi.ldflags = RbConfig.expand("$(LDFLAGS) #{libpathflag([relative_from($topdir, "..")])} #{$LIBRUBYARG}") + libffi.arch = RbConfig::CONFIG['host'] + if $mswin + $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} + --enable-builddir=#{RUBY_PLATFORM} + ] + 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)", nowarn)} + LD=#{ld} LDFLAGS=#{libffi.ldflags} + ] + + FileUtils.rm_f("#{libffi.include}/ffitarget.h") + Logging::open do + Logging.message("%p in %p\n", args, opts) + system(*args, **opts) or + raise "failed to configure libffi. Please install libffi." + 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("#{srcdir}/src/x86/ffitarget.h", libffi.include, preserve: true) + end + $INCFLAGS << " -I" << libffi.include +end + +if ver + ver = ver.gsub(/-rc\d+/, '') # If ver contains rc version, just ignored. + ver = (ver.split('.') + [0,0])[0,3] + $defs.push(%{-DRUBY_LIBFFI_MODVERSION=#{ '%d%03d%03d' % ver }}) +end + +have_header 'sys/mman.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" +elsif have_header "windows.h" + %w{ LoadLibrary FreeLibrary GetProcAddress }.each do |func| + abort "missing function #{func}" unless have_func(func) + end +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 << format("-DTYPE_%s=TYPE_%s", signed||type, size) + end + if signed + check_signedness(type.downcase, "stddef.h") + end + end +end + +if libffi + $LOCAL_LIBS.prepend("./#{libffi.a} ").strip! +end +create_makefile 'fiddle' do |conf| + if !libffi + next conf << "LIBFFI_CLEAN = none\n" + elsif $gnumake && !$nmake + submake = "$(MAKE) -C $(LIBFFI_DIR)\n" + else + submake = "cd $(LIBFFI_DIR) && \\\n\t\t" << "#{config_string("exec")} $(MAKE)".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} + LIBFFI_DIR = #{libffi.dir} + LIBFFI_A = #{libffi.a} + LIBFFI_CFLAGS = #{libffi.cflags} + LIBFFI_LDFLAGS = #{libffi.ldflags} + FFI_H = $(LIBFFI_DIR)/include/ffi.h + SUBMAKE_LIBFFI = #{submake} + LIBFFI_CLEAN = libffi + MK +end + +if libffi + $LIBPATH.pop +end + +# :startdoc: diff --git a/ruby_2_2/ext/fiddle/extlibs b/ruby_2_2/ext/fiddle/extlibs new file mode 100644 index 0000000000..7d5fda5247 --- /dev/null +++ b/ruby_2_2/ext/fiddle/extlibs @@ -0,0 +1,2 @@ +ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz md5:83b89587607e3eb65c70d361f13bab43 + win32/libffi-3.2.1-mswin.patch -p0 diff --git a/ruby_2_2/ext/fiddle/fiddle.c b/ruby_2_2/ext/fiddle/fiddle.c new file mode 100644 index 0000000000..9f3d1537d6 --- /dev/null +++ b/ruby_2_2/ext/fiddle/fiddle.c @@ -0,0 +1,454 @@ +#include <fiddle.h> + +VALUE mFiddle; +VALUE rb_eFiddleError; + +#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) + +void Init_fiddle_pointer(void); + +/* + * 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_xmalloc(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::DLError + * + * standard dynamic load exception + */ + rb_eFiddleError = rb_define_class_under(mFiddle, "DLError", rb_eStandardError); + + /* 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 + + /* 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)); + + /* 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_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_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: 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(); +} +/* vim: set noet sws=4 sw=4: */ diff --git a/ruby_2_2/ext/fiddle/fiddle.h b/ruby_2_2/ext/fiddle/fiddle.h new file mode 100644 index 0000000000..d2583c1cbf --- /dev/null +++ b/ruby_2_2/ext/fiddle/fiddle.h @@ -0,0 +1,138 @@ +#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_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 ALIGN_OF(type) offsetof(struct {char align_c; type align_x;}, align_x) + +#define ALIGN_VOIDP ALIGN_OF(void*) +#define ALIGN_SHORT ALIGN_OF(short) +#define ALIGN_CHAR ALIGN_OF(char) +#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) + +extern VALUE mFiddle; +extern VALUE rb_eFiddleError; + +VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type); + +#endif +/* vim: set noet sws=4 sw=4: */ diff --git a/ruby_2_2/ext/fiddle/function.c b/ruby_2_2/ext/fiddle/function.c new file mode 100644 index 0000000000..e0da8b69cb --- /dev/null +++ b/ruby_2_2/ext/fiddle/function.c @@ -0,0 +1,279 @@ +#include <fiddle.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) \ + if ((size_t)(len) < MAX_ARGS) { \ + /* OK */ \ + } \ + else { \ + rb_raise(rb_eTypeError, \ + name" is so large that it can cause integer overflow (%d)", \ + (len)); \ + } + +static void +deallocate(void *p) +{ + ffi_cif *ptr = p; + if (ptr->arg_types) xfree(ptr->arg_types); + xfree(ptr); +} + +static size_t +function_memsize(const void *p) +{ + /* const */ffi_cif *ptr = (ffi_cif *)p; + size_t size = 0; + + if (ptr) { + size += sizeof(*ptr); +#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API + size += ffi_raw_size(ptr); +#endif + } + return size; +} + +const rb_data_type_t function_data_type = { + "fiddle/function", + {0, deallocate, function_memsize,}, +}; + +static VALUE +allocate(VALUE klass) +{ + ffi_cif * cif; + + return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif); +} + +VALUE +rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type) +{ + VALUE argv[3]; + + argv[0] = address; + argv[1] = arg_types; + argv[2] = ret_type; + + return rb_class_new_instance(3, argv, cFiddleFunction); +} + +static int +parse_keyword_arg_i(VALUE key, VALUE value, VALUE self) +{ + if (key == ID2SYM(rb_intern("name"))) { + rb_iv_set(self, "@name", value); + } else { + rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE, + RB_OBJ_STRING(key)); + } + return ST_CONTINUE; +} + +static VALUE +initialize(int argc, VALUE argv[], VALUE self) +{ + ffi_cif * cif; + ffi_type **arg_types; + ffi_status result; + VALUE ptr, args, ret_type, abi, kwds; + int i; + + rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds); + if(NIL_P(abi)) abi = INT2NUM(FFI_DEFAULT_ABI); + + Check_Type(args, T_ARRAY); + Check_Max_Args("args", RARRAY_LENINT(args)); + + rb_iv_set(self, "@ptr", ptr); + rb_iv_set(self, "@args", args); + rb_iv_set(self, "@return_type", ret_type); + rb_iv_set(self, "@abi", abi); + + if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self); + + TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); + + arg_types = xcalloc(RARRAY_LEN(args) + 1, sizeof(ffi_type *)); + + for (i = 0; i < RARRAY_LEN(args); i++) { + int type = NUM2INT(RARRAY_PTR(args)[i]); + arg_types[i] = INT2FFI_TYPE(type); + } + arg_types[RARRAY_LEN(args)] = NULL; + + result = ffi_prep_cif ( + cif, + NUM2INT(abi), + RARRAY_LENINT(args), + INT2FFI_TYPE(NUM2INT(ret_type)), + arg_types); + + if (result) + rb_raise(rb_eRuntimeError, "error creating CIF %d", result); + + return self; +} + +static VALUE +function_call(int argc, VALUE argv[], VALUE self) +{ + ffi_cif * cif; + fiddle_generic retval; + fiddle_generic *generic_args; + void **values; + VALUE cfunc, types, cPointer; + int i; + VALUE alloc_buffer = 0; + + cfunc = rb_iv_get(self, "@ptr"); + types = rb_iv_get(self, "@args"); + cPointer = rb_const_get(mFiddle, rb_intern("Pointer")); + + Check_Max_Args("number of arguments", argc); + if(argc != RARRAY_LENINT(types)) { + rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", + argc, RARRAY_LENINT(types)); + } + + TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif); + + if (rb_safe_level() >= 1) { + for (i = 0; i < argc; i++) { + VALUE src = argv[i]; + if (OBJ_TAINTED(src)) { + rb_raise(rb_eSecurityError, "tainted parameter not allowed"); + } + } + } + + generic_args = ALLOCV(alloc_buffer, + (size_t)(argc + 1) * sizeof(void *) + (size_t)argc * sizeof(fiddle_generic)); + values = (void **)((char *)generic_args + (size_t)argc * sizeof(fiddle_generic)); + + for (i = 0; i < argc; i++) { + VALUE type = RARRAY_PTR(types)[i]; + VALUE src = argv[i]; + + if(NUM2INT(type) == TYPE_VOIDP) { + if(NIL_P(src)) { + src = INT2FIX(0); + } else if(cPointer != CLASS_OF(src)) { + src = rb_funcall(cPointer, rb_intern("[]"), 1, src); + } + src = rb_Integer(src); + } + + VALUE2GENERIC(NUM2INT(type), src, &generic_args[i]); + values[i] = (void *)&generic_args[i]; + } + values[argc] = NULL; + + ffi_call(cif, NUM2PTR(rb_Integer(cfunc)), &retval, values); + + rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno)); +#if defined(_WIN32) + rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno)); +#endif + + ALLOCV_END(alloc_buffer); + + return GENERIC2VALUE(rb_iv_get(self, "@return_type"), retval); +} + +void +Init_fiddle_function(void) +{ + /* + * Document-class: Fiddle::Function + * + * == Description + * + * A representation of a C function + * + * == Examples + * + * === 'strcpy' + * + * @libc = Fiddle.dlopen "/lib/libc.so.6" + * #=> #<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+ + * + * For an example see Fiddle::Function + * + */ + rb_define_method(cFiddleFunction, "call", function_call, -1); + + /* + * Document-method: new + * call-seq: new(ptr, args, ret_type, abi = DEFAULT) + * + * Constructs a Function object. + * * +ptr+ is a referenced function, of a Fiddle::Handle + * * +args+ is an Array of arguments, passed to the +ptr+ function + * * +ret_type+ is the return type of the function + * * +abi+ is the ABI of the function + * + */ + rb_define_method(cFiddleFunction, "initialize", initialize, -1); +} +/* vim: set noet sws=4 sw=4: */ diff --git a/ruby_2_2/ext/fiddle/function.h b/ruby_2_2/ext/fiddle/function.h new file mode 100644 index 0000000000..829e592c8a --- /dev/null +++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/handle.c b/ruby_2_2/ext/fiddle/handle.c new file mode 100644 index 0000000000..c5c356c175 --- /dev/null +++ b/ruby_2_2/ext/fiddle/handle.c @@ -0,0 +1,482 @@ +#include <ruby.h> +#include <fiddle.h> + +#define SafeStringValueCStr(v) (rb_check_safe_obj(rb_string_value(&v)), StringValueCStr(v)) + +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 ptr ? sizeof(struct dl_handle) : 0; +} + +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_eFiddleError, "%s", dlerror()); +#else + rb_raise(rb_eFiddleError, "could not close handle"); +#endif + } + return INT2NUM(ret); + } + rb_raise(rb_eFiddleError, "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 : SafeStringValueCStr(lib); + cflag = RTLD_LAZY | RTLD_GLOBAL; + break; + case 2: + clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib); + cflag = NUM2INT(flag); + break; + default: + rb_bug("rb_fiddle_handle_new"); + } + + rb_secure(2); + +#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_eFiddleError, "%s", err); + } +#else + if( !ptr ){ + err = dlerror(); + rb_raise(rb_eFiddleError, "%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); +} + +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_eFiddleError, "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 = SafeStringValueCStr(symbol); + + rb_secure(2); +#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_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol); + } + + return PTR2NUM(func); +} + +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, "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, "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/ruby_2_2/ext/fiddle/lib/fiddle.rb b/ruby_2_2/ext/fiddle/lib/fiddle.rb new file mode 100644 index 0000000000..ae6e299637 --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle.rb @@ -0,0 +1,55 @@ +require 'fiddle.so' +require 'fiddle/function' +require 'fiddle/closure' + +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 + 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/ruby_2_2/ext/fiddle/lib/fiddle/closure.rb b/ruby_2_2/ext/fiddle/lib/fiddle/closure.rb new file mode 100644 index 0000000000..beb90ecbe5 --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle/closure.rb @@ -0,0 +1,48 @@ +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/ruby_2_2/ext/fiddle/lib/fiddle/cparser.rb b/ruby_2_2/ext/fiddle/lib/fiddle/cparser.rb new file mode 100644 index 0000000000..43fb184a12 --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle/cparser.rb @@ -0,0 +1,176 @@ +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 increment(int)') + # #=> ["increment", Fiddle::TYPE_INT, [Fiddle::TYPE_INT]] + # + module CParser + # Parses a C struct's members + # + # Example: + # + # include Fiddle::CParser + # #=> Object + # + # parse_struct_signature(['int i', 'char c']) + # #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]] + # + def parse_struct_signature(signature, tymap=nil) + if( signature.is_a?(String) ) + signature = signature.split(/\s*,\s*/) + end + mems = [] + tys = [] + signature.each{|msig| + tks = msig.split(/\s+(\*)?/) + ty = tks[0..-2].join(" ") + member = tks[-1] + + case ty + when /\[(\d+)\]/ + n = $1.to_i + ty.gsub!(/\s*\[\d+\]/,"") + ty = [ty, n] + when /\[\]/ + ty.gsub!(/\s*\[\]/, "*") + end + + case member + when /\[(\d+)\]/ + ty = [ty, $1.to_i] + member.gsub!(/\s*\[\d+\]/,"") + when /\[\]/ + ty = ty + "*" + member.gsub!(/\s*\[\]/, "") + end + + mems.push(member) + tys.push(parse_ctype(ty,tymap)) + } + 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: + # + # include Fiddle::CParser + # #=> Object + # + # parse_signature('double sum(double, double)') + # #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]] + # + def parse_signature(signature, tymap=nil) + tymap ||= {} + signature = signature.gsub(/\s+/, " ").strip + case signature + when /^([\w@\*\s]+)\(([\w\*\s\,\[\]]*)\)$/ + ret = $1 + (args = $2).strip! + ret = ret.split(/\s+/) + args = args.split(/\s*,\s*/) + func = ret.pop + if( func =~ /^\*/ ) + func.gsub!(/^\*+/,"") + ret.push("*") + end + ret = ret.join(" ") + return [func, parse_ctype(ret, tymap), 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: + # + # include Fiddle::CParser + # #=> Object + # + # parse_ctype('int') + # #=> Fiddle::TYPE_INT + # + # parse_ctype('double') + # #=> Fiddle::TYPE_DOUBLE + # + # parse_ctype('unsigned char') + # #=> -Fiddle::TYPE_CHAR + # + def parse_ctype(ty, tymap=nil) + tymap ||= {} + case ty + when Array + return [parse_ctype(ty[0], tymap), ty[1]] + when "void" + return TYPE_VOID + when "char" + return TYPE_CHAR + when "unsigned char" + return -TYPE_CHAR + when "short" + return TYPE_SHORT + when "unsigned short" + return -TYPE_SHORT + when "int" + return TYPE_INT + when "unsigned int", 'uint' + return -TYPE_INT + when "long" + return TYPE_LONG + when "unsigned long" + return -TYPE_LONG + when "long long" + if( defined?(TYPE_LONG_LONG) ) + return TYPE_LONG_LONG + else + raise(RuntimeError, "unsupported type: #{ty}") + end + when "unsigned long long" + if( defined?(TYPE_LONG_LONG) ) + return -TYPE_LONG_LONG + else + raise(RuntimeError, "unsupported type: #{ty}") + end + when "float" + return TYPE_FLOAT + when "double" + return TYPE_DOUBLE + when "size_t" + return TYPE_SIZE_T + when "ssize_t" + return TYPE_SSIZE_T + when "ptrdiff_t" + return TYPE_PTRDIFF_T + when "intptr_t" + return TYPE_INTPTR_T + when "uintptr_t" + return TYPE_UINTPTR_T + when /\*/, /\[\s*\]/ + return TYPE_VOIDP + else + if( tymap[ty] ) + return parse_ctype(tymap[ty], tymap) + else + raise(DLError, "unknown type: #{ty}") + end + end + end + end +end diff --git a/ruby_2_2/ext/fiddle/lib/fiddle/function.rb b/ruby_2_2/ext/fiddle/lib/fiddle/function.rb new file mode 100644 index 0000000000..ab7496e944 --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle/function.rb @@ -0,0 +1,17 @@ +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 + + # The integer memory location of this function + def to_i + ptr.to_i + end + end +end diff --git a/ruby_2_2/ext/fiddle/lib/fiddle/import.rb b/ruby_2_2/ext/fiddle/lib/fiddle/import.rb new file mode 100644 index 0000000000..34f5d7f81c --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle/import.rb @@ -0,0 +1,314 @@ +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 + + # 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 + begin + Fiddle.dlopen(lib) + rescue DLError + raise(DLError, "can't load #{lib}") + end + 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_LONG_LONG + return SIZEOF_LONG_LONG + when TYPE_FLOAT + return SIZEOF_FLOAT + when TYPE_DOUBLE + return SIZEOF_DOUBLE + when TYPE_VOIDP + return SIZEOF_VOIDP + else + raise(DLError, "unknown type: #{ty}") + 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::STDCALL rescue 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 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/ruby_2_2/ext/fiddle/lib/fiddle/pack.rb b/ruby_2_2/ext/fiddle/lib/fiddle/pack.rb new file mode 100644 index 0000000000..e4e9542cc0 --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle/pack.rb @@ -0,0 +1,128 @@ +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 => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "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 + 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) + when SIZEOF_LONG_LONG + ary.pack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + def unpack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.join().unpack(@template) + when SIZEOF_LONG_LONG + ary.join().unpack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + private + + def parse_types(types) + @template = "" + 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/ruby_2_2/ext/fiddle/lib/fiddle/struct.rb b/ruby_2_2/ext/fiddle/lib/fiddle/struct.rb new file mode 100644 index 0000000000..695a4d2247 --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle/struct.rb @@ -0,0 +1,243 @@ +require 'fiddle' +require 'fiddle/value' +require 'fiddle/pack' + +module Fiddle + # C struct shell + class CStruct + # accessor to Fiddle::CStructEntity + def CStruct.entity_class + CStructEntity + end + end + + # C union shell + class CUnion + # accessor to Fiddle::CUnionEntity + def CUnion.entity_class + CUnionEntity + 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. + # + # Example: + # + # 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) + # + # obj = MyStruct.allocate + # + def create(klass, types, members) + new_class = Class.new(klass){ + define_method(:initialize){|addr| + @entity = klass.entity_class.new(addr, types) + @entity.assign_names(members) + } + define_method(:to_ptr){ @entity } + define_method(:to_i){ @entity.to_i } + members.each{|name| + define_method(name){ @entity[name] } + define_method(name + "="){|val| @entity[name] = val } + } + } + size = klass.entity_class.size(types) + new_class.module_eval(<<-EOS, __FILE__, __LINE__+1) + def new_class.size() + #{size} + end + def new_class.malloc() + addr = Fiddle.malloc(#{size}) + new(addr) + end + EOS + return new_class + end + module_function :create + end + + # A C struct wrapper + class CStructEntity < Fiddle::Pointer + include PackInfo + include ValueUtil + + # Allocates a C struct with the +types+ provided. + # + # When the instance is garbage collected, the C function +func+ is called. + def CStructEntity.malloc(types, func = nil) + addr = Fiddle.malloc(CStructEntity.size(types)) + CStructEntity.new(addr, types, func) + 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 + + align = PackInfo::ALIGN_MAP[type] + offset = PackInfo.align(last_offset, align) + + (PackInfo::SIZE_MAP[type] * 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) + set_ctypes(types) + super(addr, @size, func) + end + + # Set the names of the +members+ in this C struct + def assign_names(members) + @members = members + 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 + align = ALIGN_MAP[type] + offset = PackInfo.align(orig_offset, align) + + @offset << offset + + offset += (SIZE_MAP[type] * count) + + align + }.max + + @size = PackInfo.align(offset, max_align) + end + + # Fetch struct member +name+ + def [](name) + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + if( ty.is_a?(Array) ) + r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) + 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 val.collect{|v| unsigned_value(v,ty[0])} + else + return val + end + end + + # Set struct member +name+, to value +val+ + def []=(name, val) + 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 + + def to_s() # :nodoc: + super(@size) + end + end + + # A C union wrapper + class CUnionEntity < CStructEntity + include PackInfo + + # Allocates a C union the +types+ provided. + # + # When the instance is garbage collected, the C function +func+ is called. + def CUnionEntity.malloc(types, func=nil) + addr = Fiddle.malloc(CUnionEntity.size(types)) + CUnionEntity.new(addr, types, func) + end + + # 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| + PackInfo::SIZE_MAP[type] * count + }.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/ruby_2_2/ext/fiddle/lib/fiddle/types.rb b/ruby_2_2/ext/fiddle/lib/fiddle/types.rb new file mode 100644 index 0000000000..02c1d25a37 --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle/types.rb @@ -0,0 +1,71 @@ +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: + m.module_eval{ + typealias "DWORD", "unsigned long" + typealias "PDWORD", "unsigned long *" + typealias "DWORD32", "unsigned long" + typealias "DWORD64", "unsigned long long" + typealias "WORD", "unsigned short" + typealias "PWORD", "unsigned short *" + typealias "BOOL", "int" + typealias "ATOM", "int" + typealias "BYTE", "unsigned char" + typealias "PBYTE", "unsigned char *" + typealias "UINT", "unsigned int" + typealias "ULONG", "unsigned long" + typealias "UCHAR", "unsigned char" + typealias "HANDLE", "uintptr_t" + typealias "PHANDLE", "void*" + typealias "PVOID", "void*" + typealias "LPCSTR", "char*" + typealias "LPSTR", "char*" + typealias "HINSTANCE", "unsigned int" + typealias "HDC", "unsigned int" + typealias "HWND", "unsigned int" + } + 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/ruby_2_2/ext/fiddle/lib/fiddle/value.rb b/ruby_2_2/ext/fiddle/lib/fiddle/value.rb new file mode 100644 index 0000000000..8d71e47ce6 --- /dev/null +++ b/ruby_2_2/ext/fiddle/lib/fiddle/value.rb @@ -0,0 +1,112 @@ +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] + when TYPE_LONG_LONG + [val].pack("q").unpack("Q")[0] + else + val + 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] + when TYPE_LONG_LONG + [val].pack("Q").unpack("q")[0] + else + val + 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] + when SIZEOF_LONG_LONG + return [arg].pack("p").unpack("q")[0] + else + raise(RuntimeError, "sizeof(void*)?") + 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/ruby_2_2/ext/fiddle/pointer.c b/ruby_2_2/ext/fiddle/pointer.c new file mode 100644 index 0000000000..a09daf77ea --- /dev/null +++ b/ruby_2_2/ext/fiddle/pointer.c @@ -0,0 +1,721 @@ +/* -*- C -*- + * $Id$ + */ + +#include <ruby/ruby.h> +#include <ruby/io.h> +#include <ctype.h> +#include <fiddle.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 rb_cPointer; + +typedef void (*freefunc_t)(void*); + +struct ptr_data { + void *ptr; + long size; + freefunc_t free; + 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(void *ptr) +{ + struct ptr_data *data = ptr; + if (data->ptr) { + if (data->free) { + (*(data->free))(data->ptr); + } + } + xfree(ptr); +} + +static size_t +fiddle_ptr_memsize(const void *ptr) +{ + const struct ptr_data *data = ptr; + return data ? sizeof(*data) + data->size : 0; +} + +static const rb_data_type_t fiddle_ptr_data_type = { + "fiddle/pointer", + {fiddle_ptr_mark, fiddle_ptr_free, fiddle_ptr_memsize,}, +}; + +static VALUE +rb_fiddle_ptr_new2(VALUE klass, void *ptr, long size, freefunc_t func) +{ + 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->size = size; + OBJ_TAINT(val); + + return val; +} + +static VALUE +rb_fiddle_ptr_new(void *ptr, long size, freefunc_t func) +{ + return rb_fiddle_ptr_new2(rb_cPointer, ptr, size, func); +} + +static VALUE +rb_fiddle_ptr_malloc(long size, freefunc_t func) +{ + void *ptr; + + ptr = ruby_xmalloc((size_t)size); + memset(ptr,0,(size_t)size); + return rb_fiddle_ptr_new(ptr, size, func); +} + +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; + + 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; +} + +/* + * call-seq: + * + * Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance + * + * Allocate +size+ bytes of memory and associate it with an optional + * +freefunc+ that will be called when the pointer is garbage collected. + * + * +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(s,f); + if (wrap) RPTR_DATA(obj)->wrap[1] = wrap; + + 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: + * + * 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_tainted_str_new2((char*)(data->ptr)); + break; + case 1: + len = NUM2INT(arg1); + val = rb_tainted_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_tainted_str_new((char*)(data->ptr),data->size); + break; + case 1: + len = NUM2INT(arg1); + val = rb_tainted_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), data, data->ptr, data->size, 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_eFiddleError, "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_tainted_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_eFiddleError, "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); + 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_eFiddleError, "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); + } + OBJ_INFECT(ptr, val); + if (wrap) RPTR_DATA(ptr)->wrap[0] = wrap; + return ptr; +} + +void +Init_fiddle_pointer(void) +{ + 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, "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); + + /* Document-const: NULL + * + * A NULL pointer + */ + rb_define_const(mFiddle, "NULL", rb_fiddle_ptr_new(0, 0, 0)); +} diff --git a/ruby_2_2/ext/fiddle/win32/fficonfig.h b/ruby_2_2/ext/fiddle/win32/fficonfig.h new file mode 100755 index 0000000000..776808159c --- /dev/null +++ b/ruby_2_2/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/ruby_2_2/ext/fiddle/win32/libffi-3.2.1-mswin.patch b/ruby_2_2/ext/fiddle/win32/libffi-3.2.1-mswin.patch new file mode 100644 index 0000000000..782cf41e36 --- /dev/null +++ b/ruby_2_2/ext/fiddle/win32/libffi-3.2.1-mswin.patch @@ -0,0 +1,132 @@ +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-22 16:00:42.000000000 +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 +@@ -599,11 +601,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 +646,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) \ +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 --git a/ruby_2_2/ext/fiddle/win32/libffi-config.rb b/ruby_2_2/ext/fiddle/win32/libffi-config.rb new file mode 100755 index 0000000000..ecd3b086c0 --- /dev/null +++ b/ruby_2_2/ext/fiddle/win32/libffi-config.rb @@ -0,0 +1,47 @@ +#!/usr/bin/ruby +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/ruby_2_2/ext/fiddle/win32/libffi.mk.tmpl b/ruby_2_2/ext/fiddle/win32/libffi.mk.tmpl new file mode 100755 index 0000000000..2a16e8efec --- /dev/null +++ b/ruby_2_2/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 |