如何解开从 C++ 导出的符号在 macOS 上 Xcode 的动态库中

发布于 2024-10-08 10:29:26 字数 554 浏览 5 评论 0原文

我一直在尝试用 C++ 开发一个可以在应用程序中运行时加载的动态库。我终于成功了,但它有点难看。我有一个函数,它接受一个指向 C++ 类的指针作为参数,它看起来像这样:

bool registerGrindPlugin( Grind::PluginManager* mgr );

但当然它被导出为:

_Z19registerGrindPluginPN5Grind13PluginManagerE

我尝试了一个带有简单函数的 .c 文件,它导出得很好,为“registerGrindPlugin”,但是当然我不能以这种方式传递 C++ 类作为参数。

所以...我的问题是,有没有办法对导出的符号进行解开或别名,这样我就不必在 dlsym 调用中使用像 Z19registerGrindPluginPN5Grind13PluginManagerE 这样的怪物?

我确实看到了一些关于 -alias_list 作为链接器选项的内容,但我还没有完全弄清楚如何在 Xcode 中使用它。如果这是解决方案,有人可以提供有关如何使用它的更多详细信息吗?

I've been trying to develop a dynamic library in C++ that can be run-time loaded in an application. I finally got it working, but it's a little ugly. I have a function that takes a pointer to a C++ class as an argument, which looks like this:

bool registerGrindPlugin( Grind::PluginManager* mgr );

But of course it's being exported as:

_Z19registerGrindPluginPN5Grind13PluginManagerE

I tried a .c file with a simple function and it exported fine as "registerGrindPlugin", but of course I can't pass a C++ class as the argument that way.

So... my question is, is there a way to unmangle or alias the exported symbol so that I don't have to use a monstrosity like Z19registerGrindPluginPN5Grind13PluginManagerE in my dlsym call?

I did see something about -alias_list as a linker option, but I haven't quite figured out how to use it in Xcode. If that is the solution, can somebody provide some more details on how to use this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烟─花易冷 2024-10-15 10:29:26

您尝试的方式不会长期有效。

您不能指望任何特定的 C++ 修饰/分解算法。不同的编译器——甚至同一编译器的不同版本——使用了不同的编译器。所以你可以这样做,并切换到新版本的 Xcode,但情况会很糟糕。

此外,C++ 还存在脆弱二进制接口问题。为了避免这种情况,Grind::PluginManager 实例内部的所有操作(从创建到成员访问再到删除)都需要在同一个动态库中进行。

解决这些问题是 Objective C 消息系统和 Windows OLE 系统背后的一些基本原理。

C++ 解决方案是使用包装器系统。

首先,您需要定义一个不透明的指针类型来代表 Grind::PluginManager*。 C 语言 Cocoa 绑定经常这样做。

typedef void* MyGrindPlugInManagerOpaqueHandle;

其次,对于您想要从动态库外部对 Grind::PluginManager 执行的每个操作,您需要使用 extern "C" 来定义具有非损坏 C 绑定的函数,这需要这些不透明指针之一作为参数。例如:

#ifdef __cplusplus
extern "C" {
#endif

void foo_wrapper(MyGrindPlugInManagerOpaqueHandle *bar);

#ifdef __cplusplus
}
#endif

第三,C++ 文件中的实现将如下所示:

void foo_wrapper(MyGrindPlugInManagerOpaqueHandle *bar)
{
    Grind::PluginManager* baz = (Grind::PluginManager*)bar;
    baz->foo();
}

The way you're trying to do it isn't going to work over the long haul.

You can't count on any particular C++ mangling/demangling algorithm. Different compilers - and even different versions of the same compiler - have used different ones. So you could do this, and switch to a new version of Xcode, and be left in a bad situation.

Also, C++ suffers from the Fragile Binary Interface Problem. To avoid that, all operations on the internals of a Grind::PluginManager instance, from creation to member access to deletion, need to happen in the same dynamic library.

Solving these problems is some of the rationale behind Objective C's messaging system, and the Windows OLE system.

The C++ solution is to use a wrapper system.

First, you need to define an opaque pointer type to stand in for Grind::PluginManager*. The C-language Cocoa bindings do this a lot.

typedef void* MyGrindPlugInManagerOpaqueHandle;

Second, for each operation you want to do on a Grind::PluginManager from outside the dynamic library, you need to use extern "C" to define a function with non-mangled C binding, and that takes one of those opaque pointers as an argument. For instance:

#ifdef __cplusplus
extern "C" {
#endif

void foo_wrapper(MyGrindPlugInManagerOpaqueHandle *bar);

#ifdef __cplusplus
}
#endif

Third, the implementation in a C++ file will look something like this:

void foo_wrapper(MyGrindPlugInManagerOpaqueHandle *bar)
{
    Grind::PluginManager* baz = (Grind::PluginManager*)bar;
    baz->foo();
}
通知家属抬走 2024-10-15 10:29:26

通常,插件接口是使用 c 命名和调用约定来定义的。名称修改可能与编译器相关(非标准)。

因此,最简单的解决方案是定义一些接口函数并将其声明为 extern "C"

extern "C"  {
    bool registerGrindPlugin( Grind::PluginManager* mgr );
}

您可能必须将 Grid::PluginManager 替换为 void* 类型和内部强制转换。这必须是一个简单的结构,而不是具有虚函数的类。

如果您确实坚持处理损坏的名称,您可以查看“c++filt”的源代码,它是一个 GNU 实用程序,也可在 OS X 上使用。

Usually, plugin interfaces are defined with c-naming and calling conventions. Name mangling is potentially compiler dependent (non standard).

So the easiest solution is to define some interface function and declare that as extern "C":

extern "C"  {
    bool registerGrindPlugin( Grind::PluginManager* mgr );
}

You may have to replace the Grid::PluginManager with a void* type and an internal cast. This has to be a simple structure, rather than a class with virtual functions.

If you do insist on dealing with mangled names you can take a look at the source code of 'c++filt' which is a gnu utility that is also available on OS X.

别在捏我脸啦 2024-10-15 10:29:26

您可以在 XCode 中设置链接器标志,方法是在项目窗口的左侧窗格中选择要构建的目标并单击“信息”按钮。在“构建”选项卡中,有一整部分链接器设置,其中包括一个名为“其他链接器标志”的部分;这应该让您指定您想要尝试的任何链接器选项。

You can set linker flags in XCode by selecting the target you're trying to build in the left-hand pane of the project window and clicking the "info" button. In the "build" tab, there's a whole section of linker settings, including one called "Other Linker Flags"; that should let you specify whatever linker options you want to try.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文