【问题标题】:dlopen/LoadLibrary on same applicationdlopen/LoadLibrary 在同一个应用程序上
【发布时间】:2012-07-31 13:45:22
【问题描述】:

我需要从我自己的应用程序中调用导出的符号 - 因此我需要知道在“你自己”上调用 dlopen / LoadLibrary 是否安全。

一个例子是:

LoadLibrary("test.exe");

在一个名为test.exe的程序中。

我对其进行了测试,它似乎可以工作,但我不太确定它是否真的受支持。

【问题讨论】:

  • 如果在自己的应用程序中,为什么不直接调用呢?
  • 它在插件架构中。实现(有点像 COM 中的)是基于 DLL/SO 的机制加载的。此机制适用于文件名。现在我们遇到了在应用程序中定义一些插件(至少作为备份)的用例。因此我们需要扩展Loader。如果我们可以通过 dlopen/LoadLibrary 安全地从应用程序加载,我们可以以统一的方式处理所有情况。

标签: c++ c loadlibrary dlopen


【解决方案1】:

来自 MSDN 文档here...

系统在所有加载的模块上维护每个进程的引用计数。调用 LoadLibrary 会增加引用计数。调用 FreeLibraryFreeLibraryAndExitThread 函数会减少引用计数。当模块的引用计数达到零或进程终止时(无论引用计数如何),系统都会卸载模块。

它会起作用的,只要记得打电话给FreeLibrary自己清理一下,如上所述。

您可能真正想要的是GetModuleHandle

检索指定模块的模块句柄。该模块必须已被调用进程加载。

事实上,你尝试做的甚至是一个特例。

lpModuleName [输入,可选]

...

如果此参数为 NULL,GetModuleHandle 返回用于创建调用进程的文件(.exe 文件)的句柄。

所以,试试这个……

#include <stdio.h>
#include <windows.h>

__declspec(dllexport) void print(void) {
  puts("OK");
}

main() {
  HMODULE mod = GetModuleHandle(0);
  FARPROC proc = GetProcAddress(mod, "print");
  proc();
  return 0;
}

... 似乎有效:

C:\dev\scrap>gcc -oprint print.c

C:\dev\scrap>print
OK

对于dlopen,看起来非常相似。

如果 filename 是 NULL 指针,则返回的句柄是主程序的。当给 dlsym() 时,此句柄会导致在主程序中搜索符号,然后是在程序启动时加载的所有共享库,然后是 dlopen 加载的所有共享库strong>() 带有 RTLD_GLOBAL 标志。

【讨论】:

  • 我知道GetModuleHandle,但是有统一处理的问题。当前,映射存储在 std::map<:string std::string> 中,它将类型映射到 dll/so。因此 NULL 不是一个可用的选项,应尽可能避免特殊处理...
【解决方案2】:

Windows 上的快速测试:

#include <windows.h>
#include <stdio.h>

__declspec(dllexport) FARPROC Test() { 
    printf("It worked");
}

int main(int argc, char **argv) {
    HMODULE m = LoadLibrary(argv[0]);
    FARPROC test = GetProcAddress(m, "Test");
    test();
    return 0;
}

产生以下输出:

It worked

我认为理论上这并不能保证它不会在其他版本的 Windows 上崩溃,但我认为这相当值得怀疑。

【讨论】:

    【解决方案3】:

    您可以使用 dlopen() 轻松地做到这一点 - 有一个作为 GNU 扩展的伪句柄 RTLD_DEFAULT 就像您自己的句柄一样,因此您可以简单地跳过 dlopen() 调用并编写:

    dlsym(RTLD_DEFAULT, "entry_func");
    

    例如:

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <dlfcn.h>
    #include <assert.h>
    
    void print(void) {
      puts("OK");
    }
    
    int main() {
      void (*proc)(void) = dlsym(RTLD_DEFAULT, "print");
      assert(proc);
      proc();
      return 0;
    }
    

    请注意,您需要使用 -rdynamic 进行编译才能使其正常工作。

    根据最近的 Solaris 机器上的联机帮助页,RTLD_DEFAULT 也可以在那里找到。

    【讨论】:

    • 根据手册页可以使用 dlopen(nullptr);无论出于何种原因, RTLD_DEFAULT 对我不起作用。由于我现在在 Windows 上使用 GetModuleHandle,我不妨在 Linux 上使用 dlopen(nullptr) ... [因为我已经需要以不同方式处理主机程序...]
    猜你喜欢
    • 2013-10-23
    • 2019-06-07
    • 2015-02-10
    • 2020-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-08
    相关资源
    最近更新 更多