1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* ext/fiddle/handle.c: Make Fiddle independent of DL, copy DL::Handle

to Fiddle::Handle.
* ext/fiddle/pointer.c: Make Fiddle independent of DL, copy
  DL::Pointer to Fiddle::Pointer.
* test/fiddle/test_func.rb: relevent tests
* test/fiddle/test_handle.rb: ditto
* test/fiddle/test_pointer.rb: ditto
* ext/dl/lib/dl/struct.rb: use Fiddle::Pointer if available
* ext/fiddle/extconf.rb: check for dlfcn.h
* ext/fiddle/fiddle.c: add constants for sizeof() things
* ext/fiddle/fiddle.h: include dlfcn.h
* ext/fiddle/function.c: expose a C function for creating new
  Fiddle::Function objects.
* ext/fiddle/lib/fiddle.rb: include constants for dl backwards compat
* ext/fiddle/lib/fiddle/function.rb: read the pointer from the
  function for dl backwards compat.
* test/dl/test_callback.rb: check the addresses of the pointers rather
  than their types.
* test/fiddle/helper.rb: remove dependency on dl
* test/fiddle/test_closure.rb: ditto
* test/fiddle/test_fiddle.rb: ditto

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37907 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
tenderlove 2012-11-27 19:54:50 +00:00
parent c8d3536852
commit c1fb6a82dc
17 changed files with 1949 additions and 33 deletions

View file

@ -1,3 +1,27 @@
Wed Nov 28 04:53:40 2012 Aaron Patterson <aaron@tenderlovemaking.com>
* ext/fiddle/handle.c: Make Fiddle independent of DL, copy DL::Handle
to Fiddle::Handle.
* ext/fiddle/pointer.c: Make Fiddle independent of DL, copy
DL::Pointer to Fiddle::Pointer.
* test/fiddle/test_func.rb: relevent tests
* test/fiddle/test_handle.rb: ditto
* test/fiddle/test_pointer.rb: ditto
* ext/dl/lib/dl/struct.rb: use Fiddle::Pointer if available
* ext/fiddle/extconf.rb: check for dlfcn.h
* ext/fiddle/fiddle.c: add constants for sizeof() things
* ext/fiddle/fiddle.h: include dlfcn.h
* ext/fiddle/function.c: expose a C function for creating new
Fiddle::Function objects.
* ext/fiddle/lib/fiddle.rb: include constants for dl backwards compat
* ext/fiddle/lib/fiddle/function.rb: read the pointer from the
function for dl backwards compat.
* test/dl/test_callback.rb: check the addresses of the pointers rather
than their types.
* test/fiddle/helper.rb: remove dependency on dl
* test/fiddle/test_closure.rb: ditto
* test/fiddle/test_fiddle.rb: ditto
Wed Nov 28 03:03:28 2012 NARUSE, Yui <naruse@ruby-lang.org>
* configure.in (opt-dir): don't use non portable flag -E of sed.

View file

@ -76,7 +76,7 @@ module DL
end
# A C struct wrapper
class CStructEntity < CPtr
class CStructEntity < (DL.fiddle? ? Fiddle::Pointer : CPtr)
include PackInfo
include ValueUtil

View file

@ -19,6 +19,20 @@ 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
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|

View file

@ -1,6 +1,7 @@
#include <fiddle.h>
VALUE mFiddle;
VALUE rb_eFiddleError;
#ifndef TYPE_SSIZE_T
# if SIZEOF_SIZE_T == SIZEOF_INT
@ -34,6 +35,100 @@ VALUE mFiddle;
#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;
rb_secure(4);
ptr = (void*)ruby_xmalloc(NUM2INT(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);
rb_secure(4);
ptr = (void*)ruby_xrealloc(ptr, NUM2INT(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);
rb_secure(4);
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)
{
rb_secure(4);
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)
{
@ -47,6 +142,13 @@ Init_fiddle(void)
*/
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
@ -143,7 +245,101 @@ Init_fiddle(void)
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));
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: */

View file

@ -12,6 +12,30 @@
#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
@ -98,6 +122,9 @@
#define TYPE_DOUBLE 8
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: */

View file

@ -38,6 +38,18 @@ allocate(VALUE klass)
return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
}
VALUE
rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type)
{
VALUE argv[3];
argv[0] = address;
argv[1] = arg_types;
argv[2] = ret_type;
return rb_class_new_instance(3, argv, cFiddleFunction);
}
static VALUE
initialize(int argc, VALUE argv[], VALUE self)
{

465
ext/fiddle/handle.c Normal file
View file

@ -0,0 +1,465 @@
#include <ruby.h>
#include <fiddle.h>
VALUE rb_cHandle;
struct dl_handle {
void *ptr;
int open;
int enable_close;
};
#ifdef _WIN32
# ifndef _WIN32_WCE
static void *
w32_coredll(void)
{
MEMORY_BASIC_INFORMATION m;
memset(&m, 0, sizeof(m));
if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL;
return m.AllocationBase;
}
# endif
static int
w32_dlclose(void *ptr)
{
# ifndef _WIN32_WCE
if( ptr == w32_coredll() ) return 0;
# endif
if( FreeLibrary((HMODULE)ptr) ) return 0;
return errno = rb_w32_map_errno(GetLastError());
}
#define dlclose(ptr) w32_dlclose(ptr)
#endif
static void
fiddle_handle_free(void *ptr)
{
struct dl_handle *fiddle_handle = ptr;
if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
dlclose(fiddle_handle->ptr);
}
}
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 Fiddle::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:
* initialize(lib = nil, flags = Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
*
* Create a new handler that opens library named +lib+ with +flags+. If no
* library is specified, RTLD_DEFAULT is used.
*/
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 : StringValuePtr(lib);
cflag = RTLD_LAZY | RTLD_GLOBAL;
break;
case 2:
clib = NIL_P(lib) ? NULL : StringValuePtr(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
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 Fiddle::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 Fiddle::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 Fiddle::Handle is
* garbage collected.
*/
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, const char *symbol);
/*
* Document-method: sym
* Document-method: []
*
* 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, StringValueCStr(sym));
}
#ifndef RTLD_NEXT
#define RTLD_NEXT NULL
#endif
#ifndef RTLD_DEFAULT
#define RTLD_DEFAULT NULL
#endif
/*
* Document-method: sym
* Document-method: []
*
* 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, StringValueCStr(sym));
}
static VALUE
fiddle_handle_sym(void *handle, const char *name)
{
#if defined(HAVE_DLERROR)
const char *err;
# define CHECK_DLERROR if( err = dlerror() ){ func = 0; }
#else
# define CHECK_DLERROR
#endif
void (*func)();
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 \"%s\"", name);
}
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>
*
* === 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 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: */

View file

@ -1,13 +1,8 @@
require 'fiddle.so'
require 'fiddle/function'
require 'fiddle/closure'
require 'dl' unless Object.const_defined?(:DL)
module Fiddle
# A reference to DL::CPtr
Pointer = DL::CPtr
if WINDOWS
# Returns the last win32 +Error+ of the current executing +Thread+ or nil
# if none
@ -31,4 +26,15 @@ module Fiddle
Thread.current[:__DL2_LAST_ERROR__] = error
Thread.current[:__FIDDLE_LAST_ERROR__] = error
end
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

View file

@ -2,5 +2,12 @@ module Fiddle
class Function
# The ABI of the Function.
attr_reader :abi
# The address of this function
attr_reader :ptr
def to_i
ptr.to_i
end
end
end

691
ext/fiddle/pointer.c Normal file
View file

@ -0,0 +1,691 @@
/* -*- C -*-
* $Id$
*/
#include <ruby/ruby.h>
#include <ruby/io.h>
#include <ctype.h>
#include <fiddle.h>
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);
}
}
}
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;
rb_secure(4);
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;
rb_secure(4);
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, "DL::PtrData was expected");
}
return ptr;
}
static VALUE
rb_fiddle_ptr_s_allocate(VALUE klass)
{
VALUE obj;
struct ptr_data *data;
rb_secure(4);
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
* Fiddle::Pointer.new(address, size) => fiddle_cptr
* Fiddle::Pointer.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 DL::CPtr.
*/
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 CPtr 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 DL::CPtr that is a dereferenced pointer for this DL::CPtr.
* 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 DL::CPtr that is a reference pointer for this DL::CPtr.
* 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 the DL::CFunc in +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
*
* Get the free function for this pointer. Returns Fiddle::Function.
*/
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.
*/
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.
*/
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 DL::CPtr
*/
static VALUE
rb_fiddle_ptr_inspect(VALUE self)
{
struct ptr_data *data;
char str[1024];
TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
snprintf(str, 1023, "#<%s:%p ptr=%p size=%ld free=%p>",
rb_class2name(CLASS_OF(self)), data, data->ptr, data->size, data->free);
return rb_str_new2(str);
}
/*
* 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 DL::CPtr 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 DL::CPtr 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 _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.to_ptr(val) => cptr
* Fiddle::Pointer[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));
}

View file

@ -25,7 +25,7 @@ module DL
func = CFunc.new(addr, TYPE_VOIDP, 'test')
f = Function.new(func, [TYPE_VOIDP])
ptr = CPtr['blah']
assert_equal ptr, f.call(ptr)
assert_equal ptr.to_i, f.call(ptr).to_i
end
def test_callback_return_arbitrary

View file

@ -1,5 +1,4 @@
require 'minitest/autorun'
require 'dl'
require 'fiddle'
# FIXME: this is stolen from DL and needs to be refactored.
@ -104,8 +103,8 @@ Fiddle::LIBM_SO = libm_so
module Fiddle
class TestCase < MiniTest::Unit::TestCase
def setup
@libc = DL.dlopen(LIBC_SO)
@libm = DL.dlopen(LIBM_SO)
@libc = Fiddle.dlopen(LIBC_SO)
@libm = Fiddle.dlopen(LIBM_SO)
end
end
end

View file

@ -57,8 +57,8 @@ module Fiddle
end
%w[INT SHORT CHAR LONG LONG_LONG].each do |name|
type = DL.const_get("TYPE_#{name}") rescue next
size = DL.const_get("SIZEOF_#{name}")
type = Fiddle.const_get("TYPE_#{name}") rescue next
size = Fiddle.const_get("SIZEOF_#{name}")
[[type, size-1, name], [-type, size, "unsigned_"+name]].each do |t, s, n|
define_method("test_conversion_#{n.downcase}") do
arg = nil

View file

@ -4,27 +4,6 @@ rescue LoadError
end
class TestFiddle < Fiddle::TestCase
def test_constants_match
[
:TYPE_VOID,
:TYPE_VOIDP,
:TYPE_CHAR,
:TYPE_SHORT,
:TYPE_INT,
:TYPE_LONG,
:TYPE_LONG_LONG,
:TYPE_FLOAT,
:TYPE_DOUBLE,
:TYPE_SIZE_T,
:TYPE_SSIZE_T,
:TYPE_PTRDIFF_T,
:TYPE_INTPTR_T,
:TYPE_UINTPTR_T,
].each do |name|
assert_equal(DL.const_get(name), Fiddle.const_get(name), "Fiddle::#{name}")
end
end
def test_windows_constant
require 'rbconfig'
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/

79
test/fiddle/test_func.rb Normal file
View file

@ -0,0 +1,79 @@
require_relative 'helper'
module Fiddle
class TestFunc < TestCase
def test_random
f = Function.new(@libc['srand'], [-TYPE_LONG], TYPE_VOID)
assert_nil f.call(10)
end
def test_sinf
begin
f = Function.new(@libm['sinf'], [TYPE_FLOAT], TYPE_FLOAT)
rescue Fiddle::DLError
skip "libm may not have sinf()"
end
assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
end
def test_sin
f = Function.new(@libm['sin'], [TYPE_DOUBLE], TYPE_DOUBLE)
assert_in_delta 1.0, f.call(90 * Math::PI / 180), 0.0001
end
def test_string
stress, GC.stress = GC.stress, true
f = Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
buff = "000"
str = f.call(buff, "123")
assert_equal("123", buff)
assert_equal("123", str.to_s)
ensure
GC.stress = stress
end
def test_isdigit
f = Function.new(@libc['isdigit'], [TYPE_INT], TYPE_INT)
r1 = f.call(?1.ord)
r2 = f.call(?2.ord)
rr = f.call(?r.ord)
assert_operator r1, :>, 0
assert_operator r2, :>, 0
assert_equal 0, rr
end
def test_atof
f = Function.new(@libc['atof'], [TYPE_VOIDP], TYPE_DOUBLE)
r = f.call("12.34")
assert_includes(12.00..13.00, r)
end
def test_strtod
f = Function.new(@libc['strtod'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_DOUBLE)
buff1 = Pointer["12.34"]
buff2 = buff1 + 4
r = f.call(buff1, - buff2)
assert_in_delta(12.34, r, 0.001)
end
def test_qsort1
cb = Class.new(Closure) {
def call(x, y)
Pointer.new(x)[0] <=> Pointer.new(y)[0]
end
}.new(TYPE_INT, [TYPE_VOIDP, TYPE_VOIDP])
qsort = Function.new(@libc['qsort'],
[TYPE_VOIDP, TYPE_SIZE_T, TYPE_SIZE_T, TYPE_VOIDP],
TYPE_VOID)
buff = "9341"
qsort.call(buff, buff.size, 1, cb)
assert_equal("1349", buff)
bug4929 = '[ruby-core:37395]'
buff = "9341"
EnvUtil.under_gc_stress {qsort.call(buff, buff.size, 1, cb)}
assert_equal("1349", buff, bug4929)
end
end
end

186
test/fiddle/test_handle.rb Normal file
View file

@ -0,0 +1,186 @@
require_relative 'helper'
module Fiddle
class TestHandle < TestCase
include Fiddle
def test_to_i
handle = Fiddle::Handle.new(LIBC_SO)
assert_kind_of Integer, handle.to_i
end
def test_static_sym_secure
assert_raises(SecurityError) do
Thread.new do
$SAFE = 2
Fiddle::Handle.sym('calloc')
end.join
end
end
def test_static_sym_unknown
assert_raises(DLError) { Fiddle::Handle.sym('fooo') }
assert_raises(DLError) { Fiddle::Handle['fooo'] }
end
def test_static_sym
skip "Fiddle::Handle.sym is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
begin
# Linux / Darwin / FreeBSD
refute_nil Fiddle::Handle.sym('dlopen')
assert_equal Fiddle::Handle.sym('dlopen'), Fiddle::Handle['dlopen']
rescue
# NetBSD
require 'objspace'
refute_nil Fiddle::Handle.sym('Init_objspace')
assert_equal Fiddle::Handle.sym('Init_objspace'), Fiddle::Handle['Init_objspace']
end
end
def test_sym_closed_handle
handle = Fiddle::Handle.new(LIBC_SO)
handle.close
assert_raises(DLError) { handle.sym("calloc") }
assert_raises(DLError) { handle["calloc"] }
end
def test_sym_unknown
handle = Fiddle::Handle.new(LIBC_SO)
assert_raises(DLError) { handle.sym('fooo') }
assert_raises(DLError) { handle['fooo'] }
end
def test_sym_with_bad_args
handle = Handle.new(LIBC_SO)
assert_raises(TypeError) { handle.sym(nil) }
assert_raises(TypeError) { handle[nil] }
end
def test_sym_secure
assert_raises(SecurityError) do
Thread.new do
$SAFE = 2
handle = Handle.new(LIBC_SO)
handle.sym('calloc')
end.join
end
end
def test_sym
handle = Handle.new(LIBC_SO)
refute_nil handle.sym('calloc')
refute_nil handle['calloc']
end
def test_handle_close
handle = Handle.new(LIBC_SO)
assert_equal 0, handle.close
end
def test_handle_close_twice
handle = Handle.new(LIBC_SO)
handle.close
assert_raises(DLError) do
handle.close
end
end
def test_dlopen_returns_handle
assert_instance_of Handle, dlopen(LIBC_SO)
end
def test_dlopen_safe
assert_raises(SecurityError) do
Thread.new do
$SAFE = 2
dlopen(LIBC_SO)
end.join
end
end
def test_initialize_safe
assert_raises(SecurityError) do
Thread.new do
$SAFE = 2
Handle.new(LIBC_SO)
end.join
end
end
def test_initialize_noargs
handle = Handle.new
refute_nil handle['rb_str_new']
end
def test_initialize_flags
handle = Handle.new(LIBC_SO, RTLD_LAZY | RTLD_GLOBAL)
refute_nil handle['calloc']
end
def test_enable_close
handle = Handle.new(LIBC_SO)
assert !handle.close_enabled?, 'close is enabled'
handle.enable_close
assert handle.close_enabled?, 'close is not enabled'
end
def test_disable_close
handle = Handle.new(LIBC_SO)
handle.enable_close
assert handle.close_enabled?, 'close is enabled'
handle.disable_close
assert !handle.close_enabled?, 'close is enabled'
end
def test_NEXT
begin
# Linux / Darwin
#
# There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find
# the first occurrence of the desired symbol using the default library search order. The
# latter will find the next occurrence of a function in the search order after the current
# library. This allows one to provide a wrapper around a function in another shared
# library.
# --- Ubuntu Linux 8.04 dlsym(3)
handle = Handle::NEXT
refute_nil handle['malloc']
rescue
# BSD
#
# If dlsym() is called with the special handle RTLD_NEXT, then the search
# for the symbol is limited to the shared objects which were loaded after
# the one issuing the call to dlsym(). Thus, if the function is called
# from the main program, all the shared libraries are searched. If it is
# called from a shared library, all subsequent shared libraries are
# searched. RTLD_NEXT is useful for implementing wrappers around library
# functions. For example, a wrapper function getpid() could access the
# "real" getpid() with dlsym(RTLD_NEXT, "getpid"). (Actually, the dlfunc()
# interface, below, should be used, since getpid() is a function and not a
# data object.)
# --- FreeBSD 8.0 dlsym(3)
require 'objspace'
handle = Handle::NEXT
refute_nil handle['Init_objspace']
end
end unless /mswin|mingw/ =~ RUBY_PLATFORM
def test_DEFAULT
skip "Handle::DEFAULT is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
handle = Handle::DEFAULT
refute_nil handle['malloc']
end unless /mswin|mingw/ =~ RUBY_PLATFORM
def test_dlerror
# FreeBSD (at least 7.2 to 7.2) calls nsdispatch(3) when it calls
# getaddrinfo(3). And nsdispatch(3) doesn't call dlerror(3) even if
# it calls _nss_cache_cycle_prevention_function with dlsym(3).
# So our Fiddle::Handle#sym must call dlerror(3) before call dlsym.
# In general uses of dlerror(3) should call it before use it.
require 'socket'
Socket.gethostbyname("localhost")
Fiddle.dlopen("/usr/lib/libc.so").sym('strcpy')
end if /freebsd/=~ RUBY_PLATFORM
end
end

231
test/fiddle/test_pointer.rb Normal file
View file

@ -0,0 +1,231 @@
require_relative 'helper'
require_relative '../ruby/envutil'
module Fiddle
class TestPointer < TestCase
def dlwrap arg
Fiddle.dlwrap arg
end
include Test::Unit::Assertions
def test_cptr_to_int
null = Fiddle::NULL
assert_equal(null.to_i, null.to_int)
end
def test_malloc_free_func_int
free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
assert_equal free.to_i, Fiddle::RUBY_FREE.to_i
ptr = Pointer.malloc(10, free.to_i)
assert_equal 10, ptr.size
assert_equal free.to_i, ptr.free.to_i
end
def test_malloc_free_func
free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
ptr = Pointer.malloc(10, free)
assert_equal 10, ptr.size
assert_equal free.to_i, ptr.free.to_i
end
def test_to_str
str = "hello world"
ptr = Pointer[str]
assert_equal 3, ptr.to_str(3).length
assert_equal str, ptr.to_str
ptr[5] = 0
assert_equal "hello\0world", ptr.to_str
end
def test_to_s
str = "hello world"
ptr = Pointer[str]
assert_equal 3, ptr.to_s(3).length
assert_equal str, ptr.to_s
ptr[5] = 0
assert_equal 'hello', ptr.to_s
end
def test_minus
str = "hello world"
ptr = Pointer[str]
assert_equal ptr.to_s, (ptr + 3 - 3).to_s
end
# TODO: what if the pointer size is 0? raise an exception? do we care?
def test_plus
str = "hello world"
ptr = Pointer[str]
new_str = ptr + 3
assert_equal 'lo world', new_str.to_s
end
def test_inspect
ptr = Pointer.new(0)
inspect = ptr.inspect
assert_match(/size=#{ptr.size}/, inspect)
assert_match(/free=#{sprintf("%#x", ptr.free.to_i)}/, inspect)
assert_match(/ptr=#{sprintf("%#x", ptr.to_i)}/, inspect)
end
def test_to_ptr_string
str = "hello world"
ptr = Pointer[str]
assert ptr.tainted?, 'pointer should be tainted'
assert_equal str.length, ptr.size
assert_equal 'hello', ptr[0,5]
end
def test_to_ptr_io
buf = Pointer.malloc(10)
File.open(__FILE__, 'r') do |f|
ptr = Pointer.to_ptr f
fread = Function.new(@libc['fread'],
[TYPE_VOIDP, TYPE_INT, TYPE_INT, TYPE_VOIDP],
TYPE_INT)
fread.call(buf.to_i, Fiddle::SIZEOF_CHAR, buf.size - 1, ptr.to_i)
end
File.open(__FILE__, 'r') do |f|
assert_equal f.read(9), buf.to_s
end
end
def test_to_ptr_with_ptr
ptr = Pointer.new 0
ptr2 = Pointer.to_ptr Struct.new(:to_ptr).new(ptr)
assert_equal ptr, ptr2
assert_raises(Fiddle::DLError) do
Pointer.to_ptr Struct.new(:to_ptr).new(nil)
end
end
def test_to_ptr_with_num
ptr = Pointer.new 0
assert_equal ptr, Pointer[0]
end
def test_equals
ptr = Pointer.new 0
ptr2 = Pointer.new 0
assert_equal ptr2, ptr
end
def test_not_equals
ptr = Pointer.new 0
refute_equal 10, ptr, '10 should not equal the pointer'
end
def test_cmp
ptr = Pointer.new 0
assert_nil(ptr <=> 10, '10 should not be comparable')
end
def test_ref_ptr
ary = [0,1,2,4,5]
addr = Pointer.new(dlwrap(ary))
assert_equal addr.to_i, addr.ref.ptr.to_i
assert_equal addr.to_i, (+ (- addr)).to_i
end
def test_to_value
ary = [0,1,2,4,5]
addr = Pointer.new(dlwrap(ary))
assert_equal ary, addr.to_value
end
def test_free
ptr = Pointer.malloc(4)
assert_nil ptr.free
end
def test_free=
assert_normal_exit(<<-"End", '[ruby-dev:39269]')
require 'fiddle'
Fiddle::LIBC_SO = #{Fiddle::LIBC_SO.dump}
Fiddle::LIBM_SO = #{Fiddle::LIBM_SO.dump}
include Fiddle
@libc = dlopen(LIBC_SO)
@libm = dlopen(LIBM_SO)
free = Fiddle::Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
ptr = Fiddle::Pointer.malloc(4)
ptr.free = free
free.ptr
ptr.free.ptr
End
free = Function.new(Fiddle::RUBY_FREE, [TYPE_VOIDP], TYPE_VOID)
ptr = Pointer.malloc(4)
ptr.free = free
assert_equal free.ptr, ptr.free.ptr
end
def test_null?
ptr = Pointer.new(0)
assert ptr.null?
end
def test_size
ptr = Pointer.malloc(4)
assert_equal 4, ptr.size
Fiddle.free ptr.to_i
end
def test_size=
ptr = Pointer.malloc(4)
ptr.size = 10
assert_equal 10, ptr.size
Fiddle.free ptr.to_i
end
def test_aref_aset
check = Proc.new{|str,ptr|
assert_equal(str.size(), ptr.size())
assert_equal(str, ptr.to_s())
assert_equal(str[0,2], ptr.to_s(2))
assert_equal(str[0,2], ptr[0,2])
assert_equal(str[1,2], ptr[1,2])
assert_equal(str[1,0], ptr[1,0])
assert_equal(str[0].ord, ptr[0])
assert_equal(str[1].ord, ptr[1])
}
str = 'abc'
ptr = Pointer[str]
check.call(str, ptr)
str[0] = "c"
assert_equal 'c'.ord, ptr[0] = "c".ord
check.call(str, ptr)
str[0,2] = "aa"
assert_equal 'aa', ptr[0,2] = "aa"
check.call(str, ptr)
ptr2 = Pointer['cdeeee']
str[0,2] = "cd"
assert_equal ptr2, ptr[0,2] = ptr2
check.call(str, ptr)
ptr3 = Pointer['vvvv']
str[0,2] = "vv"
assert_equal ptr3.to_i, ptr[0,2] = ptr3.to_i
check.call(str, ptr)
end
def test_null_pointer
nullpo = Pointer.new(0)
assert_raise(DLError) {nullpo[0]}
assert_raise(DLError) {nullpo[0] = 1}
end
end
end