How to unmangle exported symbols from C++ in dynamic libraries in Xcode on macOS
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?
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();
}
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.
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.
精彩评论