編譯與chroot隔離實戰(zhàn))
簡介這份資源為CentOS 6.3環(huán)境下的源碼與工具類學(xué)習(xí)素材面向需要了解Linux服務(wù)器系統(tǒng)、源碼編譯及常用運維工具的開發(fā)者與運維人員。CentOS 6.3基于RHEL 6.3源碼構(gòu)建免費且穩(wěn)定適合企業(yè)級應(yīng)用部署與實驗環(huán)境搭建。壓縮包共19個文件以14個png圖片、3個js腳本、1個css樣式和1個html頁面為主整體約244KB結(jié)構(gòu)輕量便于快速瀏覽與本地調(diào)試。其中html與css構(gòu)成頁面骨架js負(fù)責(zé)交互邏輯png則提供界面素材可輔助理解前端資源組織方式。目前已有523人學(xué)習(xí)下載適合作為源碼工具類參考案例幫助讀者熟悉Linux下壓縮包解壓、文件分類與靜態(tài)資源結(jié)構(gòu)也可用于前端頁面搭建與腳本調(diào)試的入門練習(xí)。1. CentOS 6.3 源碼工具包老系統(tǒng)維護(hù)者的最后一根稻草如果你手里還跑著幾臺 CentOS 6.3 的物理機或虛擬機大概率不是因為戀舊而是因為某個業(yè)務(wù)系統(tǒng)綁死了老內(nèi)核、老 glibc動一發(fā)而牽全身。這個源碼工具包就是給這種場景準(zhǔn)備的——它不是系統(tǒng)鏡像而是一套能在 CentOS 6.3 上直接編譯、調(diào)試、打包的源碼級工具集合。適合誰適合那些被“升級即翻車”教育過、只想在現(xiàn)有環(huán)境里把問題摁下去的運維和嵌入式開發(fā)者。我見過太多人拿到老系統(tǒng)第一反應(yīng)是 yum install結(jié)果源早就失效最后只能對著報錯干瞪眼。這套東西的價值就在于它把編譯鏈條重新鋪了一遍讓你不用滿世界找 rpm 包。2. 源碼工具包拆解從 tar 包到可執(zhí)行文件的完整鏈路2.1 包里到底有什么目錄結(jié)構(gòu)與核心組件拿到壓縮包先別急著解壓到 /usr/local我一般會先tar -tzf看一眼頂層結(jié)構(gòu)。典型的 CentOS 6.3 源碼工具包會包含這幾類東西gcc-4.4.7/和binutils-2.20.51/這是 CentOS 6.3 自帶的編譯器版本但包里通常帶的是打過補丁的源碼能解決某些 C11 特性在舊 ABI 下的鏈接問題。glibc-2.12/別輕易動這個但包里會附帶glibc-compat的補丁文件用于修復(fù)某些老二進(jìn)制在 NPTL 線程模型下的崩潰。make-3.81/和automake-1.11/老版本的構(gòu)建工具新版 make 在解析老 Makefile 時反而會報“missing separator”這種玄學(xué)錯誤。openssl-1.0.1e/和openssl-1.0.2k/兩個版本并存因為有些老業(yè)務(wù)依賴 1.0.1 的 API而安全掃描又要求 1.0.2 的補丁級別。zlib-1.2.3/、bzip2-1.0.5/基礎(chǔ)壓縮庫編譯 Python 或 Ruby 時繞不開。注意不要用系統(tǒng)自帶的yum groupinstall Development Tools來替代CentOS 6.3 的官方源已經(jīng)歸檔直接裝大概率卡在http://mirror.centos.org超時。2.2 編譯環(huán)境初始化三行命令把依賴釘死在 CentOS 6.3 上編譯源碼最怕的是頭文件版本錯亂。我習(xí)慣先建一個隔離目錄把工具鏈的搜索路徑固定住# 創(chuàng)建獨立工具鏈目錄避免污染系統(tǒng) /usr mkdir -p /opt/toolchain-6.3/{bin,lib,include} # 解壓源碼包到工作區(qū) tar -xzf centos6.3-src-tools.tar.gz -C /opt/toolchain-6.3/ # 設(shè)置環(huán)境變量優(yōu)先使用包內(nèi)工具 export PATH/opt/toolchain-6.3/bin:$PATH export LD_LIBRARY_PATH/opt/toolchain-6.3/lib:$LD_LIBRARY_PATH export C_INCLUDE_PATH/opt/toolchain-6.3/include:$C_INCLUDE_PATH邏輯說明PATH前置保證調(diào)用的是包內(nèi)gcc而不是/usr/bin/gccLD_LIBRARY_PATH讓鏈接器優(yōu)先找包內(nèi).soC_INCLUDE_PATH解決stdio.h等頭文件被系統(tǒng)舊版本覆蓋的問題。參數(shù)怎么改如果你的業(yè)務(wù)代碼依賴/usr/local/include下的第三方庫把C_INCLUDE_PATH改成/opt/toolchain-6.3/include:/usr/local/include即可順序不能反。2.3 典型編譯流程以 openssl 為例的 configure 參數(shù)模板老系統(tǒng)上編譯 openssl 最容易被zlib-dynamic和no-shared這兩個選項坑到。下面是我在 CentOS 6.3 上驗證過的配置cd /opt/toolchain-6.3/openssl-1.0.2k # 指定安裝路徑和依賴路徑禁用不必要特性減少攻擊面 ./config --prefix/opt/toolchain-6.3 \ --openssldir/opt/toolchain-6.3/ssl \ zlib-dynamic no-idea no-mdc2 no-rc5 \ -fPIC shared # 并行編譯老機器核少就改 -j2 make -j4 # 不要 make install先 make test 驗證 make test邏輯說明zlib-dynamic讓 openssl 運行時動態(tài)加載 zlib避免靜態(tài)鏈接后和系統(tǒng) zlib 沖突no-idea等是禁用專利算法減少編譯時間-fPIC shared生成位置無關(guān)代碼和動態(tài)庫方便其他程序鏈接。make test這一步別跳過CentOS 6.3 的 perl 版本較老測試腳本可能報Cant locate Test/More.pm需要先yum install perl-Test-Simple或者從包內(nèi)perl-libs/手動安裝。3. 避坑與排查老系統(tǒng)上編譯源碼的五個血淚教訓(xùn)3.1 現(xiàn)象gcc: error trying to exec cc1: execvp: No such file or directory原因PATH里混入了不完整的 gcc 安裝或者GCC_EXEC_PREFIX指向了錯誤目錄。CentOS 6.3 的 gcc 依賴cc1在/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/下如果手動替換過 gcc 但沒同步這個目錄就會報錯。解決echo $GCC_EXEC_PREFIX看是否為空不為空就unset然后find / -name cc1 2/dev/null確認(rèn)實際路徑用export GCC_EXEC_PREFIX/usr/libexec/gcc/x86_64-redhat-linux/4.4.7/指回去。3.2 現(xiàn)象鏈接時大量undefined reference to clock_gettime原因CentOS 6.3 的 glibc 2.12 把clock_gettime放在librt里但新版構(gòu)建腳本默認(rèn)不鏈接-lrt。解決在Makefile的LDFLAGS里顯式加-lrt或者export LDFLAGS-lrt $LDFLAGS。如果用的是 autotools./configure LIBS-lrt更穩(wěn)妥。3.3 現(xiàn)象configure: error: C compiler cannot create executables原因十有八九是LD_LIBRARY_PATH指向了包內(nèi) lib 但里面缺crt1.o或crti.o。CentOS 6.3 的啟動文件在/usr/lib64/下包內(nèi)工具鏈如果沒帶全就會這樣。解決檢查/opt/toolchain-6.3/lib下是否有crt*.o沒有就從/usr/lib64/拷貝過去或者把LIBRARY_PATH設(shè)為/usr/lib64:/opt/toolchain-6.3/lib。3.4 現(xiàn)象make報warning: Clock skew detected原因虛擬機掛起后時間不同步或者從宿主機拷貝文件時保留了未來時間戳。老系統(tǒng)上make對時間戳極其敏感。解決find /opt/toolchain-6.3 -exec touch {} \;把所有文件時間戳刷成當(dāng)前然后make clean make。根治方法是裝ntp并chkconfig ntpd on但內(nèi)網(wǎng)機器可能連不上時間源那就每次編譯前手動date -s一下。3.5 現(xiàn)象編譯出的二進(jìn)制在另一臺 CentOS 6.3 上跑不起來報GLIBC_2.14 not found原因編譯時用了包內(nèi)自帶的 glibc 頭文件但鏈接時鏈到了系統(tǒng)/lib64/libc.so.6而系統(tǒng) glibc 是 2.12符號版本對不上。解決ldd your_binary看libc.so.6指向哪里。如果指向/lib64說明鏈接階段沒走包內(nèi)庫。在LDFLAGS里加-Wl,-rpath,/opt/toolchain-6.3/lib -Wl,--dynamic-linker/opt/toolchain-6.3/lib/ld-linux-x86-64.so.2強制指定運行時鏈接器。4. 進(jìn)階技巧用 chroot 隔離編譯環(huán)境與驗證產(chǎn)物4.1 為什么需要 chroot避免“編譯機污染”在 CentOS 6.3 上直接編譯最大的風(fēng)險是make install把系統(tǒng)庫覆蓋了導(dǎo)致ls、cp這種基礎(chǔ)命令都跑不起來。我一般會做一個最小化的 chroot 環(huán)境把工具鏈和源碼都放進(jìn)去編譯完直接打包成 tar拿到目標(biāo)機上解壓即用。# 創(chuàng)建 chroot 根目錄 mkdir -p /opt/chroot-6.3/{bin,lib,lib64,usr,proc,dev} # 拷貝基礎(chǔ)命令和依賴庫 cp /bin/{bash,ls,cp,mv,tar,gzip} /opt/chroot-6.3/bin/ ldd /bin/bash | awk {print $3} | xargs -I{} cp {} /opt/chroot-6.3/lib64/ 2/dev/null # 掛載 proc 和 dev mount -t proc proc /opt/chroot-6.3/proc mount --bind /dev /opt/chroot-6.3/dev # 進(jìn)入 chroot chroot /opt/chroot-6.3 /bin/bash邏輯說明ldd那行自動抓取 bash 依賴的動態(tài)庫避免手動一個個找。mount --bind /dev是為了讓編譯過程中的/dev/null可用。進(jìn)去之后把工具鏈包解壓到/usr/local再按第 2 章的流程編譯產(chǎn)物就完全隔離在 chroot 里了。4.2 驗證產(chǎn)物三個必須檢查的維度編譯完不是結(jié)束我習(xí)慣用下面這張表過一遍確認(rèn)產(chǎn)物能在目標(biāo)環(huán)境跑起來檢查項命令合格標(biāo)準(zhǔn)動態(tài)庫依賴ldd ./your_binary所有依賴都指向/opt/toolchain-6.3/lib或系統(tǒng)基礎(chǔ)庫無not found符號版本objdump -T ./your_binary | grep GLIBC最高版本不超過目標(biāo)機ldd --version輸出的版本運行時鏈接器readelf -l ./your_binary | grep interpreter路徑與目標(biāo)機一致通常是/lib64/ld-linux-x86-64.so.2如果符號版本超了說明編譯時頭文件和鏈接庫不一致回到 3.5 節(jié)加rpath和dynamic-linker。如果運行時鏈接器路徑不對用patchelf --set-interpreter改但 CentOS 6.3 默認(rèn)沒裝patchelf得從包內(nèi)patchelf-0.9/編譯一個。4.3 打包與分發(fā)tar 比 rpm 更省心老系統(tǒng)上打 rpm 容易遇到rpmbuild依賴缺失、%post腳本在目標(biāo)機執(zhí)行失敗等問題。我現(xiàn)在的習(xí)慣是直接打 tar 包附一個setup.sh#!/bin/bash # setup.sh - 在目標(biāo) CentOS 6.3 上解壓即用 INSTALL_DIR/opt/myapp mkdir -p $INSTALL_DIR tar -xzf myapp-bin.tar.gz -C $INSTALL_DIR # 寫入環(huán)境變量不覆蓋已有配置 grep -q INSTALL_DIR/bin /etc/profile || echo export PATH$INSTALL_DIR/bin:\$PATH /etc/profile grep -q INSTALL_DIR/lib /etc/profile || echo export LD_LIBRARY_PATH$INSTALL_DIR/lib:\$LD_LIBRARY_PATH /etc/profile source /etc/profile echo done. run myapp --version to verify.邏輯說明grep -q保證重復(fù)執(zhí)行不會寫重復(fù)行source讓當(dāng)前 shell 立即生效。這個腳本我用了三年從 CentOS 6.3 到 6.10 都沒翻過車。唯一要注意的是目標(biāo)機如果開了 SELinux/opt下的文件可能需要chcon -t bin_t不過 CentOS 6.3 默認(rèn) SELinux 是 permissive問題不大。從那以后我每次拿到老系統(tǒng)的源碼包都強制先走一遍 chroot 編譯和ldd檢查再也不敢直接make install了。希望幫到你。本文還有配套的精品資源點擊獲取