没有外部“C”的动态加载

2023-11-25

我想一般使用 libdl 动态加载 C++。问题是在运行时识别名称已被破坏的符号。

如此处所述,一种解决方案是使用 extern“C” 删除名称修饰。

http://www.tldp.org/HOWTO/C++-dlopen/theproblem.html

此解决方案的缺点是将动态加载的资源限制为 C 样式接口。例如,动态加载的函数不能是重载函数。

有什么好方法可以克服这个限制呢?

一种可能的解决方案是使用附带的函数对库源代码进行命名重整,以便在需要链接库时获取重整后的名称。 llvm 是否提供了这方面的工具?

也许一个笨拙的解决方案是一个函数,它接受函数签名,使用具有签名的函数创建虚拟代码,通过管道输入与生成程序集的标志一起使用的编译器,解析输出以检索损坏的名称,然后返回损坏的名称作为字符串。然后可以将该字符串传递给 dlsym()。

为了使问题具体化,这里有两个示例程序,它们说明了 extern“C”解决方案在不修改库代码的情况下无法动态加载的情况。第一个以传统 C++ 方式动态链接库。第二个使用 dlopen。在第一个程序中链接重载函数很简单。没有简单的方法来链接第二个程序中的重载函数。

程序1:加载时动态链接

main.cpp

// forward declarations of functions that will be linked
void say(int);
void say(float);

int main() {
    int myint = 3;
    say(myint);
    float myfloat = 5.0f;
    say(myfloat);
}

say.cpp

#include <iostream>

//extern "C" function signatures would collide

//extern "C" void say(int a) {
void say(int a) {
    std::cout << "The int value is " << a << ".\n";
}

//extern "C" void say(float a) {
void say(float r) {
    std::cout << "The float value is " << r << ".\n";
}

output

$ ./main
The int value is 3.
The float value is 5.

方案2:运行时动态链接

main_with_dl.cpp

#include <iostream>
#include <dlfcn.h>

int main() {
    // open library
    void* handle = dlopen("./say_externC.so", RTLD_LAZY);
    if (!handle) {
        std::cerr << "dlopen error: " << dlerror() << '\n';
        return 1;
    }

    // load symbol
    typedef void (*say_t)(int);

    // clear errors, find symbol, check errors
    dlerror();
    say_t say = (say_t) dlsym(handle, "say");
    const char *dlsym_error = dlerror();
    if (dlsym_error) {
        std::cerr << "dlsym error: " << dlsym_error << '\n';
        dlclose(handle);
        return 1;
    }

    // use function
    int myint = 3;
    say(myint);
    // can't load in void say(float)
    // float myfloat = 5.0f;
    // say(myfloat);

    // close library
    dlclose(handle);
}

output

$ ./main_with_dl
The int value is 3.

编译

Makefile

CXX = g++

all: main main_with_dl say_externC.so

main: main.cpp say.so
    $(CXX) -o $@ $^

main_with_dl: main_with_dl.cpp
    $(CXX) -o $@ $<

%.so : %.cpp
    $(CXX) -shared -o $@ $<

.PHONY: clean
clean:
    rm main main_with_dl say.so say_externC.so

感谢 Mooing Duck,我能够使用 clang 并受到 Visual Studio 的启发想出一个解决方案。

关键是Visual Studio和clang提供的宏。这__FUNCDNAME__宏解析为封闭函数的损坏名称。通过定义与我们想要动态链接的函数具有相同签名的函数,我们可以得到__FUNCDNAME__解析所需的名称 mangle。

这是程序 2 的新版本,可以调用两者void say(int) and void say(float).

EDIT咕咕鸭向我传授了更多知识。这是一个版本main_with_dl.cppsay.cpp在问题中。

#include <iostream>
#include <dlfcn.h>

void* handle;

template<class func_sig> func_sig get_func(const char* signature)
{
    dlerror();
    func_sig func = (func_sig) dlsym(handle, signature);
    const char *dlsym_error = dlerror();
    if (dlsym_error) {
        std::cerr << "dlsym error: " << dlsym_error << '\n';
        dlclose(handle);
        exit(1);
    }
    return func;
}

void say(int a) {
    typedef void(*func_sig)(int);
    static func_sig func = get_func<func_sig>(__FUNCDNAME__);
    return func(a);
}

void say(float a) {
    typedef void(*func_sig)(float);
    static func_sig func = get_func<func_sig>(__FUNCDNAME__);
    return func(a);
}

int main() {
    // open library
    //void* handle = dlopen("./say_externC.so", RTLD_LAZY);
    handle = dlopen("./say.so", RTLD_LAZY);
    if (!handle) {
        std::cerr << "dlopen error: " << dlerror() << '\n';
        return 1;
    }

    // use function
    int myint = 3;
    say(myint);
    float myfloat = 5.0f;
    say(myfloat);

    // close library
    dlclose(handle);
}

http://coliru.stacked-crooked.com/a/7249cc6c82ceab00

代码必须使用以下方式编译clang++-fms-extensions标记为__FUNCDNAME__上班。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

没有外部“C”的动态加载 的相关文章

随机推荐

  • 是否可以分别推送不同架构的docker镜像?

    据我所知docker buildx build push将用您在中指定的架构覆盖现有的映像架构 platform范围 据我了解 在使用时必须同时构建和推动所有架构buildx 但是 我知道官方 docker 镜像使用arm64构建农场来构建
  • Null 不是一个使用 Expo 的对象(评估 'RNFetchBlob.DocumentDir')

    我正在使用 Expo 并尝试使用react native fetch blob 下载文件 import RNFetchBlob from rn fetch blob RNFetchBlob fetch GET http www exampl
  • 透明背景 JFrame Linux 上的动画

    我想为 Frame 或 JFrame 创建完全透明的背景并让它显示透明动画 我设法让它在 Windows 7 x64 中运行 但相同的代码无法在我的 Linux Lubuntu x64 15 04 上运行 下面的代码显示了我想要实现的目标
  • 我该怎么做:使用@databaseName

    DECLARE DatabaseName NVARCHAR max SET DatabaseName MainDb USE DatabaseName 行不通 怎样制作呢 如果你想像这样动态地执行它 你必须使用动态 SQL 意味着您想要在该数
  • 我们可以给静态数组的大小一个变量吗

    大家好 我想问一下 我已经读到 我们只能通过使用指针并使用 malloc 或 newlike 来声明动态数组 int array new int strlen argv 2 但我已经写了 int array strlen argv 2 它没
  • FFMPEG 命令在 Android Q 中不起作用

    尝试此命令来合并两个音频文件 但它在 Android 10 0 Q 中不起作用针对 SDK 29 但是 这个命令完全有效针对 sdk 24 至 28 我正在使用这个 FFMPEG 实现库 nl bravobit android ffmpeg
  • 将 JSON 显示为 HTML [关闭]

    Closed 这个问题是无关 目前不接受答案 关于如何将 JSON 嵌入到 HTML 页面中 并将 JSON 格式化为人类可读的样式 有什么建议吗 例如 当您在浏览器中查看 XML 时 大多数浏览器都会显示 XML 格式 缩进 正确的换行符
  • jquery如何做一个翻转效果?

    我试图模仿通常在移动设备上发现的效果 在移动设备上有一个面板 当您单击按钮时 它会旋转 而在另一侧它有一些其他信息 我发现了一些使用 css 转换的代码这是一个例子 the js signup on click function e e p
  • Perl6:获取 Perl5 模块的数组引用

    我正在尝试使用 Excel Writer XLSX 在 Perl6 中编写 Excel 笔记本 我正在使用 Inline Perl5 通过use Excel Writer XLSX from
  • Xcode 6 不显示 iOS 7.1 模拟器

    我在 Mac OS X 10 9 5 上拥有 Xcode 版本 6 0 1 6A317 以及所有 iOS 8 模拟器 我还下载了 iOS 7 1 模拟器 我正在开发一个适用于 iOS 8 和 iOS 7 的应用程序 我遇到的问题是 Xcod
  • Python copy.deepcopy() 函数无法正常工作[重复]

    这个问题在这里已经有答案了 我一直在使用深度复制功能和复制功能 并且我对它们都遇到了同样的问题 这就像副本是引用 或指针 而不是正确的副本 我正在Python中处理数据记录 类 也许可能是这样 我给你看一个例子 gt gt gt impor
  • 如何使用 ggplot2 从 qcc 包重现 pareto.chart 图?

    我一直在使用 R 中 qcc 包中的 pareto chart 函数 我真的很喜欢它 现在我想移植所有图形以使用 ggplot2 包 然而 尽管有出色的文档 但我对 ggplot2 的了解非常有限 因此我无法弄清楚所有细节 基本上我想要一个
  • 如何生成 a[i] != i 的排列?

    假设我有一个整数数组int a 0 1 N 1 where N的大小是a 现在我需要生成所有排列a s that a i i对全部0 lt i lt N 你会怎么做 下面是一些 C 实现的算法 该算法基于递归的双射证明 n n 1 n 1
  • Android 通用用户代理 (UA)

    我正在构建一个 Android 应用程序来显示来自服务器的内容源 服务器是一个移动网站 例如http m google com 它跟踪来自各种移动客户端的流量 为了区分 Android 客户端 如何为我的应用程序提供通用字符串 这就是我问这
  • 是否可以在索引视图中使用 LabelFor 作为标题行

    我正在尝试在 ASP NET MVC 索引视图中利用 DataAnnotation 值 有趣的是 代码生成器使用字段名称 例如 BlogPost 而不是Html LabelFor m gt Model ColumNames BlogPost
  • java中整数线程递增是安全的吗?

    Java代码 public class IncreaseTest public static int value 0 public synchronized int increment return value 是方法increment 线
  • 如何从 Java 代码中编写乐观锁和悲观锁

    我知道什么是乐观锁和悲观锁 但是当你编写java代码时你会怎么做呢 假设我将 Oracle 与 Java 结合使用 JDBC 中是否有任何方法可以帮助我做到这一点 我将如何配置这个东西 任何指示将不胜感激 您可以通过这种方式在数据库表中实现
  • 如果文件名为 _ViewStart.cshtml,Razor 布局将不起作用

    我有一个 MVC3 Web 应用程序 我想开始使用整个 Razor 视图 该网站的大部分内容都可以与 Site Master 配合使用 因此我已将其大部分内容打包到部分视图中 并尝试添加具有相同内容的布局 阅读 Scott Gu 的博客后
  • 如何在运行时轻松地在 PyQt 或 PySide 之间进行选择?

    我想在一个源文件 QT py 中执行类似的操作 import sys import PyQt4 sys modules Qt PyQt4 然后将此文件导入到其他源文件中 并像这样使用它 import QT from Qt QtCore im
  • 没有外部“C”的动态加载

    我想一般使用 libdl 动态加载 C 问题是在运行时识别名称已被破坏的符号 如此处所述 一种解决方案是使用 extern C 删除名称修饰 http www tldp org HOWTO C dlopen theproblem html