為 Linux 程式打包

2021-06-17 16:58:02 字數 3029 閱讀 2192

2012-09-14

最近有個專案需要把編譯好的 linux 程式打包後安裝到多種 linux 發行版上執行。由於是不同的發行版,所以不適合使用各個發行版自己的軟體包格式。即使針對特定發行版,還是會因為不同版本的系統庫版本不同而無法建立通用的軟體包。(程式既需要安裝到 fedora 6 這樣「古老」的版本上,也需要安裝到這兩年發布的發行版上。)比較幾種解決方案後最終選擇了打包開發系統上的動態鏈結庫,用指令碼指定 dynamic linker 和動態鏈結庫目錄的方式來執行打包後二進位制檔案的方案。

最先考慮的是靜態鏈結,但開發用的 ubuntu 並沒有提供所有依賴庫的靜態鏈結版本。自己重新編譯所有的庫比較麻煩,所以沒有用這個方案。

之後嘗試過 cde (後面會具體介紹),由於一些限制最後還是決定自己打包,這樣不依賴其他工具,可以獲得完全的控制。最開始的時候以為只要簡單的把程式依賴的動態鏈結庫找出來全部放到乙個目錄下,在其他系統上指定ld_library_path即可正常使用,但實際上這麼做還差了一步。

只指定ld_library_path時遇到了兩種錯誤:

google 之後發現 stack overflow 上也有人遇到過第二個問題。其實這兩個錯誤的原因都跟 dynamic linker (dynamic linking loader) 有關。

在使用 gnu c 庫的系統上,執行 elf 檔案時會自動執行 dynamic linker (在 32-bit linux 上一般是/lib/ld-linux.so.2,64-bit 上是/lib/ld-linux-x86-64.so.2),dynamic linker 會讀取ld_library_path然後在指定的目錄下找 shared library。但 dynamic linker 自身的路徑是硬編碼在 elf 檔案頭中的。

所以 64-bit 系統上報的錯誤真正的含義是找不到 dynamic linker,因為該系統上並沒有安裝 32-bit 的 c 庫。第二個錯誤應該是由於 fedora 6 上的 dynamic linker 和我打包的 ubuntu 的 c 庫不相容。(stack overflow 上的回答裡說 loader 是 c 庫的一部分,不過不相容的具體原因不清楚。)

解決這個問題的辦法是指定程式啟動使用的 loader,呼叫方式如下:

ld_library_path=/lib/ld-linux.so.2
/lib/ld-linux.so.2由於是 dynamic linker,所以支援這樣的呼叫方式。但其實通過ld_library_path環境變數來指定 library search path 有乙個缺點:程序會帶有這個環境變數。因此程序在嘗試執行系統上的其他命令時可能遇到上述的第二個問題。事實上應該盡可能避免使用ld_library_path這個環境變數,參考 why ld_library_path is bad。

好在 dynamic linker 可以使用引數來指定 library search path,如下:

/lib/ld-linux.so.2 --library-path
給 dynamic linker 指定引數後就會忽略ld_library_path

知道 dynamic linker 的問題之後,打包剩下的工作就是修改指令碼來用 dynamic linker 啟動可執行檔案了。

如果發布的程式經常要在 shell 裡互動使用,每次執行時指定 dynamic linker 還是會很不方便。實際上 dynamic linker 和 library path 資訊都儲存在 elf header 中的,程式在鏈結時可以指定這些資訊。

library path 可以通過傳遞--rpath=引數給鏈結器來指定,dynamic loader 則是通過--dynamic-linker=來指定。如果是由 gcc 來呼叫 linker,可以通過-wl,來把選項傳遞給 linker,如下:

gcc ... -wl,--rpath=-wl,--dynamic-linker=
可以使用readelf -a來檢視得到的 elf 檔案頭資訊,其中interp指定的就是 dynamic linker,而rpath就是 library search path。

因為不想修改 configure 指令碼,所以我最後沒有採用這種方式。

其實 linux 下打包程式的問題以前就有人嘗試解決過。前段時間讀了 philip j. guo 的 the ph.d. grind,他博士期間的一項工作 code data environment (cde) 就是用來為應用程式建立乙個自包含的包,然後發布到其他 linux 系統上去執行。掃過一眼 cde 的**,其最主要的工作機制是使用ptrace來攔截系統呼叫,從而捕獲程式依賴的檔案以及系統環境(例如環境變數)。簡單起見只考慮程式依賴的檔案,cde 工作其原理如下:

這個原理簡單有效,cde 實際使用下來我覺得也非常方便,10 分鐘內我就成功的把一台 fedora 6 上的 vim 安裝到另一台 debian 6 上並且正常使用了。對某些需求來說,cde 的確可以作為解決 linux dependency hell 乙個方便的工具。

然而 cde 打包後的程式是在 sandbox 中執行,不能訪問 sandbox 之外的檔案,除非使用 seamless execution mode。但要讓 seamless execution mode 正常工作比較 tricky,cde 對我來說畢竟是新工具,擔心了解不夠需要花太多時間 debug 所以最後沒有採用。(cde 執行應用程式時需要使用ptrace監控程式,對程式執行效能會有一點影響,不過影響不大,對我的應用來說效能不是主要問題。)

program library howto,這是一篇不錯的介紹 shared library 的文章。這裡是我做的一些筆記。

posted by chen yufei

2012-09-14 linux

將Python程式打包為exe

當python專案編碼完成之後,有時候我們需要讓其在沒有python環境的電腦上執行,此時我們就需要將python專案進行打包了,本篇文章主要是基於python的第三方庫pyinstaller進行的。使用pyinstaller打包python專案,相對於c和c 專案的打包要簡單很多。這個程式展示了使...

NSIS打包electron程式為exe安裝包

我們把out資料夾複製到桌面 不複製也可以,我是為了方便演示 開啟nisi 1 選擇視覺化指令碼編輯器 2 選擇新建指令碼 嚮導 5 設定安裝程式圖示 圖示必須是ico格式 名稱,語言 simpchinese 介面,然後下一步 7 授權檔案有就填,沒有就填空白 然後下一步 8 新增應用程式檔案,預設...

linux 打包qt程式

linuxdeploy的qtrelease包位址 mv liuxdeployqt 5cd usr local bin 進入bin目錄下 chmod a x linuxdeployqt 增加執行許可權cd usr local bin 進入bin目錄下 mkdir mydir mv 在最終打包之前還有幾...