Compiling

讓 ld 選擇正確的庫

  • October 16, 2018

我正在嘗試編譯一個程序prog並將其與 OpenSSL 的 1.0.2 測試版連結,該測試版是從原始碼建構並安裝在/usr/local/ssl-1.0.2. 在使用 0.9.8 的舊系統上,這可以正常工作。在安裝了 1.0.1 的更新系統上,這需要更多的工作。我想知道為什麼。

  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 正確連結。

  1. 在 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),但不是執行時庫路徑。

  1. 如何讓它在 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_PATHor ld.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-sharedmain 中的目標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()

引用自:https://unix.stackexchange.com/questions/137784