首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >跨编译器/平台裸包装函数,无条件跳转到函数指针

跨编译器/平台裸包装函数,无条件跳转到函数指针
EN

Stack Overflow用户
提问于 2013-11-14 06:16:01
回答 1查看 562关注 0票数 1

我正在处理一个复杂的程序,它将有插件调用函数,但是这些函数的方法将在启动时被选择,并使用函数指针分配。

我希望在主可执行文件中有一些高效的包装器函数来调用适当的函数,而不是传递函数指针。

因为对于插件接口,调用约定将根据构建目标(使用宏)定义__cdecl__stdcall,函数将声明为extern "C"

基本上,我希望能够在我的可执行文件中声明一个符号,插件可以根据需要加载。对于需要解决复杂科学问题的不同任务,解决这些任务的解决方案或方法的范围是如何的,这些解决方案或方法将存储在插件中,因此很容易添加新方法(不重新编译整个应用程序),这也使共享新方法变得更容易,因为任何具有基本代码的人都可以添加任何不需要自己体验的插件。

无论如何,我都可以使用这个概念,或者在加载插件时必须将函数映射传递给插件,但是函数映射的具体内容取决于加载的配置和插件,因此在加载插件之前,我不知道它是什么,这将是一个问题。因此,我的解决方案是将映射存储为主可执行文件中的一组全局变量,并通过包装器函数访问。

但是,这并不是直接的,因为函数具有调用约定,在调用后和返回之前涉及操作堆栈,在包装器上应该忽略这一点,它还应该为intel x386 ASM执行一个非相关的跳转x386,而不是为intel x386 ASM执行函数调用call,而控制应该从跳转函数返回到调用代码而不是包装程序。但是,我需要C/C++代码来独立于编译器/平台/处理器。

下面是一个基本概念示例,用于测试我的想法并演示我想要做的事情:

C++代码(Microsoft C++ 2010 (特定))

代码语言:javascript
运行
复制
#include <iostream>
void * pFunc;
int doit(int,int);
int wrapper(int, int);
int main() {
    pFunc = (void*)doit;
    std::cout << "Wrapper(2,3): " << wrapper(2,3) << std::endl;
    std::cout << "doit(2,3):    " << doit(2,3) << std::endl;
    return 0; }
int doit(int a,int b) { return a*b; }
__declspec(naked) int wrapper(int, int) { __asm jmp pFunc }

代码经过测试才能正常工作,这两个调用都是输出6。

包装器和doit的ASM输出

代码语言:javascript
运行
复制
PUBLIC  ?wrapper@@YAHHH@Z               ; wrapper
; Function compile flags: /Odtp
;   COMDAT ?wrapper@@YAHHH@Z
_TEXT   SEGMENT
___formal$ = 8                      ; size = 4
___formal$ = 12                     ; size = 4
?wrapper@@YAHHH@Z PROC                  ; wrapper, COMDAT
; File c:\users\glen fletcher\documents\visual studio 2010\projects\test_wrapper\test_wrapper.cpp
; Line 15
    jmp DWORD PTR ?pFunc@@3PAXA         ; pFunc
?wrapper@@YAHHH@Z ENDP                  ; wrapper
_TEXT   ENDS
PUBLIC  ?doit@@YAHHH@Z                  ; doit
; Function compile flags: /Ogtp
;   COMDAT ?doit@@YAHHH@Z
_TEXT   SEGMENT
_a$ = 8                         ; size = 4
_b$ = 12                        ; size = 4
?doit@@YAHHH@Z PROC                 ; doit, COMDAT
; Line 14
    push    ebp
    mov ebp, esp
    mov eax, DWORD PTR _a$[ebp]
    imul    eax, DWORD PTR _b$[ebp]
    pop ebp
    ret 0
?doit@@YAHHH@Z ENDP                 ; doit
; Function compile flags: /Ogtp
_TEXT   ENDS

用于包装的非包装ASM

代码语言:javascript
运行
复制
PUBLIC wrapper
_1$ = 8
_2$ = 12
_TEXT SEGMENT
wrapper PROC
   push ebp
   mov ebp, esp
   mov eax, DWORD PTR _2$[ebp]
   push eax
   mov ecx, DWORD PTR _1$[ebp]
   push ecx
   call DWORD PTR pFunc
   add esp, 8
   pop ebp
   ret 0
wrapper ENDP
_TEXT ENDS

如何以跨平台和跨编译器的方式生成原始代码??与编译器生成的带有epilog和prolog代码的C/C++函数的标准相反,备注也不想对处理器作出假设,因此不能执行单独的ASM文件,希望编译器只使用无条件跳转语句生成代码。

goto不工作,因为pFunc是一个变量,而不是标签,甚至不确定它的goto在函数之间是否能工作。

EN

回答 1

Stack Overflow用户

发布于 2013-11-15 03:03:21

我想出了解决问题的方法,而不是使用裸露的函数或传递函数指针的列表。

我可以传递一个指向函数指针结构的指针,即

代码语言:javascript
运行
复制
struct Functions {
   bool (AppAPI *logInfo(std::string,...)),
   bool (AppAPI *logWarn(std::string,...)),
   bool (AppAPI *logError(std::string,...)),
   bool (AppAPI *registerFunction(std::string,void *))
   ...
} PluginFunctions;

for (int i = 0;i<plugins;i++) {
   plugin[i].initialize(&PluginFunctions)
}

PluginFunctions.logInfo = LogInfo;
...

由于插件init函数将指针传递给结构,它可以存储这个指针,然后从内存中加载函数指针的当前值,结构只是内存中的指针表,函数指针可以在结构传递到插件之后设置,并且它仍然更新插件。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19970859

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档