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:
parent
c8d3536852
commit
c1fb6a82dc
17 changed files with 1949 additions and 33 deletions
24
ChangeLog
24
ChangeLog
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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: */
|
||||
|
|
|
@ -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
465
ext/fiddle/handle.c
Normal 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: */
|
|
@ -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
|
||||
|
|
|
@ -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
691
ext/fiddle/pointer.c
Normal 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));
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
79
test/fiddle/test_func.rb
Normal 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
186
test/fiddle/test_handle.rb
Normal 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
231
test/fiddle/test_pointer.rb
Normal 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
|
Loading…
Reference in a new issue