符号链接 进程名 linux,由函数符号找不到联想到的动态链接进程

由函数符号找不到联想到的动态链接过程

最近遇到一个因符号找不到导致程序无法启动的情况,原因就是在于字符串表被修改了。下面是我自己创建的一个栗子;因为自己遇到的问题就是函数指针引起的,所以这边还是使用函数指针。

test1.c,后面将会编译成libtest1.so

intfun2()

{return -1;

}int (*pf)();intfun1()

{

pf=fun2;returnpf() ;

}

test3.c,调用libtest1.so中的函数fun1,

intfun1();intmain()

{

fun1();return 1;

}

这里讲两个elf文件中的知识,elf文件一般有两个符号表,分别是.dynsym和.symtab,和两个字符串表,分别是.dynstr和.strtab,做了一下实验如果是连接动态库时,修改后者是不会对连接以及运行时造成影响的,而如果是静态链接,那么两者必须全部都一致。但是反汇编时使用的是后者。

本来test3文件是可以运行的,现在我把dynstr中的fun2改成fun6,此时会出现下面的提示,实际上该符号根本不需要重定向。这个可以通过将fun2定义为static时对比得知,如果fun2是static的,那么dynsym表中并不会出现fun2,或者是可执行文件也不会导出fun2。所以这边可以确定.dynsym中的是导出符号或者需要重定位符号。

./test: symbol lookup error: libtest1.so: undefined symbol: fun6

首先修改的是字符串表,间接影响到了符号表,但导出符号中关于fun6函数的信息还是

2: 00000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.0 (3)3: 00000000 0NOTYPE WEAK DEFAULT UND __gmon_start__4: 00000000 0NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses5: 00002010 0NOTYPE GLOBAL DEFAULT ABS _edata6: 00002018 4 OBJECT GLOBAL DEFAULT 23pf7: 000004a6 62 FUNC GLOBAL DEFAULT 11fun18: 0000201c 0NOTYPE GLOBAL DEFAULT ABS _end9: 0000049c 10 FUNC GLOBAL DEFAULT 11 fun6

未修改前:

9: 0000049c    10 FUNC    GLOBAL DEFAULT   11 fun2

接着实验,我把fun1改成fun8此时还是提示fun6找不到

5: 00002010 0NOTYPE GLOBAL DEFAULT ABS _edata6: 00002018 4 OBJECT GLOBAL DEFAULT 23pf7: 000004a6 62 FUNC GLOBAL DEFAULT 11fun88: 0000201c 0NOTYPE GLOBAL DEFAULT ABS _end9: 0000049c 10 FUNC GLOBAL DEFAULT 11fun610: 00002010 0NOTYPE GLOBAL DEFAULT ABS __bss_start11: 00000368 0 FUNC GLOBAL DEFAULT 9_init12: 00000528 0 FUNC GLOBAL DEFAULT 12 _fini

另一个实验,如果把fun2改成fun1,fun1还是fun1,那么会导致程序一直在调用fun1,知道栈溢出。

所以现在可以确定几件事情,对应源码来看,pf=fun2这句话相当于变成了pf=fun1,然后变成了解析fun1的函数位置。查看源码可以知道,pf是指向某个字符串表的字符串,通过该字符串来解析的,

当然这个我们就会想到使用的是同一个字符串表,fun6还是fun6啊,还是可以找到啊,但是实际上如果一个程序的符号很少,当然可以通过这种逐个对比的方式,在我前一篇的博文中热补丁就是使用的这种方式,而实际上,解释器程序会把字符串通过哈希函数映射到字符串表中的某个条目,而我们编译时使用的键值是fun2,而现在使用的是fun6,所以会导致找不到的现象出现。所以当我们把字符串改为fun6时,查找fun6,当然是找不到的。所以确定原因在于pf这个全局变量是需要重定位的。而将fun1改为fun8时还是提示fun6找不到,原因在于解析重定向表时的顺序。

另外一种情况:

#include

intfun2()

{return -1;

}int (*pf)();intfun1()

{

printf("fun1\n");//pf = fun2;

returnfun2() ;

}

所有的现象还是不变。查看反汇编代码可以看到,本来调用fun2的位置(也就是现在调用fun1的位置),

000004a6 :

4a6:55 push %ebp

4a7:89 e5 mov %esp,%ebp

4a9:53 push %ebx

4aa:83 ec 14 sub $0x14,%esp

4ad: e8 e5 ff ff ff call497 <__i686.get_pc_thunk.bx>4b2:81 c3 42 1b 00 00 add $0x1b42,%ebx

4b8: 8d83 3e e5 ff ff lea -0x1ac2(%ebx),%eax

4be:89 04 24 mov %eax,(%esp)

4c1: e8 fa fe ff ff call 3c04c6: e8 d5 fe ff ff call 3a04cb:83 c4 14 add $0x14,%esp

4ce: 5b pop%ebx

是通过plt表来解析的,这个解析效果和函数指针是一样的,因为是通过符号名称来重定位的。

曾经一度怀疑自己以前的想法是不是错了,以前我认为解释器只是解析重定向表中的符号,乍一看还以为解释器解析整个符号表。当然现在看来我还是正确的,解释器是只解析重定向表中的符号,只是使用了效率更高的hash表,而不是遍历。

总结一下,解释器解释函数符号时,通过函数符号经过hash函数运算得到hash值,然后映射到符号表中的一个条目,再通过符号表重定位该符号,不同符号会导致不同的hash值,所以会导致找不到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值