返回

FreeBSD 编译 PHP 8.4 报错 R_X86_64_32 解决指南

php

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 标志。

  1. 进入 zlib 源码目录:
    通常,zlib 的源码可能在 /usr/local/src/zlib-版本号 或类似的地方。 假设zlib源码在/usr/local/zlib-1.3.1下, 使用 cd /usr/local/zlib-1.3.1进入。

  2. 配置并编译 (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: 安装。
  3. 如果 ./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 重新编译该库.