【问题标题】:How to execute unknown functions from dynamic load libraries?如何从动态加载库中执行未知函数?
【发布时间】:2011-02-18 16:12:11
【问题描述】:

当您在设计时了解该函数时,从动态库中加载函数很容易。 做这样的事情:

   int (*fn)(int);

   l0 = dlopen("./libfoo.so", RTLD_LAZY);
   if (!l0) 
   {
      fprintf(stderr, "l0 %s\n", dlerror());
      return 1;
   }


   fn = (int (*)(int))dlsym(l0, "foo");


   if ((error = dlerror()) != NULL)  
   {
      fprintf(stderr, "fn:%s\n", error);
      return 1;
   }

   x=(*fn)(y);

...

设计时未知的库函数如何执行?在运行时你有一个函数名和参数指针数组和参数大小数组:

char* fn_name="foo"; int foo_argc; 无效* foo_argv[]; int foo_argv_size[];

在脚本语言中这是小菜一碟,但是如何在 c++ 中很好地实现呢?

【问题讨论】:

  • 你是说你实际上不知道你要使用的函数的名称是什么?您还需要区分其中的库,例如用 Python 制作的 Python 和用 C 制作的 Python 中的库或模块,或者您想到的任何脚本语言。目前你的意思有点模糊。
  • 我不是 C++ 大师,但这似乎有点臭。

标签: c++ dynamic load runtime


【解决方案1】:

如果您知道函数的调用约定以及它接收哪些参数,那么实际上有一种方法可以在运行时调用函数。然而,这超出了标准 C/C++ 语言范围。

对于 x86 汇编器:

假设如下:

  1. 您知道将函数的所有参数准备在一个固态缓冲区中,完全按照将它们打包到堆栈中的方式。
  2. 您的函数不会按值获取/返回 C++ 对象。

您可以使用以下功能:

int CallAnyFunc(PVOID pfn, PVOID pParams, size_t nSizeParams)
{
    // Reserve the space on the stack
    // This is equivalent (in some sense) to 'push' all the parameters into the stack.
    // NOTE: Don't just subtract the stack pointer, better to call _alloca, because it also takes
    // care of ensuring all the consumed memory pages are accessible
    _alloca(nSizeParams);

    // Obtain the stack top pointer
    char* pStack;
    _asm {
        mov pStack, esp
    };

    // Copy all the parameters into the stack
    // NOTE: Don't use the memcpy function. Because the call to it
    // will overwrite the stack (which we're currently building)
    for (size_t i = 0; i < nSizeParams; i++)
        pStack[i] = ((char*) pParams)[i];

    // Call your function
    int retVal;
    _asm {
        call pfn
        // Most of the calling conventions return the value of the function (if anything is returned)
        // in EAX register
        mov retVal, eax
    };

    return retVal;
}

您可能需要调整此函数,具体取决于使用的调用约定

【讨论】:

  • 一个不错的技巧,但它仍然需要某人(调用此函数的人)知道在编写对您的函数的调用时将哪些参数传递给@987654322 @.
  • 没有。参数的“固体缓冲区”可以在运行时分配和初始化。注意:参数缓冲区的大小是一个变量,而不是一个常量。
【解决方案2】:

作为一种静态语言,C++ 确实依赖于在编译时了解类型/接口。我怀疑是否有一种很好的低级方法可以完成您要解决的任务,但与往常一样,如果您添加另一层抽象,您可能可以实现目标。特别是在这种情况下,我过去在不确切知道需要将哪些“参数”传递给动态加载的插件时使用的方法是传递 std::map&lt; std::string, std::string &gt; &gt; 的单个参数(即 key=value 字典)只需打包它带有需要传递的任何信息,并让插件弄清楚如何处理它。这有点不方便,因为您必须对大部分数据进行编码/解码,但它至少可以用来优雅地处理我遇到的大多数情况。

当然,这假设您有能力通过标准化 Plugin 接口来控制动态加载的 DLL 的 API。如果你不能这样做......你可能不走运。

【讨论】:

    【解决方案3】:

    正如问题所述,您不能在标准 C++ 中执行此操作。除非编译器在编译时知道被调用者的参数,否则无法有效调用函数。即使在 varargs 函数的情况下,调用代码也必须在知道传递了哪些参数的情况下进行编译 - 无法构建 va_list

    如果您控制 dll 中的函数,那么最好的方法是传递 boost::any 的集合(如果两者都将使用相同的编译器和库进行编译)或 void*(如果您不有那么大的兼容性),或者参数的某种序列化形式(可能是 Google 的协议缓冲区)。

    如果 dll 已经存在并且您无法更改其接口,那么您需要使用堆栈玩非标准技巧以根据您的调用约定将参数放在正确的位置(这是动态语言所做的)当他们有一个 C 调用机制时),否则你需要一个巨大的 switch 语句来覆盖它可能的所有函数签名。显然,后者只有在您已经清楚自己正在处理的 dll 时才有可能。

    此外,通常您需要的不仅仅是指向参数及其大小的指针列表。调用约定很可能取决于参数的类型,而不仅仅是大小。例如,浮点参数可以在 FPU 寄存器中传递,而不是在堆栈中。

    您还需要担心返回类型,以及调用代码期望用它做什么。

    【讨论】:

      【解决方案4】:

      设计时未知的库函数如何执行?

      你不能。 C++ 中没有反射。您必须知道函数原型才能调用它。 (无论如何,除非你实现自己的反射。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-03
        • 2021-10-18
        • 2012-01-31
        • 1970-01-01
        • 2011-02-03
        相关资源
        最近更新 更多