clang 和 g++ 在链接共享库(其中存在已在本地对象文件中定义的符号)方面的区别

问题描述 投票:0回答:1

我在处理最近开始从事的 C++ 项目时遇到了一个问题。该项目的组织如下(我无法更改):

  • 代码的核心部分位于中央共享库中。我可以访问中央库源文件,并且最终可以修改当前的编译过程。为了便于讨论,我们假设中央库
    liba.so
    是通过编译包含两个函数
    a.cpp
    void foo()
    的单个文件
    void bar()
    创建的。
  • 从这个核心部分开始,创建多个可执行文件。每个可执行文件都驻留在其自己的文件夹中,它是通过编译主代码
    main.cpp
    和其他源代码创建的,这些源代码也实现了核心库中已存在的一些功能。为了便于讨论,我们假设有一个文件 b.cpp 包含
    void bar()
    
    
  • 预期的项目设计是始终优先考虑本地源代码中定义的功能。该项目在 Linux 机器上使用 g++ 进行了测试,但在 Apple 机器上使用 clang 编译时显示出不同的行为。

我知道这种设计方式可能是完全应该避免的,但这不是我的责任。我正在检查该项目是否也可以用 clang 编译

可以使用以下代码重现此行为:

啊啊

#ifndef a_h_g #define a_h_g void foo(); void bar(); #endif

a.cpp

#include <iostream> #include "a.h" void foo(){ std::cout<<"I am foo in a.cpp"<<std::endl; bar(); } void bar(){ std::cout<<"I am bar in a.cpp"<<std::endl; }

b.cpp

#include "a.h" #include <iostream> void bar(){ std::cout<<"I AM bar in b.cpp"<<std::endl; }

主.cpp

#include "a.h" #include <iostream> int main(){ std::cout<<"I AM MAIN"<<std::endl; foo(); return 0; }

并编译为:

g++ -fPIC -shared -o liba.so a.cpp

g++ -c b.cpp

g++ -c main.cpp

g++ -o main main.o b.o -L. -la

在使用 g++ 版本 11.2.0 的 Centos7 机器上尝试此代码时,结果就是该项目的目标:

I AM MAIN I am foo in a.cpp I AM bar in b.cpp

相反,在 clang 版本 14.0.0 的 MAC 上,结果是:

I AM MAIN I am foo in a.cpp I am bar in a.cpp

有没有办法用 clang 重现 Centos7 结果?

c++ g++ clang shared-libraries
1个回答
0
投票
没有任何关系,而是与共享库在不同平台上的工作方式有关。

在 MacOS 上,共享库更喜欢

在同一共享库中定义的符号而不是全局定义的符号(类似于 Windows DLL 的工作方式)。

在 Linux 上,共享库更喜欢全局符号,除非该库与 -Bsymbolic

-Bsymbilic-functions

 链接。
来自 MacOS 链接器 
文档

Two-level namespace By default all references resolved to a dynamic library record the library to which they were resolved. At runtime, dyld uses that information to directly resolve symobls. The alternative is to use the -flat_namespace option. With flat namespace, the library is not recorded. At runtime, dyld will search each dynamic library in load order when resolving symbols. This is slower, but more like how other operating systems resolve symbols.

您应该能够通过将
-Wl,-flat_namespace
添加到

liba.so

 链接线来获得类似 Linux 的行为。
有没有办法用 clang 重现 Centos7 结果?

当然:只需将

-Wl,-Bsymbolic
添加到

liba.so

 链接线即可。来自 Linux 
man ld
:
-Bsymbolic
  When creating a shared library, bind references to global symbols
  to the definition within the shared library, if any.  Normally, it is
  possible for a program linked against a shared library to override the
  definition within the shared library.  This option is only meaningful
  on ELF platforms which support shared libraries.

重复您的命令:
g++ -fPIC -shared -o liba.so a.cpp
g++ -c b.cpp
g++ -c main.cpp
g++ -o main main.o b.o -L. -la -Wl,-rpath=.

./main
I AM MAIN
I am foo in a.cpp
I AM bar in b.cpp

现在用
liba.so
重建

-Bsymbolic

:
$ g++ -fPIC -shared -o liba.so a.cpp -Wl,-Bsymbolic
$ ./main
I AM MAIN
I am foo in a.cpp
I am bar in a.cpp

可能有一个标志要求 MacOS 链接器更喜欢全局范围,但我对 MacOS 不太了解。
    

© www.soinside.com 2019 - 2024. All rights reserved.