summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/dl/dl.h23
-rw-r--r--ext/dl/extconf.rb13
-rw-r--r--ext/dl/sym.c124
3 files changed, 122 insertions, 38 deletions
diff --git a/ext/dl/dl.h b/ext/dl/dl.h
index a88e95d66f..ceee8c5384 100644
--- a/ext/dl/dl.h
+++ b/ext/dl/dl.h
@@ -142,8 +142,8 @@
#if defined(USE_INLINE_ASM)
# if defined(__i386__) && defined(__GNUC__)
-# define ASM_START(type)
-# define ASM_END(type)
+# define ASM_START(sym)
+# define ASM_END(sym)
# define ASM_PUSH_C(x) asm volatile ("pushl %0" :: "g" (x));
# define ASM_PUSH_H(x) asm volatile ("pushl %0" :: "g" (x));
# define ASM_PUSH_I(x) asm volatile ("pushl %0" :: "g" (x));
@@ -158,15 +158,16 @@
# else
# error --with-asm is not supported on this machine
# endif
-#else
-# define ASM_START(type)
-# define ASM_END(type)
-# define ASM_PUSH_C(x)
-# define ASM_PUSH_I(x)
-# define ASM_PUSH_L(x)
-# define ASM_PUSH_P(x)
-# define ASM_PUSH_F(x)
-# define ASM_PUSH_D(x)
+#elif defined(USE_DLSTACK)
+# define ASM_START(sym)
+# define ASM_END(sym)
+# define ASM_PUSH_C(x) memcpy(sp,&x,1); sp++;
+# define ASM_PUSH_H(x) memcpy(sp,&x,sizeof(short)); sp++;
+# define ASM_PUSH_I(x) memcpy(sp,&x,sizeof(int)); sp++;
+# define ASM_PUSH_L(x) memcpy(sp,&x,sizeof(long)); sp++;
+# define ASM_PUSH_P(x) memcpy(sp,&x,sizeof(void*)); sp++;
+# define ASM_PUSH_F(x) memcpy(sp,&x,sizeof(float)); sp+=sizeof(float)/sizeof(long);
+# define ASM_PUSH_D(x) memcpy(sp,&x,sizeof(double)); sp+=sizeof(double)/sizeof(long);
#endif
extern VALUE rb_mDL;
diff --git a/ext/dl/extconf.rb b/ext/dl/extconf.rb
index d119ed1d6d..6dc6abca4e 100644
--- a/ext/dl/extconf.rb
+++ b/ext/dl/extconf.rb
@@ -13,6 +13,7 @@ if( ARGV.include?("--help") )
--with-type-float strictly use type 'float'
--with-asm use the embedded assembler for passing arguments.
(this option is available for i386 machine now.)
+ --with-dlstack use a stack emulation for constructing function call. [experimental]
--with-args=<max_arg>,<max_cbarg>,<max_cbent>
<max_arg>: maximum number of arguments of the function
<max_cbarg>: maximum number of arguments of the callback
@@ -32,6 +33,7 @@ if (Config::CONFIG['CC'] =~ /gcc/) && (Config::CONFIG['arch'] =~ /i.86/)
else
$with_asm = false
end
+$with_dlstack = false
$with_type_int = try_run(<<EOF)
int main(){ return sizeof(int) == sizeof(long); }
@@ -63,10 +65,11 @@ $with_type_short = enable_config("type-short", $with_type_short)
$with_type_float = enable_config("type-float", $with_type_float)
$with_asm = enable_config("asm", $with_asm)
+$with_dlstack = enable_config("dlstack", $with_dlstack)
args = with_config("args")
max_arg = max_cbarg = max_cbent = nil
-if( $with_asm )
+if( $with_asm || $with_dlstack )
$with_type_char = true
$with_type_short = true
$with_type_float = true
@@ -106,8 +109,12 @@ def dlc_define(const)
"#endif\n"
end
-if( $with_asm )
- $dlconfig_h << "#define USE_INLINE_ASM\n"
+if( $with_dlstack )
+ $dlconfig_h << "#define USE_DLSTACK\n"
+else
+ if( $with_asm )
+ $dlconfig_h << "#define USE_INLINE_ASM\n"
+ end
end
if( $with_type_char )
$dlconfig_h << "#define WITH_TYPE_CHAR\n"
diff --git a/ext/dl/sym.c b/ext/dl/sym.c
index 7f9793fa51..996831bec2 100644
--- a/ext/dl/sym.c
+++ b/ext/dl/sym.c
@@ -101,7 +101,7 @@ rb_dlsym_new(void (*func)(), const char *name, const char *type)
data->name = name ? strdup(name) : NULL;
data->type = type ? strdup(type) : NULL;
data->len = type ? strlen(type) : 0;
-#ifndef USE_INLINE_ASM
+#if !(defined(USE_INLINE_ASM) || defined(USE_DLSTACK))
if( data->len - 1 > MAX_ARG ){
rb_raise(rb_eDLError, "maximum number of arguments is %d.", MAX_ARG);
};
@@ -268,6 +268,47 @@ rb_dlsym_inspect(VALUE self)
return val;
}
+static int
+stack_size(struct sym_data *sym)
+{
+ int i;
+ int size;
+
+ size = 0;
+ for( i=1; i < sym->len; i++ ){
+ switch(sym->type[i]){
+ case 'C':
+ case 'H':
+ case 'I':
+ case 'L':
+ size += sizeof(long);
+ break;
+ case 'F':
+ size += sizeof(float);
+ break;
+ case 'D':
+ size += sizeof(double);
+ break;
+ case 'c':
+ case 'h':
+ case 'i':
+ case 'l':
+ case 'f':
+ case 'd':
+ case 'p':
+ case 'P':
+ case 's':
+ case 'S':
+ case 'a':
+ case 'A':
+ size += sizeof(void*);
+ break;
+ default:
+ return -(sym->type[i]);
+ }
+ }
+ return size;
+}
VALUE
rb_dlsym_call(int argc, VALUE argv[], VALUE self)
@@ -478,9 +519,43 @@ rb_dlsym_call(int argc, VALUE argv[], VALUE self)
func = sym->func;
-#ifdef USE_INLINE_ASM
- ASM_START(sym->type);
- for( i = sym->len - 2; i >= 0; i-- ){
+#if defined(USE_INLINE_ASM) || defined(USE_DLSTACK)
+ {
+#if defined(USE_DLSTACK)
+#define DLSTACK_PROTO long,long,long,long,long,\
+ long,long,long,long,long,\
+ long,long,long,long,long
+#define DLSTACK_ARGS stack[0],stack[1],stack[2],stack[3],stack[4],\
+ stack[5],stack[6],stack[7],stack[8],stack[9],\
+ stack[10],stack[11],stack[12],stack[13],stack[14]
+ int stk_size;
+ long *stack, *sp;
+
+ stk_size = stack_size(sym);
+ if( stk_size < 0 ){
+ FREE_ARGS;
+ rb_raise(rb_eDLTypeError, "unknown type '%c'.", -stk_size);
+ }
+ else if( stk_size > (int)(sizeof(long) * 15) ){
+ FREE_ARGS;
+ rb_raise(rb_eArgError, "too many arguments.");
+ stk_size = sizeof(long) * 15;
+ }
+ stack = (long*)alloca(stk_size);
+ sp = stack;
+#elif defined(USE_INLINE_ASM)
+#define DLSTACK_PROTO
+#define DLSTACK_ARGS
+#endif
+
+ ASM_START(sym);
+
+#if defined(USE_DLSTACK)
+ for( i = 0; i <= sym->len -2; i++ )
+#else
+ for( i = sym->len - 2; i >= 0; i-- )
+#endif
+ {
switch( sym->type[i+1] ){
case 'p':
case 'P':
@@ -538,71 +613,72 @@ rb_dlsym_call(int argc, VALUE argv[], VALUE self)
switch( sym->type[0] ){
case '0':
{
- void (*f)() = func;
- f();
+ void (*f)(DLSTACK_PROTO) = func;
+ f(DLSTACK_ARGS);
};
break;
case 'P':
case 'p':
{
- void * (*f)() = func;
- ret.p = f();
+ void * (*f)(DLSTACK_PROTO) = func;
+ ret.p = f(DLSTACK_ARGS);
};
break;
case 'C':
case 'c':
{
- char (*f)() = func;
- ret.c = f();
+ char (*f)(DLSTACK_PROTO) = func;
+ ret.c = f(DLSTACK_ARGS);
};
break;
case 'H':
case 'h':
{
- short (*f)() = func;
- ret.h = f();
+ short (*f)(DLSTACK_PROTO) = func;
+ ret.h = f(DLSTACK_ARGS);
};
break;
case 'I':
case 'i':
{
- int (*f)() = func;
- ret.i = f();
+ int (*f)(DLSTACK_PROTO) = func;
+ ret.i = f(DLSTACK_ARGS);
};
break;
case 'L':
case 'l':
{
- long (*f)() = func;
- ret.l = f();
+ long (*f)(DLSTACK_PROTO) = func;
+ ret.l = f(DLSTACK_ARGS);
};
break;
case 'F':
case 'f':
{
- float (*f)() = func;
- ret.f = f();
+ float (*f)(DLSTACK_PROTO) = func;
+ ret.f = f(DLSTACK_ARGS);
};
break;
case 'D':
case 'd':
{
- double (*f)() = func;
- ret.d = f();
+ double (*f)(DLSTACK_PROTO) = func;
+ ret.d = f(DLSTACK_ARGS);
};
break;
case 'S':
case 's':
{
- char * (*f)() = func;
- ret.s = f();
+ char * (*f)(DLSTACK_PROTO) = func;
+ ret.s = f(DLSTACK_ARGS);
};
break;
default:
FREE_ARGS;
rb_raise(rb_eDLTypeError, "unknown type `%c'", sym->type[0]);
- };
- };
+ }
+ }
+ }
#else
switch(ftype){
#include "call.func"