linux glibc不兼容問題解決
如需轉載請標明出處:http://blog.csdn.net/itas109
QQ技術交流羣:129518033
文章目錄
相關問題:
1.glibc不兼容
2. /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21’ not found
3. 跨操作系統應用程序
相關閱讀:
Windows/Linux鏈接器加載動態庫的搜索路徑順序
gcc link鏈接常用選項及應用
環境:
OS : deepIn 15.11/Centos 7
編譯器: g++ 6.3.0/g++ 4.8.5
前言
開發環境爲gcc 6.3.0,但是生產環境glibc版本爲4.8.5,這種情況下該怎麼運行程序呢?
本文將以一個例子來介紹如何解決這種不同版本glibc的問題。有如下幾種方式:
- 打包依賴動態庫並修改elf(推薦)
- 靜態編譯
- docker容器
- 升級gcc/g++版本
1.預備知識
1.1 查看glibc版本
- gcc 6.3.0
$ ldd --version
ldd (Debian GLIBC 2.24-11+deb9u3) 2.24
- gcc 4.8.5
$ ldd --version
ldd (GNU libc) 2.17
1.2 查看GLIBCXX版本
- g++ 6.3.0
$ strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX
GLIBCXX_3.4
GLIBCXX_3.4.1
...
GLIBCXX_3.4.22
GLIBCXX_DEBUG_MESSAGE_LENGTH
- g++ 4.8.5
$ strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX
GLIBCXX_3.4
GLIBCXX_3.4.1
...
GLIBCXX_3.4.19
GLIBCXX_DEBUG_MESSAGE_LENGTH
2.示例程序
注意:本程序在gcc 4.8.5的環境無法正常運行
https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions
//regex.cpp
#include<iostream>
#include <regex>
using namespace std;
int main()
{
// gcc 4.8.5 run error
std::regex m_regex("[a-z]+");
std::cout << std::regex_match("abc", m_regex) << std::endl;
return 0;
}
3.解決方案
3.1 普通編譯運行
g++ 6.3.0
$ g++ -o regex regex.cpp
$ ldd regex
linux-vdso.so.1 (0x00007fff087f9000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fdeb0b41000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fdeb083d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fdeb0626000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdeb0287000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdeb1103000)
$ du -h regex
508K regex
$ ./regex
1
使用g++ 6.3.0直接編譯的程序,在g++ 4.8.5的環境上運行會報錯
g++ 4.8.5
$ ldd regex
./regex: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by ./regex)
./regex: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./regex)
linux-vdso.so.1 => (0x00007ffff05d7000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f1d59c9b000)
libm.so.6 => /lib64/libm.so.6 (0x00007f1d59999000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f1d59783000)
libc.so.6 => /lib64/libc.so.6 (0x00007f1d593b6000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1d5a1e2000)
$ ./regex
./regex: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by ./regex)
./regex: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./regex)
3.2 打包依賴動態庫並修改elf(推薦)
3.2.1 打包依賴動態庫
g++ 6.3.0的開發環境上:
編寫拷貝動態庫的shell腳本copylib.sh
#!/bin/bash
# copylib.sh
LibDir=$PWD"/lib"
Target=$1
lib_array=($(ldd $Target | grep -o "/.*" | grep -o "/.*/[^[:space:]]*"))
$(mkdir $LibDir)
for Variable in ${lib_array[@]}
do
cp "$Variable" $LibDir
done
./copylib.sh regex
$ tree
.
├── cplib.sh
├── lib
│ ├── ld-linux-x86-64.so.2
│ ├── libc.so.6
│ ├── libgcc_s.so.1
│ ├── libm.so.6
│ └── libstdc++.so.6
└── regex
3.2.2 修改elf的interpreter和dynamic loader (“ELF interpreter”)
g++ 4.8.5的生產環境上:
源碼編譯patchelf
git clone https://github.com/NixOS/patchelf.git
./bootstrap.sh
./configure
make
sudo make install
查看patchelf版本
$ patchelf --version
patchelf 0.11
修改可執行程序的動態庫搜索路徑rpath和動態庫加載器dynamic loader (“ELF interpreter”)
patchelf --set-rpath `pwd`/lib regex
patchelf --set-interpreter `pwd`/lib/ld-linux-x86-64.so.2 regex
$ ldd regex
linux-vdso.so.1 => (0x00007ffd12ddd000)
libstdc++.so.6 => /home/dev1/regex/pack/lib/libstdc++.so.6 (0x00007f6b30f1b000)
libm.so.6 => /home/dev1/regex/pack/lib/libm.so.6 (0x00007f6b30c17000)
libgcc_s.so.1 => /home/dev1/regex/pack/lib/libgcc_s.so.1 (0x00007f6b30a00000)
libc.so.6 => /home/dev1/regex/pack/lib/libc.so.6 (0x00007f6b30661000)
/home/dev1/regex/pack/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f6b314e0000)
執行程序
$ ./regex
1
特別說明:
- 動態庫加載器dynamic loader(“ELF interpreter”)必須要修改嗎?
答案是肯定的。
動態庫加載器dynamic loader(“ELF interpreter”),程序啓動時,操作系統會把控制權轉交給ld-linux-x86-64.so.2,而不是交給程序正常的進入地址,ld-linux-x86-64.so.2會尋找並加載所有需要的庫文件,然後再將控制權交給應用的起始入口。
經過查看文件頭,可以看出ld-linux-x86-64.so.2的位置信息寫死在ELF中,並不受rpath和LD_LIBRARY_PATH的影響。
$ readelf -l regex
Elf 文件類型爲 DYN (共享目標文件)
入口點 0x44b0
共有 9 個程序頭,開始於偏移量64
程序頭:
Type Offset VirtAddr PhysAddr
...
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
...
這裏可以通過
方式1:(有源碼,指定ld-linux-x86-64.so.2的路徑)
g++ -o regex regex.cpp -Wl,-dynamic-linker='./lib/ld-linux-x86-64.so.2'
方式2:(無源碼,通過patchelf修改elf中ld-linux-x86-64.so.2路徑)
patchelf --set-interpreter `pwd`/lib/ld-linux-x86-64.so.2 regex
- 動態庫搜索路徑rpath必須要修改嗎?
答案是否定的
可以通過LD_LIBRARY_PATH設置搜索路徑
export LD_LIBRARY_PATH=`pwd`/lib:$LD_LIBRARY_PATH
./regex
查看程序elf動態庫信息
$ readelf -d regex
Dynamic section at offset 0x80000 contains 30 entries:
標記 類型 名稱/值
0x000000000000001d (RUNPATH) Library runpath: [/home/dev1/regex/lib]
0x0000000000000001 (NEEDED) 共享庫:[libstdc++.so.6]
0x0000000000000001 (NEEDED) 共享庫:[libm.so.6]
0x0000000000000001 (NEEDED) 共享庫:[libgcc_s.so.1]
0x0000000000000001 (NEEDED) 共享庫:[libc.so.6]
...
3.3 靜態編譯
注意:準確說應該是半靜態編譯,真正靜態編譯選項爲-static
g++ 6.3.0
$ g++ -o regex regex.cpp -static-libgcc -static-libstdc++
$ ldd regex
linux-vdso.so.1 (0x00007ffc627db000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f57f4985000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f57f45e6000)
/lib64/ld-linux-x86-64.so.2 (0x00007f57f4fbd000)
$ du -h regex
1.8M regex
$ ./regex
1
g++ 4.8.5
$ ldd regex
linux-vdso.so.1 => (0x00007ffd67757000)
libm.so.6 => /lib64/libm.so.6 (0x00007f60167ec000)
libc.so.6 => /lib64/libc.so.6 (0x00007f601641f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f6016e22000)
$ ./regex
1
3.4 docker容器
docker容器是一種解決方式,但是由於其需要先安裝docker,所以不太建議使用該方式。
3.5 升級gcc/g++版本
該方式雖然可以解決問題,但是在生產環境中該方式風險極大,所以極不推薦使用該方式。
License
License under CC BY-NC-ND 4.0: 署名-非商業使用-禁止演繹
如需轉載請標明出處:http://blog.csdn.net/itas109
QQ技術交流羣:129518033
Reference:
NULL