在 ARM 嵌入式 Linux (如路由器、開發板) 上交叉編譯 PHP 5 後,`phpinfo()` 顯示:
Dynamic Library support => not available
導致 `dl()`、 `extension=xxx.so` 失效,無法載入任何動態擴展 (PDO、GD、MongoDB 等)。
PHP `configure` 階段自動檢測 `dlopen`、`dlsym` 等符號 (通常在 `libdl.so`)。交叉編譯時,**目標板上的庫路徑與編譯機不同**,導致 `configure` 以為目標系統不支援動態載入,將 `HAVE_LIBDL` 設為 0,進而禁用整個動態擴展機制。
# Makefile 頂部
LDFLAGS = -ldl $(LDFLAGS)
#define HAVE_LIBDL 1
#define HAVE_DLOPEN 1
#define HAVE_DLSYM 1
#define HAVE_DLERROR 1
#define HAVE_DLCLOSE 1
#if defined(HAVE_LIBDL) || defined(HAVE_MACH_O_DYLD_H)
#include
#include
#include // 關鍵:dlopen 系列函數宣告
#endif
make clean
make -j$(nproc)
make install
# 1. 準備工具鏈與 sysroot
export CROSS_COMPILE=arm-linux-gnueabihf-
export SYSROOT=/opt/arm-linux-gnueabihf/sysroot
export CC=${CROSS_COMPILE}gcc
export CXX=${CROSS_COMPILE}g++
export AR=${CROSS_COMPILE}ar
export RANLIB=${CROSS_COMPILE}ranlib
export STRIP=${CROSS_COMPILE}strip
export CFLAGS="--sysroot=$SYSROOT -O2 -pipe"
export LDFLAGS="--sysroot=$SYSROOT -Wl,-rpath-link=$SYSROOT/lib -Wl,-rpath-link=$SYSROOT/usr/lib"
# 2. 依賴庫交叉編譯 (zlib, libxml2, openssl, libcurl, libpng, libjpeg, freetype, libicu 等)
# 建議使用 Buildroot / Yocto / OpenWrt SDK 統一產生 sysroot,避免手動編譯依賴地獄。
# 3. PHP configure
./configure \
--host=arm-linux-gnueabihf \
--target=arm-linux-gnueabihf \
--prefix=/usr/local/php \
--with-config-file-path=/etc/php \
--enable-fpm \
--with-fpm-user=www \
--with-fpm-group=www \
--disable-cgi \
--disable-cli \
--disable-phpdbg \
--with-zlib-dir=$SYSROOT/usr \
--with-libxml-dir=$SYSROOT/usr \
--with-openssl-dir=$SYSROOT/usr \
--with-curl=$SYSROOT/usr \
--with-gd \
--with-jpeg-dir=$SYSROOT/usr \
--with-png-dir=$SYSROOT/usr \
--with-freetype-dir=$SYSROOT/usr \
--enable-mbstring \
--enable-sockets \
--enable-pdo \
--with-pdo-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--enable-bcmath \
--enable-json \
--enable-opcache \
--disable-rpath \
--disable-debug \
--without-pear \
--disable-short-tags \
--disable-ipv6 \
--disable-all \
--enable-libdl # 部分版本支援此選項,若無則靠後續強制宏
# 目標板上
php -m | grep -E 'pdo|gd|mbstring|json'
# 應列出已編譯為 shared 的擴展
php -r "dl('pdo_mysql.so'); echo extension_loaded('pdo_mysql') ? 'OK' : 'FAIL';"
# 輸出 OK 即成功
| 現象 | 原因 | 解決 |
|---|---|---|
| configure 报错 `cannot run test program while cross compiling` | AC_RUN_IFELSE 試圖在編譯機執行目標二進制 | 設定 `ac_cv_func_dlopen=yes ac_cv_func_dlsym=yes` 等快取變數,或使用 `--cache-file=config.cache` 預填 |
| 編譯通過但目標板 `dlopen` 失敗 `libphp5.so: cannot open shared object file` | RPATH 未正確指向庫目錄 | 編譯時加 `-Wl,-rpath=/usr/local/php/lib`;運行時 `LD_LIBRARY_PATH=/usr/local/php/lib` |
| 啟用擴展後 PHP-FPM 啟動 segfault | 擴展與 PHP 核心 API 版本不匹配 (ZEND_MODULE_API_NO) | 確保擴展與 PHP 同源碼編譯,或使用 `phpize` 重新編譯擴展 |
| 靜態編譯擴展 (--enable-xxx=static) 卻仍需 dlopen | 誤解:static 意味著編入 php 二進制,無需 dlopen | 嵌入式建議核心擴展 (pdo, mbstring, json) 全部 static,減少 .so 依賴 |
PHP 5 已停止維護 (EOL 2018-12-31)。新專案**強烈建議直接交叉編譯 PHP 8.2+**;預設啟用 `libdl`、OPcache JIT、Fiber、JIT 編譯,效能與安全性大幅提升。嵌入式場景可 `--disable-all` 只啟用必要擴展,並全部 `--enable-xxx=static` 產出單一 `php` 二進制,部署極簡。