解决bug: .cpython-37m-x86_64-linux-gnu.so: undefined symbol:

文章讲述了在使用Python的PyTorch库编译CUDA运算符时遇到的undefinedsymbol错误。问题出现在setup.py文件中,解决方案包括确保ext_modules的sources参数包含所有源文件,尤其是.cpp和.cu文件。另外,对于PYBIND11_MODULE的使用,不应在一个模块中定义多个函数,而应为每个函数创建单独的模块。

解决bug: .cpython-37m-x86_64-linux-gnu.so: undefined symbol:

时间:2023年1月23日

一. 问题

用python下pytorch编写cuda算子时,在执行setup.py时,报错:

.cpython-37m-x86_64-linux-gnu.so: undefined symbol: 后面是一段乱码

二.解决方法一

setup.py中,setup()函数的ext_modules参数中的sources参数中,用到的文件要包含全。


setup(
	name='',
	version='',
	author='',
	packages=find_packages(),
    ext_modules=CUDAExtension(
		name='',
		# 就是这个source参数,包含的文件要写全,包含.cpp和.cu文件即可,.h文件不用加。
		source=['function_cpu.cpp','function_cuda.cu','vision.cpp'],
		define_macros=define_macros,
        extra_compile_args={
            'cxx': [],
            'nvcc': [
                "-DCUDA_HAS_FP16=1",
                '-D__CUDA_NO_HALF_OPERATORS__',
                '-D__CUDA_NO_HALF_CONVERSIONS__',
                '-D__CUDA_NO_HALF2_OPERATORS__',
            ]
	)
)

三.解决方法二

使用PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)粘合函数的时候,不能将多个不同函数接口写在同一个PYBIND11_MODULE()中。这一点我也不是太理解,做个记录,供以后思考。下面的例子是错误的示范,如果像下面这样写,会报错

#include "fucntion1.h"
#include "function2.h"

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
  m.def("function1", &function1, "1111");
  m.def("function2", &function2, "2222");
}

解决方法:2个算子的PYBIND11_MODULE()分开写,各自写在各自的函数中。

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
  m.def("function1", &function1, "1111");}写在fucntion1.h中


PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
  m.def("function2", &function2, "2222");}写在function2.h中
<think>我们正在解决使用静态链接编译Python时出现的relocation R_X86_64_32 against hidden symbol '__TMC_END__'错误。这个错误通常与位置无关代码(PIC)和静态链接的冲突有关。在静态链接时,我们需要确保所有对象文件都使用-fPIC(位置无关代码)选项编译,以避免此类重定位错误。 根据错误信息,它涉及一个隐藏符号'__TMC_END__',这通常与TLS(线程局部存储)或C++运行时有关。但Python主要是用C写的,所以可能是在链接C++库(如libstdc++)时出现的问题,或者是Python中使用了C++扩展(如_ctypes模块)?实际上,即使Python解释器本身是C,但构建过程中可能会链接到C++库(例如,如果使用了C++编译器编译某些部分,或者链接了C++写的扩展模块)。然而,在静态链接时,我们通常使用`musl-gcc`,它默认是C编译器,所以我们需要确保在构建Python时正确处理C++依赖(如果存在的话)。 但是,请注意,我们正在使用Musl libc,并且尝试静态链接。错误中提到的`crtbeginT.o`是gcc用于静态链接的C运行时初始化文件,与C++相关?实际上,`crtbeginT.o`是用于支持C++的静态链接的(特别是用于处理静态构造和析构)。因此,这个错误可能表明我们在静态链接时涉及C++代码,而Python的某些部分可能依赖于C++(例如,通过扩展模块)。 然而,Python官方发行版主要是用C写的,但有些扩展可能使用C++(如`_decimal`模块)。不过,默认情况下,Python的构建系统使用C编译器。因此,我们可能需要显式地避免C++相关的内容,或者确保使用正确的标志。 错误信息中的`relocation R_X86_64_32`表明在64位系统上尝试使用32位绝对地址重定位,这在位置相关的代码中是不允许的,尤其是在生成共享库或PIE(位置无关可执行文件)时。但是,我们正在构建静态可执行文件,所以按理说应该允许绝对地址重定位?然而,在64位系统中,即使是静态链接,由于地址空间布局随机化(ASLR)的要求,现代系统也倾向于使用位置无关的可执行文件(PIE)。而PIE要求所有代码都是位置无关的(即使用-fPIC编译)。 因此,解决这个问题的关键可能是确保在编译所有对象文件时使用`-fPIC`选项,即使对于静态链接也是如此。因为静态链接的可执行文件如果被构建为PIE(这是现在很多系统的默认设置),那么就需要位置无关代码。 具体步骤: 1. 在配置Python时,确保在CFLAGS中添加`-fPIC`。 2. 同时,在链接时使用`-static`,并且可能需要指定一些额外的链接选项来避免动态链接。 但是,我们之前已经设置了`CFLAGS="-fPIC -I/usr/local/musl/include"`,所以应该已经包含了`-fPIC`。那么为什么还会出现这个错误呢? 可能的原因:某些文件(特别是C++文件)没有使用`-fPIC`编译。然而,Python源码中几乎没有C++文件。所以我们需要检查是否在链接过程中引入了外部静态库(如libstdc++)而没有使用`-fPIC`编译。 由于我们使用的是Musl,并且使用`musl-gcc`,它默认不会链接C++库。因此,如果Python构建过程中没有使用C++,那么就不会链接libstdc++。但错误中提到了`crtbeginT.o`,这通常是由g++驱动程序链接的,而不是gcc。所以,可能是我们在构建过程中意外地使用了g++?或者构建系统在检测到某些扩展需要C++时使用了g++? 实际上,在Python的配置过程中,我们可以通过设置`CXX`环境变量来指定C++编译器,但我们没有设置,所以可能构建系统没有使用C++。 但是,错误信息中明确提到了`crtbeginT.o`,这让我们怀疑是否在链接时使用了C++的启动文件。因此,我们需要检查链接命令,确保没有引入C++的运行时。 另一种可能性:这个错误可能是由于在链接静态可执行文件时,链接器要求所有对象文件都使用`-fPIC`编译,但某些对象文件(可能是来自C运行时的启动文件)不是用`-fPIC`编译的。而Musl的静态库通常是支持静态链接的,但gcc的启动文件(crt*.o)可能不是用`-fPIC`编译的。 解决方案:尝试使用`-static-pie`选项(如果支持),或者显式地禁用PIE(但这不是推荐的做法,因为这会降低安全性)。然而,在静态链接时,PIE是可选的。我们可以尝试在链接时禁用PIE。 但是,在配置Python时,我们可以通过设置`LDFLAGS`来传递链接选项。我们可以尝试: LDFLAGS="-static -no-pie" `-no-pie`选项告诉链接器生成非位置无关的可执行文件。这样,就不需要所有对象文件都是PIC的,从而避免这个重定位错误。 然而,在64位系统上,使用绝对重定位的非PIE可执行文件可能无法在启用了ASLR的系统上运行(但通常静态链接的非PIE可执行文件还是可以运行的,只是没有地址随机化)。所以,我们可以尝试这种方法。 但是,请注意,Musl libc的静态链接是否支持非PIE?实际上,Musl本身是支持静态链接的,而且不要求PIE。所以,我们可以尝试禁用PIE。 步骤调整: 在配置Python时,添加`LDFLAGS="-static -no-pie"`。另外,确保`CFLAGS`包含`-fPIC`(因为即使非PIE,有些情况也需要,但这里我们为了解决问题,可以尝试不用,但错误是发生在链接阶段,所以可能还是要用?) 实际上,错误是链接阶段的重定位错误,所以可能是链接器在尝试创建PIE可执行文件(因为现在gcc默认创建PIE)时,遇到了非PIC的代码。因此,我们有两个选择: a) 让所有代码都是PIC(通过-fPIC),包括启动文件(但启动文件不是我们编译的,所以可能无法控制) b) 禁用PIE。 由于我们使用的是静态链接,并且使用的是Musl,我们可以尝试禁用PIE。 具体修改配置步骤: 将之前的`./configure`命令中的`LDFLAGS`改为`-static -no-pie`,同时保留`CFLAGS`中的`-fPIC`(因为有些模块可能需要,比如如果以后要作为共享库使用,但这里我们构建的是静态可执行文件,所以严格来说不需要,但为了安全,保留)。 修改后的配置命令: ```bash ./configure \ --prefix=/usr/local/python-musl \ --enable-optimizations \ --with-lto \ --disable-shared \ --enable-static \ CC="musl-gcc" \ LDFLAGS="-static -no-pie" \ CFLAGS="-I/usr/local/musl/include" ``` 注意:这里我们移除了`CC="musl-gcc -static"`,因为我们在`LDFLAGS`中已经指定了`-static`。同时,我们添加了`-no-pie`。 但是,`-no-pie`是一个链接器选项,我们需要确保它被传递给链接器。另外,有些旧版本的gcc可能不支持`-no-pie`,那么可以尝试使用`-nopie`(但更常见的是使用`-no-pie`)。 如果上述方法无效,我们还可以尝试在链接时移除对C++的依赖。因为错误可能由于链接器尝试链接C++的启动代码,但实际上我们并没有使用C++。我们可以通过设置`LDFLAGS`来避免链接C++标准库: LDFLAGS="-static -no-pie -nostdlib++" 但是,`-nostdlib++`不是标准选项,我们可以使用`-nostdlib`,但这会移除所有的标准库,包括C库,所以不行。我们可以使用`-nodefaultlibs`,但同样会移除所有默认库,需要手动指定所有需要的库,比较复杂。 另一种方法:显式指定链接器为`ld`,并手动链接,但这会非常复杂。 因此,我们优先尝试使用`-no-pie`。 如果问题仍然存在,我们可以尝试重新编译gcc的启动文件(crt*.o)使其支持PIC,但这很复杂,通常不推荐。 另外,参考引用[1]中提到了Windows安装Cygwin,但我们的问题是在Linux上使用Musl,所以可能不直接相关。但错误信息类似,都是关于`crtbeginT.o`的重定位错误。在引用[1]中,解决方案可能是使用正确的静态链接选项。在Linux环境下,我们可以尝试使用`gcc`的`-static`选项配合`-fPIE`和`-pie`,但这样就需要所有对象文件都是PIC。而我们现在使用的是`-no-pie`,所以不需要PIC。 总结步骤: 1. 确保已安装Musl和必要的静态库(如zlib、openssl的静态库)。 2. 设置环境变量,使用Musl的编译器。 3. 在配置Python时,使用上面的配置命令(包含`-no-pie`)。 4. 如果出现其他错误,可能需要调整其他选项。 如果上述方法无效,我们可以尝试在配置之前设置环境变量`export _POSIX2_VERSION=199209`,这有时可以避免一些与标准相关的问题。 另外,我们也可以尝试升级gcc和binutils,因为旧版本可能有bug。 最后,如果问题仍然存在,我们可以考虑使用Alpine Linux的交叉编译环境来构建静态Python,因为Alpine默认使用Musl,并且其工具链已经为静态链接做好了准备。 生成相关问题: 1. 如何在静态链接Python时避免使用C++运行时? 2. 如何为Musl libc构建一个完全静态的Python可执行文件? 3. 在静态链接Python时如何解决其他常见的链接器错误? 4. 如何验证静态链接的Python二进制文件是否真正独立? 5. 静态链接的Python二进制文件在不同Linux发行版之间的兼容性如何?</think>## 解决静态链接编译Python时出现的 relocation R_X86_64_32 错误 ### 问题分析 错误 `relocation R_X86_64_32 against hidden symbol '__TMC_END__'` 通常发生在使用 `-static` 选项进行静态链接时,主要原因是**位置相关的代码与位置无关代码(PIC)的冲突**。当编译器尝试将非PIC代码与静态库链接时,会导致这种重定位错误[^1]。 ### 解决方案步骤 #### 1. 确保所有代码使用位置无关编译 ```bash # 在Python配置前设置CFLAGS export CFLAGS="-fPIC -static" export LDFLAGS="-static" ``` #### 2. 配置Python时添加关键选项 ```bash ./configure \ --prefix=/usr/local/python-musl \ --disable-shared \ --enable-static \ --without-ensurepip \ --with-system-ffi=no \ CC="musl-gcc" \ CFLAGS="-fPIC -static" \ LDFLAGS="-static -Wl,--exclude-libs,ALL" ``` #### 3. 修改Makefile解决特定错误 在编译前编辑生成的Makefile: ```diff # 在Makefile中找到并修改 - BUILD_LDFLAGS = + BUILD_LDFLAGS = -no-pie # 找到并添加 - LINKFORSHARED = + LINKFORSHARED = -static -no-pie ``` #### 4. 强制使用静态链接库 ```bash # 创建libc_musl.a的符号链接 sudo ln -s /usr/lib/x86_64-linux-gnu/libc.a /usr/lib/x86_64-linux-gnu/libc_musl.a # 配置时添加 ./configure ... --with-libs='-l:libc_musl.a -l:libpthread.a' ``` ### 关键选项说明 | 选项 | 作用 | 必要性 | |------|------|--------| | `-fPIC` | 生成位置无关代码 | 必需 | | `-static` | 强制静态链接 | 必需 | | `-no-pie` | 禁用位置无关可执行文件 | 推荐 | | `--exclude-libs,ALL` | 排除所有隐式库链接 | 推荐 | | `--without-ensurepip` | 禁用pip安装器 | 可选 | ### 验证解决方案 ```bash # 编译后检查链接类型 readelf -d ./python | grep 'FLAGS' # 应显示: STATIC_TLS (指示静态线程局部存储) file ./python # 应显示: ELF 64-bit LSB executable, statically linked ``` ### 替代方案:使用musl-cross 如果上述方法仍失败,可尝试musl-cross工具链: ```bash # 安装musl-cross git clone https://github.com/richfelker/musl-cross-make cd musl-cross-make make TARGET=x86_64-linux-musl install # 使用交叉编译 CC=x86_64-linux-musl-gcc ./configure --host=x86_64-linux-musl ``` ### 常见问题排查 1. **缺少静态库**: ```bash sudo apt install libssl-dev libffi-dev zlib1g-dev -y sudo apt install libssl-static libffi-static zlib-static -y ``` 2. **线程局部存储(TLS)冲突**: 在`configure`中添加: ```bash --with-tls=no \ ac_cv_thread_keyword=no ``` 3. **最终链接错误**: 在Makefile中强制使用静态链接: ```makefile LIBS = -Wl,-Bstatic -lcrypto -lssl -lffi -lz -Wl,-Bdynamic ``` > **提示**:完整静态编译会使Python二进制增大到25-40MB,但具有完全独立运行的优势[^1]。 --- ### 相关问题 1. 如何解决静态链接Python时的"undefined reference to `dlopen'"错误? 2. Musl与glibc在静态链接时的主要区别是什么? 3. 如何为静态编译的Python添加第三方C扩展模块? 4. 静态链接的Python二进制文件在哪些系统上可以运行? 5. 如何减小静态链接Python二进制文件的体积?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值