Clang下重写__cxa_throw遇重复符号错误(GCC无此问题)
问题描述
我希望增强__cxa_throw的信息输出,该方案仅计划用于Debug构建。相关代码在GCC 4.8.5下编译运行完全正常,但在Clang 12.0.1中链接时出现如下错误:
ld.lld: error: duplicate symbol: __cxa_throw >>> defined at exc_override.cpp >>> exc.o:(__cxa_throw) >>> defined at cxa_exception.cpp >>> cxa_exception.cpp.o:(.text.__cxa_throw+0x0) in archive /opt/compilers/clang-12.0.1/bin/../lib/x86_64-unknown-linux-gnu/c++/libc++.a
涉及代码文件
main.cxx
#include <iostream> #include <stdexcept> #include "exc_override.h" int main(int argc, char** argn) { try { throw std::runtime_error("boom"); } catch(...) { std::cout << "caught" << std::endl; } return 0; }
exc_override.h
#pragma once extern "C" void __cxa_throw (void *thrown_exception, void *pvtinfo, void (*dest)(void *)) ;
exc_override.cpp
#include "exc_override.h" #include <cstdlib> #include <dlfcn.h> #include <execinfo.h> #include "stdio.h" typedef void (*cxa_throw_type)(void *, void *, void (*) (void *)); cxa_throw_type orig_cxa_throw = 0; void load_orig_throw_code() { orig_cxa_throw = (cxa_throw_type) dlsym(RTLD_NEXT, "__cxa_throw"); } extern "C" void __cxa_throw (void *thrown_exception, void *pvtinfo, void (*dest)(void *)) { printf(" ################ DETECT A THROWN !!!!! #############\n"); if (orig_cxa_throw == 0) load_orig_throw_code(); orig_cxa_throw(thrown_exception, pvtinfo, dest); }
编译命令
GCC编译命令
gcc main.o exc_override.o -o testprogram g++ main.o exc_override.o -o testprogram g++ main.o exc_override.o -o testprogram -ldl
Clang编译命令
clang++ -c main.cxx -o main.o clang++ -c exc_override.cpp -o exc.o clang++ main.o exc.o -o testprogram
请问该如何解决Clang下的重复符号问题?
解决方法
冲突根源是Clang默认链接静态版本的libc++,其中已包含__cxa_throw的实现,而你的代码又定义了同名符号,导致链接器报错。以下是几种针对性的解决方式:
1. 动态链接libc++(推荐适配Debug场景)
既然你的方案仅用于Debug构建,可通过动态链接方式让自定义实现优先加载,同时保证能找到原函数:
修改Clang链接命令,添加-ldl(支持dlsym)并指定动态版libc++:
clang++ main.o exc.o -o testprogram -ldl -shared-libc++
动态库版本的libc++会通过RTLD_NEXT被你的自定义函数找到原实现,完全避开静态库的符号冲突。
2. 将自定义__cxa_throw标记为强符号优先
通过链接器选项强制指定使用你实现的__cxa_throw,忽略静态库中的版本:
clang++ main.o exc.o -o testprogram -ldl -Wl,--exclude-libs=libc++.a -Wl,--defsym=__cxa_throw=exc.o:__cxa_throw
该命令直接告诉链接器,__cxa_throw符号采用exc.o中的定义,排除静态库中的同名符号。
3. 仅在Debug构建中编译自定义实现
利用编译宏控制,只在Debug模式下包含__cxa_throw的自定义代码:
修改exc_override.cpp:
#ifdef DEBUG extern "C" void __cxa_throw (void *thrown_exception, void *pvtinfo, void (*dest)(void *)) { printf(" ################ DETECT A THROWN !!!!! #############\n"); if (orig_cxa_throw == 0) load_orig_throw_code(); orig_cxa_throw(thrown_exception, pvtinfo, dest); } #endif
编译Debug版本时添加-DDEBUG宏:
clang++ -c exc_override.cpp -o exc.o -DDEBUG clang++ main.o exc.o -o testprogram -ldl -shared-libc++ -DDEBUG
Release构建不会包含自定义符号,自然不会有冲突。
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

