雁过无痕

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  弄着玩的。功能是简单的实现函数转发,即
      调用CALL(func),转为调用func(),
      调用CALL(func, arg1, arg2) ,转为调用func(arg1, arg2)

  代码中,宏CALL/STDCALL分别用来调用  __cdecl/__stdcall 调用规定的函数
             unsafe_call 两者都可调用,但它不是多线程安全的。

  代码只支持x86 32位, 除内嵌汇编部分,尽量符合C++11标准。
       
原理:
     刚进入函数时,
     [esp]           函数返回地址
     [esp + 4]     第一个参数,即转发函数的地址
     [esp + 8]     第二个参数,即转发函数的的第一个参数
     ... 
   
      只要写三行汇编指令实现一个c_call函数,就可调用转发函数
      pop eax                             ; eax为函数返回地址
      xchg dword ptr[esp], eax     ; eax为转发函数的地址,[esp]为函数返回地址
      jmp eax
     
     当转发函数是__cdecl,即转发函数不会调节栈,由于在c_call,pop eax,使esp多加了4,因而在调用完c_call后应该手动将esp值减4,保证栈平衡。
   
    当转发函数是__stdcall,转发函数会调节栈,调用转发函数完毕后,栈已经保持平衡,因而调用c_call完毕,不应该进行栈指针调节。似乎将c_call的调用改为__stdcall即可,但实际上c_call有变长参数,改成__stdcall没效果,每次调用编译器还是会自动生成调节栈指针代码。因而只能每次调用完毕,编译器给esp加了多少,就手动减多少。(编译器不一定会生成 call  xxxx; add esp, xx这样的代码,通过改函数返回地址,忽略后面的add esp, xx指令是很糟糕的做法。)
call_redirect
posted on 2012-08-05 21:02 flyinghearts 阅读(1794) 评论(8)  编辑 收藏 引用 所属分类: C++

评论

# re: 内嵌汇编实现的函数转发 2012-08-06 14:02 rix
typedef int (*func_ptr)(...);
int call_fun(func_ptr func, ...);
#define PUSH __asm push 0
int call_fun(func_ptr func, ...)
{
*((int*)(&func)-1) = (*((int*)(&func)-1))^(*((int*)(&func)));
*((int*)(&func)) = (*((int*)(&func)-1))^(*((int*)(&func)));
*((int*)(&func)-1) = (*((int*)(&func)-1))^(*((int*)(&func)));
return (int)func;
}

#define START_CALL PUSH
#define END_CALL

int main (int argc, char * argv[])
{
START_CALL;
call_fun((func_ptr)func_1, 1, 2);
END_CALL;
START_CALL;
printf("func_2 return=%d\n", call_fun((func_ptr)func_2));
END_CALL;
START_CALL;
call_fun((func_ptr)func_3, "printf this:%d, %s\n", 1, "ok");
END_CALL;
return 0;
}  回复  更多评论
  

# re: 内嵌汇编实现的函数转发[未登录] 2012-08-06 15:31 heroboy
这样?
template<class T,class A1>
T Call(T(*f)())
{
return f();
}

template<class T,class A1>
T Call(T(*f)(A1),A1 a)
{
return f(a);
}

template<class T,class A1,class A2>
T Call(T(*f)(A1),A1 a,A2 b)
{
return f(a,b);
}
  回复  更多评论
  

# re: 内嵌汇编实现的函数转发 2012-08-06 21:43 flyinghearts
@rix
这个代码显然是错的。
call_fun只是简单返回 func的值而已。  回复  更多评论
  

# re: 内嵌汇编实现的函数转发 2012-08-06 21:57 flyinghearts
@heroboy
你的模板写错了。

用模板优点不说了,就说说缺点吧:
如果转发函数的参数是不定的,模板无法匹配到。
另外,类型匹配太严格了,若参数类型是const char*, 传入"123"字符串都要先转下类型,特别是参数是数字时,非要查看下参数是int还是long,还是其它的。用起来太累人。



  回复  更多评论
  

# re: 内嵌汇编实现的函数转发 2012-08-13 12:03 rix
@flyinghearts
自己调用下就知道了。
*((int*)(&func)-1) = (*((int*)(&func)-1))^(*((int*)(&func)));
*((int*)(&func)) = (*((int*)(&func)-1))^(*((int*)(&func)));
*((int*)(&func)-1) = (*((int*)(&func)-1))^(*((int*)(&func)));
关键在这个交换的地方,而不是返回值。  回复  更多评论
  

# re: 内嵌汇编实现的函数转发 2012-08-14 21:10 flyinghearts
@rix
你的做法,还是避免不了要 内嵌汇编。这个做法似乎可行,但却存在很大问题:过于依赖于编译器怎么优化。有些编译器会认为那段代码在做无意义的事,直接优化掉。你用gcc试试。

另外,交换两个数,那样写法,可读性差,效率也差,。







  回复  更多评论
  

# re: 内嵌汇编实现的函数转发 2012-08-17 21:25 flyinghearts
@rix
你那样写不可取,至少要写成下面这样,保证不被优化掉:

void* c_call(void* func, ...)
{
typedef void* type;
const type tmp = func;
volatile type& eip = func;
volatile type& ret_addr = *(&func - 1);
eip = ret_addr;
ret_addr = tmp;
return (void*)tmp;
}

但是这样又存在一个大问题:要保证调用这个函数时,不会被内联,一但被内联,bug就来了。




  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理