背景:第三方so依赖glibc2.14版本,如何在不升级redhat 6.2自带的gblic2.12情况下,运行so?
结论:通过16进制编辑器修改so的elf符号表来解决这个问题,即强制让so里依赖高版本gblic的函数指向低版本的glibc。风险:有些函数在老版本下可能会出问题,具体需要多测试
首先评估so文件修改风险
通过ldd命令打印库文件所依赖的共享库列表,发现so依赖glibc2.14版本
代码语言:javascript复制[root@bogon lee]# ldd libTaSESDK.so
ldd: warning: you do not have execution permission for `./libTaSESDK.so'
./libTaSESDK.so: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./libTaSESDK.so)
linux-vdso.so.1 => (0x00007fff530a5000)
libstdc .so.6 => /usr/lib64/libstdc .so.6 (0x00007fe729ebf000)
libm.so.6 => /lib64/libm.so.6 (0x00007fe729c3b000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fe729a24000)
libc.so.6 => /lib64/libc.so.6 (0x00007fe729683000)
/lib64/ld-linux-x86-64.so.2 (0x0000003810600000)
而通过rpm命令发现当前系统对应的是glibc2.12版本
代码语言:javascript复制[root@bogon lee]# rpm -qa | grep glibc
glibc-2.12-1.47.el6.x86_64
glibc-devel-2.12-1.47.el6.x86_64
glibc-common-2.12-1.47.el6.x86_64
glibc-headers-2.12-1.47.el6.x86_64
通过objdump -T 查看so 动态符号,发现so使用glibc 2.14 版本的 memcpy
函数
[root@bogon lee]# objdump -T libTaSESDK.so | grep GLIBC_2.1.*
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.14 memcpy
通过objdump -T /lib64/libc.so.6发现当前系统 glibc 库提供的 memcpy
实现是 2.2.5 版本
[root@bogon lib64]# objdump -T /lib64/libc.so.6 | grep memcpy
0000003810e8fa80 w DF .text 0000000000000009 GLIBC_2.2.5 wmemcpy
0000003810efdc70 g DF .text 000000000000001b GLIBC_2.4 __wmemcpy_chk
0000003810e87dd0 g iD .text 000000000000003c GLIBC_2.2.5 memcpy
0000003810efbd50 g iD .text 000000000000003c GLIBC_2.3.4 __memcpy_chk
接下来,要做的就是让so指向2.2.5版本
修改elf
readelf -sV libTaSESDK.so >1.txt
通过readelf
命令查看so的ELF 的符号表
定位到 .gnu.version_r
,其表示二进制程序实际依赖的库文件版本
上图发现,该so比较简单,目前就2处,可想而知,我们只要修改 0x0010: Name: GLIBC_2.14 Flags: none Version: 3这行。
如果行数比较多,可以通过Version版本与 .gnu.version的信息对应,再与
.dynsym对应来找到对应的函数。关系是
.gnu.version通过偏移量
.dynsym里面函数对应的num*2找到对应的版本,然后.gnu.version通过version关联.gnu.version_r。具体可以自行百度,本次so比较简单,所以没有查这个。
.gnu.version_r
表是按照不同的库文件进行分段显示的,每个条目占用 0x10 也就是 16 个字节,上图偏移量为0x0054d0,加上 0x0010=0x0054E0找到如下
现在只要参考 0x0020: Name: GLIBC_2.2.5 Flags: none Version: 2 修改就行,把 0x0010: Name: GLIBC_2.14 Flags: none Version: 3(0x0054E0开始)改为 0x0020: Name: GLIBC_2.2.5 Flags: none Version: 2(0x0054d0 0x0020=0x0054F0)类似就行,相当与把gblic2.14降为2.2.5
.gnu.version_r
表中每个条目是 16 个字节的 Elfxx_Vernaux
结构体,其声明如下(Elfxx_Half
占用 2 个字节,Elfxx_Word
占用 4 个字节):
typedef struct {
Elfxx_Word vna_hash;
Elfxx_Half vna_flags;
Elfxx_Half vna_other;
Elfxx_Word vna_name;
Elfxx_Word vna_next;
} Elfxx_Vernaux;
vna_hash
为 4 个字节的库名称的 hash 值,vna_other
为对应的 .gnu.version
表中符号的版本值,vna_name
指向库名称字符串的偏移量(也可以在 ELF 头中找到),vna_next
为下一个条目的位置(一般固定为 0x00000010
)。
终于修改为如下
只有vna_other和vna_next有差异