讓 ld 選擇正確的庫
我正在嘗試編譯一個程序
prog
並將其與 OpenSSL 的 1.0.2 測試版連結,該測試版是從原始碼建構並安裝在/usr/local/ssl-1.0.2
. 在使用 0.9.8 的舊系統上,這可以正常工作。在安裝了 1.0.1 的更新系統上,這需要更多的工作。我想知道為什麼。
- 在 Ubuntu 10.04 上,使用 OpenSSL 0.9.8: =====================================
以下是我針對 1.0.2 進行編譯和連結的步驟。
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install $ ldconfig $ ldconfig -p | grep libcrypto
=> 只顯示 0.9.8 個文件,所以我添加了 1.0.2 文件的路徑…
$ ldconfig /usr/local/ssl-1.0.2/lib $ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 libcrypto.so.0.9.8 (libc6, hwcap: 0x0008000000008000) => /lib/i686/cmov/libcrypto.so.0.9.8 libcrypto.so.0.9.8 (libc6, hwcap: 0x0004000000000000) => /lib/i586/libcrypto.so.0.9.8 libcrypto.so.0.9.8 (libc6, hwcap: 0x0002000000000000) => /lib/i486/libcrypto.so.0.9.8 libcrypto.so.0.9.8 (libc6) => /lib/libcrypto.so.0.9.8 libcrypto.so.0.9.8 (libc6) => /usr/lib/libcrypto.so.0.9.8 libcrypto.so (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so
所以我可以編譯
prog
…$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto $ ldd prog
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0x0083b000)
…並且它與 1.0.2 正確連結。
- 在 Debian Wheezy 上,使用 OpenSSL 1.0.1: ======================================
相同的步驟,不同的結果。
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install $ ldconfig $ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6, hwcap: 0x0008000000008000) => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0 libcrypto.so.1.0.0 (libc6, hwcap: 0x0004000000000000) => /usr/lib/i386-linux-gnu/i586/libcrypto.so.1.0.0 libcrypto.so.1.0.0 (libc6) => /usr/lib/i386-linux-gnu/libcrypto.so.1.0.0
同樣,我將路徑添加到 1.0.2 …
$ ldconfig /usr/local/ssl-1.0.2/lib $ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6, hwcap: 0x0008000000008000) => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0 libcrypto.so.1.0.0 (libc6, hwcap: 0x0004000000000000) => /usr/lib/i386-linux-gnu/i586/libcrypto.so.1.0.0 libcrypto.so.1.0.0 (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 libcrypto.so.1.0.0 (libc6) => /usr/lib/i386-linux-gnu/libcrypto.so.1.0.0 libcrypto.so (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so
然後我嘗試編譯…
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto $ ldd prog
=>
libcrypto.so.1.0.0 => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0 (0xb7591000)
但在這裡它與 1.0.2 沒有關聯。編譯時庫路徑是正確的(用
-L
,指定gcc
否則會失敗,因為其中使用的某些函式prog
特定於 1.0.2),但不是執行時庫路徑。
- 如何讓它在 Wheezy 上執行 ===================
有或沒有執行
ldconfig /usr/local/ssl-1.0.2/lib
:$ gcc -o prog ... -Wl,--rpath=/usr/local/ssl-1.0.2/lib -L/usr/local/ssl-1.0.2/lib -lcrypto $ ldd prog
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0xb7592000)
或者,在執行
export LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
之前執行gcc
。我想知道的
LD_DEBUG=libs ./prog
按照 mr.spuratic 的建議使用,我發現路徑是在/etc/ld.so.cache
. 我打開了那個文件,發現 .so 的查找順序對應於 .so 的輸出ldconfig -p
。所以實際的問題是:
- 為什麼 1.0.2 文件在 1) 中位於 ldconfig 列表的頂部,但在 2) 中卻沒有?純隨機?由於 1.0.1 和 1.0.2 文件具有相同的後綴而感到困惑?(“1.0.0”)
或者,換一種說法,
- 為什麼在 3) 中添加的標誌在 1) 中不是必需的?
編譯/連結非預設包時需要注意三件事:
- 標題(通常
CFLAGS
)- 編譯時庫路徑(通常
LDFLAGS
)- 執行時庫路徑 ( rpath via
LDFLAGS
,LD_RUN_PATH
,LD_LIBRARY_PATH
orld.so.conf
)你還沒有說它是什麼
prog
,所以我不能說它的配置可能有多好(或者如果它使用 autoconf?),我已經看到很多只可靠地執行前兩個步驟。在連結階段,庫路徑順序是相關的,假設您使用的是 GNU 工具鏈(gcc 和 binutils),您可能可以通過
CFLAGS
之前的設置configure
(或Makefile
直接設置)來查看發生了什麼:export CFLAGS="-Wl,-t"
這會將
-t
跟踪選項傳遞給連結器。如果您在製作過程中只得到簡潔的“CC”和“LD”行輸出,您可能需要添加V=1
或添加VERBOSE=1
到製作命令。)在執行時,您可以通過仔細設置來查看
ld.so
嘗試的內容LD_DEBUG
,例如LD_DEBUG=libs ./myprog
(或嘗試 或 的值以
files
獲取symbols
更多詳細資訊)要在建構時正確指定所有三個參數,您應該能夠:
export CFLAGS="-I/usr/local/ssl-1.0.2/include"
export LDFLAGS="-L/usr/local/ssl-1.0.2/lib -R/usr/local/ssl-1.0.2/lib"
然後重新配置/重新編譯。
您正在使用
--openssldir
而不是更傳統的--prefix
(我推薦後者,並且make install_sw
如果您不需要 1000 個左右的手冊頁和符號連結,則預設安裝會為您提供)。這可能是問題的一部分。由於某種原因,您顯示的 .so 庫已知ld.so
沒有版本後綴(例如.so.1.0.2
),因此應該使用正確的 "make install
" 為您設置它(通過link-shared
main 中的目標Makefile
)。該
-R
選項指示連結器將 RPATH 嵌入到特定 OpenSSL 庫的可執行輸出中,以便它不需要依賴執行時連結器 (ld.so
) 通常提供的預設值。您可以改為修改現有的二進製文件chrpath
。這或多或少等同於導出
LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
. 您可以在此處閱讀有關 RPATH 和相關 RUNPATH 的更多資訊:http: //blog.tremily.us/posts/rpath/作為最後的手段,您可以在沒有“shared”或“noshared”的情況下建構 OpenSSL,這將為您提供不會出現此問題的靜態庫(但很可能有其他問題,例如在 ELF .so 中使用,導致 PIC /PIE 問題)
根據更新的細節,我認為問題在於 1.0.1 和 1.0.2beta 都將 .so 版本後綴(SONAME)設置為 1.0.0。在只有 0.9.8 的第一個系統上,這不會導致任何問題;在第二個版本中,1.0.1 和 1.0.2 都版本為 1.0.0,它是基於
ld.so.{conf,d}
排序的“第一場比賽獲勝”。請記住,ld
編譯時連結器與執行時連結器是不同的程序ld.so
,並且可能具有不同的行為(通常會導致符號錯誤或更糟,如您所見)。$ cd /usr/local/src/openssl/openssl/1.0.2beta1 $ readelf -a libssl.so | grep SONAME 0x0000000e (SONAME) Library soname: [libssl.so.1.0.0] $ cat verchk.c int main(int argc, char *argv[]) { printf("build: %s\n",OPENSSL_VERSION_TEXT); printf("run : %s\n",SSLeay_version(SSLEAY_VERSION)); return 0; } $ gcc -Wall -I/usr/local/src/openssl/openssl-1.0.2-beta1/include \ -Wl,-rpath /usr/local/src/openssl/openssl-1.0.2-beta1/ \ -o verchk /usr/local/src/openssl/openssl-1.0.2-beta1/libcrypto.so verchk.c $ ./verchk build: OpenSSL 1.0.2-beta1 24 Feb 2014 run : OpenSSL 1.0.2-beta1 24 Feb 2014 $ grep SHLIB_M...R= Makefile SHLIB_MAJOR=1 SHLIB_MINOR=0.0
更新 OpenSSL-1.1 進行了一些 API 級別更改,上述程式碼將無法使用 v1.1 標頭檔和舊庫 (
undefined reference to
OpenSSL_version’`) 進行編譯。
SSLeay_version()
現在已棄用並且(取決於OPENSSL_API_COMPAT
)可能是#define
-d 到正確的 API 函式OpenSSL_version()
。