FreeBSD 编译 PHP 8.4 报错 R_X86_64_32 解决指南
2025-03-17 07:54:39
PHP编译报错:relocation R_X86_64_32 cannot be used against local symbol
啥情况?
最近尝试在 FreeBSD 上从源码编译 PHP 8.4,并且启用了 GD 和 zlib 库。 系统环境长这样:
FreeBSD dev 14.2-RELEASE FreeBSD 14.2-RELEASE releng/14.2-n269506-c8918d6c7412 GENERIC amd64
结果执行 make
的时候,蹦出来一堆错误,像下面这样(只截取了开头几行):
ld: error: relocation R_X86_64_32 cannot be used against local symbol; recompile with -fPIC
>>> defined in /usr/local/lib/libz.a(gzwrite.o)
>>> referenced by gzwrite.c
>>> gzwrite.o:(gzwrite) in archive /usr/local/lib/libz.a
ld: error: relocation R_X86_64_32 cannot be used against local symbol; recompile with -fPIC
>>> defined in /usr/local/lib/libz.a(gzread.o)
>>> referenced by gzread.c
>>> gzread.o:(gzread) in archive /usr/local/lib/libz.a
ld: error: relocation R_X86_64_32S cannot be used against local symbol; recompile with -fPIC
>>> defined in /usr/local/lib/libz.a(gzlib.o)
>>> referenced by gzlib.c
>>> gzlib.o:(gz_open) in archive /usr/local/lib/libz.a
当时的 configure
命令是这样的(为了解决 libjpeg 的问题,设置了 PKG_CONFIG_PATH
):
setenv PKG_CONFIG_PATH /usr/local/lib/pkgconfig
./configure \
--with-apxs2=/usr/local/apache/bin/apxs \
--without-pdo-sqlite --without-sqlite3 --without-iconv \
--with-config-file-path=/usr/local/etc \
--enable-bcmath --without-cdb \
--enable-gd --without-iodbc \
--without-gdbm --with-ndbm --without-db2 --without-dbm \
--without-readline --with-openssl \
--without-db3 --enable-dba \
--with-curl \
--with-jpeg --enable-calendar \
--with-mhash --enable-mbstring=all \
--with-zlib \
--enable-exif --with-zip \
--with-mysqli=mysqlnd
咋回事?
简单来说,这个错误是链接器(ld)告诉我们,它在尝试链接 zlib 静态库(/usr/local/lib/libz.a
)时遇到了问题。错误信息 "relocation R_X86_64_32 cannot be used against local symbol; recompile with -fPIC" 指出,zlib 库在编译时没有使用 -fPIC
选项。
-fPIC
是啥? 它是 "Position Independent Code" 的缩写,意思是“位置无关代码”。 没用 -fPIC
编译出来的库,通常只能在固定地址加载,这种限制在一些情况下会出问题。这里的问题就是链接共享库的时候,静态库没有位置无关特性引起的冲突。
咋解决?
要解决这个问题,核心思路就是让 zlib 库以“位置无关”的方式重新编译。有几种方法可以搞定:
1. 重编译 zlib 库,加上 -fPIC
这是最直接的方法。找到 zlib 的源码目录,重新编译它,并在编译时加上 -fPIC
标志。
-
进入 zlib 源码目录:
通常,zlib 的源码可能在/usr/local/src/zlib-版本号
或类似的地方。 假设zlib源码在/usr/local/zlib-1.3.1
下, 使用cd /usr/local/zlib-1.3.1
进入。 -
配置并编译 (csh):
./configure --prefix=/usr/local make clean setenv CFLAGS "-fPIC" make make install
--prefix=/usr/local
: 指定安装路径,确保安装到正确位置,通常是/usr/local
。make clean
: 清理之前的编译残留。setenv CFLAGS "-fPIC"
: 这是关键,设置环境变量CFLAGS
,加入-fPIC
选项。make
: 编译。make install
: 安装。
-
如果
./configure
报错, 找不到文件:有时候zlib的源码包下载后需要先解压生成
configure
脚本:gzip -dc zlib-1.3.1.tar.gz | tar xf - #替换为你下载的包名 cd zlib-1.3.1
2. 使用系统包管理器(如果方便的话)
有些系统提供预编译的、带有 -fPIC
的 zlib 开发包。 如果能直接用包管理器安装,省时省力。
对于 FreeBSD, 你提到你已经用pkg install lzlib-1.14_1
, 理论上应该是可以的。 如果没有, 手动卸载旧版zlib, 然后重装试下:
pkg remove zlib # 或旧版本, 如 zlib-1.2.11,3
pkg install zlib
3. 在编译 PHP 时设置 LDFLAGS
(进阶)
如果在编译 PHP 时能指定额外的链接标志,也可以通过 LDFLAGS
告诉链接器去链接动态库版本的 zlib,而不是静态库。
setenv LDFLAGS "-L/usr/local/lib -lz" # 注意这里用 -lz,而不是 -llibz.a
./configure ... (你的 PHP configure 命令) ...
make
make install
这里,-L/usr/local/lib
告诉链接器在 /usr/local/lib
目录下找库文件,-lz
告诉链接器链接 zlib 库。系统通常会优先找动态库(.so
文件),如果找到了,就不会去找静态库(.a
文件),从而避免了问题。
这种方法为什么是“进阶”?
因为你得确定系统里确实有 zlib 的动态库。一般情况下,通过包管理器(像 pkg
)安装的 zlib,通常会同时包含静态库和动态库。 但如果你是手动编译安装的 zlib,而且没有特别指定,可能就只有静态库。
可以使用 find
命令确认有没有zlib.so存在:
find /usr/local/lib -name "libz.so*"
4.修改 PHP 源码的 Makefile
(非必要, 不推荐)
这是非常规操作. 一般不建议.
在极少数情况下,如果以上方法都不奏效,而且你对构建过程很熟悉,可以尝试直接修改 PHP 源码目录下的 Makefile
文件,找到和 zlib 相关的编译选项,手动加上 -fPIC
。 但这种方式比较麻烦,容易出错,而且在升级 PHP 时,改动可能会丢失,所以通常不推荐。
总结与建议
通常情况下,通过重新编译 zlib 并添加 -fPIC
选项(方法 1)或者通过包管理器安装 zlib 开发包(方法 2)就能解决问题。 方法3也是不错的选择。方法 4 不太推荐.
重编译 PHP 后,建议进行一次全面的测试,确保所有依赖 zlib 的功能都正常工作,比如 GD 库中的图像处理功能。
如果其他库也遇到相似的问题,一样的思路: 带 -fPIC
重新编译该库.