clang与g++链接含重定义符号共享库的差异及clang适配方案
解决Mac Clang下共享库函数优先调用本地重定义函数的问题
问题背景
项目核心代码编译为包含foo()、bar()的共享库,可执行文件由main.cpp及重定义核心库函数的本地文件(如含bar()的b.cpp)构成,要求本地定义的函数优先被调用。Linux环境下g++编译运行符合预期,但Mac环境下使用Clang编译时,共享库内foo()调用的是库自身的bar(),而非本地重定义的版本。
代码示例
a.h
#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; }
main.cpp
#include "a.h" #include <iostream> int main(){ std::cout<<"I AM MAIN"<<std::endl; foo(); return 0; }
原有编译命令与结果
Linux下g++编译命令
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++运行结果
I AM MAIN I am foo in a.cpp I AM bar in b.cpp
Mac下Clang原有编译命令(运行不符合预期)
clang -fPIC -shared -o liba.dylib a.cpp clang -c b.cpp clang -c main.cpp clang -o main main.o b.o -L. -la
Clang原有运行结果
I AM MAIN I am foo in a.cpp I am bar in a.cpp
解决方案
问题根源在于Mac的动态链接器(dyld)默认使用两级命名空间,共享库会优先绑定自身内部的符号;而Linux的动态链接器使用单级命名空间,会全局查找符号并优先使用可执行文件中的定义。要让Clang实现g++的行为,需修改链接选项强制使用单级命名空间:
修改后的Mac Clang编译链接命令
# 编译共享库(Mac下标准后缀为.dylib) clang -fPIC -shared -o liba.dylib a.cpp # 编译目标文件 clang -c b.cpp clang -c main.cpp # 链接时添加-flat_namespace和-undefined,suppress选项 clang -o main main.o b.o -L. -la -Wl,-flat_namespace,-undefined,suppress
验证结果
运行修改后的可执行文件,输出与Linux g++一致:
I AM MAIN I am foo in a.cpp I AM bar in b.cpp
补充说明
-flat_namespace:强制链接器使用单级命名空间,符号查找范围覆盖整个进程(可执行文件+所有共享库)-undefined,suppress:配合前者使用,抑制未定义符号的报错(单级命名空间下会延迟符号解析)
内容的提问来源于stack exchange,提问作者Andrea Celentano
相关产品推荐
相关产品推荐

