【问题标题】:Global names between main and shared library not visible主库和共享库之间的全局名称不可见
【发布时间】:2013-05-19 16:54:00
【问题描述】:

我用 gcc 编译了一个共享库并将其链接到我的主库。主类应该初始化一个记录器类,它应该在共享库中可见,但看起来好像共享库有它自己的实例。

包含文件如下所示:

extern Log gLog;

在 main 中声明它。

Log gLog(new StreamWriter());

当我尝试链接它时,我在共享库中收到链接器错误undefined symbol _gLog。我以为可能是因为它是一个类实例,所以我把它改成了一个指针,但我得到了同样的结果。更糟的是,我想我可以创建一个小的虚拟模块,在共享库中创建相同的全局变量,然后调用一个函数来初始化它。但是对于这个函数,我也会得到一个链接器错误,因为它在 main 中不可见。

在共享库中:

Log *gLogger;

int initLibrary(Log *pLogger)
{
    gLogger = pLogger;
}

主要是:

Log gLog(new StreamWriter());
int initLibrary(Log *pLogger);
main()
{
    initLibrary(&gLog);
}

我再次在链接器中得到一个未定义的符号,这次是我的 initLibrary 函数。

现在我通过创建一个有效的虚拟类来解决问题。但是,我想知道如何正确定义跨共享库边界的符号,因为我的理解似乎是错误的。

使用 google 时,我在这里找到了一些线程 Using a global variable in a shared libraryGlobal variables, shared libraries and -fPIC effect 作为示例(还有其他几个与此问题有关的问题)。但是我尝试使用 -fpic 重新编译所有内容,这也是主模块,但它仍然无法正常工作。 -rdynamic 选项未知,所以我不知道它来自哪里。

我可以使用共享库中的类,反之亦然,所以这只影响全局符号。那我做错了什么,主代码和共享库看不到彼此的符号?

【问题讨论】:

  • 我想这与 C++ 的名称修饰机制有关。

标签: c++ shared-libraries


【解决方案1】:

正确的方法是使用全局变量(如果封装在命名空间中更好)或 Singleton 类,在共享库内部创建 Logger 的实例。然后让你的主程序使用它。

【讨论】:

  • 实际上我确实为其中的一些东西使用了命名空间。但是,我认为让 main 控制记录器会更合乎逻辑,但是当我考虑您的答案时,这是有道理的,所以我想我会相应地更改它。但是,这并没有回答如何在共享库和主模块之间正确共享名称的基本问题。
【解决方案2】:

您需要声明具有默认可见性的变量,以使其对其他共享库或主程序可见。您似乎正在使用-fvisibility=hidden 进行编译,因此库中的符号不​​会解析为主程序或其他库中的定义,反之亦然。使用 GCC 的可见性属性,您可以逆转这种效果。

简而言之

  • 范围跨单个对象文件的实体(全局、本地、..)的声明的可见性
  • 链接跨多个对象文件(外部、内部)实体的声明的可见性
  • visibility 跨不同共享库实体的声明的可见性(默认,隐藏)。

另一种可能性是您将 C 和 C++ 代码混合在一起并弄乱了语言链接。

【讨论】:

  • 我不知道这个可见性开关,所以我使用默认值。谢谢指点,我看看是不是这个问题。我想我必须使用命名空间来避免名称冲突,但当我使用这个开关时,对吧?
【解决方案3】:

我终于找到了为什么这不适用于全局符号的问题(类没有问题)。

我在 cygwin 下编译,显然共享对象被编译为 DLL。我试图查看该库并注意到它是 EXE 格式而不是 ELF。因此,我尝试使用 Microsoft DLL 语法来导出符号,然后它突然起作用了。将__declspec(dllexport) 添加到符号就可以了。

我预计我必须在主项目中使用__declspec(dllimport) 来导入符号,但这不起作用。不确定我是否误解了这个参数,或者 cygwin 版本的 gcc 是否有一些神奇的工作。

所以当你在 cygwin 下编译共享库时,如果你想导出符号,它必须看起来像这样。

共享库 foo.h:

__declspec(dllexport) int foo(int a);

共享库 foo.cpp:

int foo(int a)
{
 ....
}

在可执行文件 foo.h 中:

int foo(int a);

在可执行的main.cpp中:

main()
{
     foo(1);
}

共享库必须使用-fpic 开关编译并与-shared 链接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-11
    • 2020-05-11
    • 2014-03-16
    • 1970-01-01
    • 2019-08-09
    • 1970-01-01
    • 2013-08-03
    • 2014-05-17
    相关资源
    最近更新 更多