【问题标题】:How to write a (generic) self-replacing stub function for a DLL function?如何为 DLL 函数编写(通用)自替换存根函数?
【发布时间】:2014-01-08 18:07:37
【问题描述】:

我有一些函数,比如int foo(int x),它是从 DLL 获得的(使用 dlsym())。所以,目前我的代码看起来像这样:

void foo(int x) {
    void (*foo)(int x);
    foo = dlsym(dll_handle, "foo");
    int y = foo(x);
    printf("y is %d", y);
}

我想要的是(类似于)这段代码工作:

void bar(int x) {
    int y = foo(x);
    printf("y is %d", y);
}

所以foo() 是一个调用 dll 函数的存根(但不必每次都搜索 DLL)。

  1. 为单个功能实现此目标的最佳方法是什么?
  2. 对于许多函数的情况,如何避免编写一堆复制粘贴存根?考虑到签名,宏解决方案可能会很棘手。也许是基于 C++11 的可变参数参数模板?

在下面的答案中,我对 1. 的解决方案有一个基本想法,但我不太确定,我想在这里采用“最佳实践”方法。

【问题讨论】:

  • 这肯定会起作用,但它永远不会在不再需要时使用dlclose(3) 卸载共享库。这可能没问题,但是如果您希望能够卸载库,则需要一个取消初始化函数,该函数将调用dlclose() 并将dll_foo 变量设置回NULL

标签: c stub dlopen idioms


【解决方案1】:

您已经在问题中回答了自己。一个小的改进可能是检查 dll 的“更新”(如果有)。

int foo(int x) {
     static void (*dll_foo)(int x) = NULL;
     static void *foo_dll_handle = NULL;
     if (dll_foo == NULL || foo_dll_handle != dll_handle) {
          dll_foo = dlsym(dll_handle, "foo");
          foo_dll_handle = dll_handle;
     }
     return dll_foo(x);
}

【讨论】:

    【解决方案2】:

    对于很多函数的情况,如何避免写一堆 复制粘贴存根?

    已经很久了,但是为了完整起见,您可以使用Implib.so自动生成这样的包装器:

    $ implib-gen.py mylib.so
    

    这将生成两个文件,mylib.so.tramp.S 和 mylib.so.init.c。程序集文件包含库函数的包装器(可选 dlopen 库并使用 dlsym 来定位正确的实现):

    // Wrapper for bar symbol
      .globl bar
    bar:
      .cfi_startproc
      // Check if library function address is resolved
      cmp $0, _libtest_so_tramp_table+0(%rip)
      je 2f
    1:
      // Fast path
      jmp *_libtest_so_tramp_table+0
    2:
      // Slow path
      pushq $0
      .cfi_adjust_cfa_offset 8
      call save_regs_and_resolve
      addq $8, %rsp
      .cfi_adjust_cfa_offset -8
      jmp 1b
      .cfi_endproc
    

    生成的 C 代码句柄 dlopendlsym 部分:

    void _libtest_so_tramp_resolve(int i) {
      assert(i < sizeof(sym_names) / sizeof(sym_names[0]) - 1);
      if(!lib_handle) {
        lib_handle = dlopen("libtest.so", RTLD_LAZY | RTLD_GLOBAL);
      }
      CHECK(lib_handle, "failed to load library 'libtest.so': %s", dlerror());
    
      // Can be sped up by manually parsing library symtab...
      _libtest_so_tramp_table[i] = dlsym(lib_handle, sym_names[i]);
      CHECK(_libtest_so_tramp_table[i], "failed to resolve symbol '%s' in library 'libtest.so'", sym_names[i]);
    }
    

    更多信息,请查看projects Github page

    【讨论】:

    • 1.你能举一个生成的存根的例子吗? 2. .S 文件是干什么用的?
    • @einpoklum 当然,附上。 .S 文件用于生成尽可能快的存根函数(它们与普通 PLT 调用一样快)。
    • 在 C 示例中,lib_handle 是在哪里定义的?是全球性的吗?此外,C 示例似乎总是走“慢路”。汇编文件快速路径中,“+0”有什么作用?
    • @einpoklum 首先请注意,我没有复制粘贴所有自动生成的代码,因为它很大并且可能不太有趣。对于较小的示例,请查看 this anwer(Implib.so 的来源)。
    • @einpoklum “在 C 示例中,lib_handle 是在哪里定义的?它是全局的吗?” - 是的,它是一个静态变量。
    【解决方案3】:

    对于单个函数的情况,我认为这样的事情应该是正确的:

    int foo(int x) {
         static void (*dll_foo)(int x) = NULL;
         if (dll_foo == NULL) {
              dll_foo = dlsym(dll_handle, "foo");
         }
         return dll_foo(x);
    }
    

    【讨论】:

      猜你喜欢
      • 2018-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-08
      • 2016-08-04
      • 2011-06-11
      • 1970-01-01
      相关资源
      最近更新 更多