程序员的自我修养-动态链接

程序员的自我修养-动态链接

例子演示

一个动态链接加载的例子如下
假设有p1.c 和 p2.C 都引用了 foo.c程序
foo.h内容如下

#ifndef LIB_H
#define LIB_H
 
void foobar(int i);
 
#endif

foo.c内容如下

#include <stdio.h>
 
void foo(int i) {
    printf("printf from lib.so %d\n", i);
    sleep(-1);

p1.c 内容如下

#include "foo.h"
int main() {
    foo(1);
    return 0;
}

p2.c 内容如下

#include "foo.h"
int main() {
    foo(2);
    return 0;
}

编译命令如下

//将foo.c编译成动态链接库
gcc -fPIC -shared -o libfoo.so foo.c
 
//编译p1和p2
gcc -o p1 p1.c ./libfoo.so
gcc -o p2 p2.c ./libfoo.so

运行p1

./p1
printf from lib.so 1

修改foo.c程序

#include <stdio.h>
void foo(int i) {
    printf("printf from lib.so %d\n", i);
    printf("hehe --> %d\n", i);
    sleep(-1);
}

再编译一遍foo.c 生成 libfoo.so文件,不用再编译p1和p2,直接p1结果为

printf from lib.so 1
hehe --> 1

执行p1 , cat /proc/[PID]/maps 结果如下


00400000-00401000 r-xp 00000000 fd:01 1063464                            /root/c/book_link/7/p1
00600000-00601000 r--p 00000000 fd:01 1063464                            /root/c/book_link/7/p1
00601000-00602000 rw-p 00001000 fd:01 1063464                            /root/c/book_link/7/p1
7f5422887000-7f5422a3f000 r-xp 00000000 fd:01 1049812                    /usr/lib64/libc-2.17.so
7f5422a3f000-7f5422c3f000 ---p 001b8000 fd:01 1049812                    /usr/lib64/libc-2.17.so
7f5422c3f000-7f5422c43000 r--p 001b8000 fd:01 1049812                    /usr/lib64/libc-2.17.so
7f5422c43000-7f5422c45000 rw-p 001bc000 fd:01 1049812                    /usr/lib64/libc-2.17.so
7f5422c45000-7f5422c4a000 rw-p 00000000 00:00 0 
7f5422c4a000-7f5422c4b000 r-xp 00000000 fd:01 1063461                    /root/c/book_link/7/libfoo.so
7f5422c4b000-7f5422e4a000 ---p 00001000 fd:01 1063461                    /root/c/book_link/7/libfoo.so
7f5422e4a000-7f5422e4b000 r--p 00000000 fd:01 1063461                    /root/c/book_link/7/libfoo.so
7f5422e4b000-7f5422e4c000 rw-p 00001000 fd:01 1063461                    /root/c/book_link/7/libfoo.so
7f5422e4c000-7f5422e6d000 r-xp 00000000 fd:01 1049801                    /usr/lib64/ld-2.17.so
7f5423061000-7f5423064000 rw-p 00000000 00:00 0 
7f542306b000-7f542306d000 rw-p 00000000 00:00 0 
7f542306d000-7f542306e000 r--p 00021000 fd:01 1049801                    /usr/lib64/ld-2.17.so
7f542306e000-7f542306f000 rw-p 00022000 fd:01 1049801                    /usr/lib64/ld-2.17.so
7f542306f000-7f5423070000 rw-p 00000000 00:00 0 
7fff36862000-7fff36883000 rw-p 00000000 00:00 0                          [stack]
7fff369a2000-7fff369a4000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
                                                                                                             

这里出现了p1程序, libc-2.17.so
还有 ld-2.17.so,这个是c语言下的动态连接器,负责将动态链接库加载到内存中
通过readelf -l 查看动态链接库,其内容跟普通的可执行文件,或者重定位文件是差不多的

动态链接

动态链接库文件在运行时会被 动态链接到内存中,这里就牵扯到地址的重定位问题
假设动态链接文件 foo.so是 0x1234开头的,而内存中0x1234已经被占用了,0x8234是空闲的,于是就将foo.so重定位0x8234这个位置,而foo.so中程序的函数的相对调用地址是不变的,所以将0x1234+0x7000 变成0x8234就可以了
用下面这个命令 不带 -fPIC 就是装载时重定位方式

gcc -shared -o libfoo.so foo.c 


/usr/bin/ld: /tmp/ccd9Axtv.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/tmp/ccd9Axtv.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
                                                                                                                                             

gcc默认生成的是不支持装载时重定位方式
使用的是地址无关的方式,把需要重定位的部分单独拿出来,跟数据部分放到一起,所以上面执行的那条命令就报错了
地址无关的重定位方式有 4 种方式
1.模块内部的函数调用,跳转等
2.模块内部的数据访问,比如定义的全局变量,静态变量
3.模块外部的函数调用,跳转等

4.模块外部的数据访问,比如其他的模块中定义的全局变量

一段示列代码如下
编译方式

gcc -fPIC -shared -o pic.so pic.c

代码如下

static int a;
extern int b;
extern void ext();
 
void bar() {
    a = 1;
    b = 2;
}
 
void foo() {
    bar();
    ext();
}

objdump -d pic.so反编译结果如下

Disassembly of section .init:
 
00000000000005e0 <_init>:
 5e0:   48 83 ec 08             sub    $0x8,%rsp
 5e4:   48 8b 05 f5 09 20 00    mov    0x2009f5(%rip),%rax        # 200fe0 <_DYNAMIC+0x1d0>
 5eb:   48 85 c0                test   %rax,%rax
 5ee:   74 05                   je     5f5 <_init+0x15>
 5f0:   e8 2b 00 00 00          callq  620 <__gmon_start__@plt>
 5f5:   48 83 c4 08             add    $0x8,%rsp
 5f9:   c3                      retq   
 
Disassembly of section .plt:
 
0000000000000600 <bar@plt-0x10>:
 600:   ff 35 02 0a 20 00       pushq  0x200a02(%rip)        # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
 606:   ff 25 04 0a 20 00       jmpq   *0x200a04(%rip)        # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
 60c:   0f 1f 40 00             nopl   0x0(%rax)
 
0000000000000610 <bar@plt>:
 610:   ff 25 02 0a 20 00       jmpq   *0x200a02(%rip)        # 201018 <_GLOBAL_OFFSET_TABLE_+0x18>
 616:   68 00 00 00 00          pushq  $0x0
 61b:   e9 e0 ff ff ff          jmpq   600 <_init+0x20>
 
0000000000000620 <__gmon_start__@plt>:
 620:   ff 25 fa 09 20 00       jmpq   *0x2009fa(%rip)        # 201020 <_GLOBAL_OFFSET_TABLE_+0x20>
 626:   68 01 00 00 00          pushq  $0x1
 62b:   e9 d0 ff ff ff          jmpq   600 <_init+0x20>
 
0000000000000630 <ext@plt>:
 630:   ff 25 f2 09 20 00       jmpq   *0x2009f2(%rip)        # 201028 <_GLOBAL_OFFSET_TABLE_+0x28>
 636:   68 02 00 00 00          pushq  $0x2
 63b:   e9 c0 ff ff ff          jmpq   600 <_init+0x20>
 
0000000000000640 <__cxa_finalize@plt>:
 640:   ff 25 ea 09 20 00       jmpq   *0x2009ea(%rip)        # 201030 <_GLOBAL_OFFSET_TABLE_+0x30>
 646:   68 03 00 00 00          pushq  $0x3
 64b:   e9 b0 ff ff ff          jmpq   600 <_init+0x20>
 
 
Disassembly of section .text:
0000000000000735 <bar>:
 735:   55                      push   %rbp
 736:   48 89 e5                mov    %rsp,%rbp
 739:   c7 05 f9 08 20 00 01    movl   $0x1,0x2008f9(%rip)        # 20103c <a>
 740:   00 00 00 
 743:   48 8b 05 8e 08 20 00    mov    0x20088e(%rip),%rax        # 200fd8 <_DYNAMIC+0x1c8>
 74a:   c7 00 02 00 00 00       movl   $0x2,(%rax)
 750:   5d                      pop    %rbp
 751:   c3                      retq   
 
0000000000000752 <foo>:
 752:   55                      push   %rbp
 753:   48 89 e5                mov    %rsp,%rbp
 756:   b8 00 00 00 00          mov    $0x0,%eax
 75b:   e8 b0 fe ff ff          callq  610 <bar@plt>
 760:   b8 00 00 00 00          mov    $0x0,%eax
 765:   e8 c6 fe ff ff          callq  630 <ext@plt>
 76a:   5d                      pop    %rbp
 76b:   c3                      retq
                                                                                                              

调用bar(),ext()都是先 call一个中间函数
这个中间函数是跳转到 _GLOBAL_OFFSET_TABLE_ + 一个偏移量
通过这个全局偏移量表最终选择到内部函数bar,或者外部函数ext
bar()函数的内部通过相当地址找到变量a,通过_DYNAMIC + 偏移量的方式找到变量b
通过objdunp -h pic.so,查看各段的信息如下


Sections:
Idx Name          Size      VMA               LMA               File off  Algn
 。。。。。
 19 .got          00000030  0000000000200fd0  0000000000200fd0  00000fd0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
。。。。。
                                                                                     

通过objdump -R pic.so 查看重定位表,重定位表就是 0x200fd0开头的,b的位置是0x200fd8

pic.so:     file format elf64-x86-64
 
DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE 
0000000000200df0 R_X86_64_RELATIVE  *ABS*+0x0000000000000700
0000000000200df8 R_X86_64_RELATIVE  *ABS*+0x00000000000006c0
0000000000200e08 R_X86_64_RELATIVE  *ABS*+0x0000000000200e08
0000000000200fd0 R_X86_64_GLOB_DAT  _ITM_deregisterTMCloneTable
0000000000200fd8 R_X86_64_GLOB_DAT  b
0000000000200fe0 R_X86_64_GLOB_DAT  __gmon_start__
0000000000200fe8 R_X86_64_GLOB_DAT  _Jv_RegisterClasses
0000000000200ff0 R_X86_64_GLOB_DAT  _ITM_registerTMCloneTable
0000000000200ff8 R_X86_64_GLOB_DAT  __cxa_finalize
0000000000201018 R_X86_64_JUMP_SLOT  bar
0000000000201020 R_X86_64_JUMP_SLOT  __gmon_start__
0000000000201028 R_X86_64_JUMP_SLOT  ext
0000000000201030 R_X86_64_JUMP_SLOT  __cxa_finalize
                                                                 

各种地址引用方式总结

 指定跳转调用数据访问
模块内部相对跳转和调用相对地址访问
模块外部间接跳转和调用 GOT间接访问 GOT

进程A和进程B都调用了共享库libfoo.so
进程A改变了libfoo.so中变量X的值,对于进程B来说没影响,因为两个进程用的是不同的副本
对于进程A内部的两个线程a1和a2来说就有影响,因为进程内部的线程共享一个副本
也可以将A进程和B进程共享一个数据,这个叫共享数据段,用来做进程间通讯
而A进程内部的线程a1和a2都可以有自己的副本,这是线程私有存储

 

延迟加载

动态链接比静态链接慢
1.是多了一步函数调用,main->got全局选择函数->bar函数
2.静态链接是已经整合好成一个文件了,动态链接是在运行启时候把相关函数加载进来
延迟加载就是在使用的时候才进行链接,避免不必要的链接,一般动态链接比静态慢5%左右
ELF使用PLT procedure linkage table方法来实现
程序的调用逻辑是 foo() -> bar(),foo的反汇编如下

   0x0000000000000752 <+0>:     push   %rbp
   0x0000000000000753 <+1>:     mov    %rsp,%rbp
   0x0000000000000756 <+4>:     mov    $0x0,%eax
   0x000000000000075b <+9>:     callq  0x610 <bar@plt>
   0x0000000000000760 <+14>:    mov    $0x0,%eax
   0x0000000000000765 <+19>:    callq  0x630 <ext@plt>
   0x000000000000076a <+24>:    pop    %rbp
   0x000000000000076b <+25>:    retq 
                                                          

foo函数是先调用bar@plt函数的,调用关系图如下

1.jmpq *0x200a02(%rip) 先调用重定位表中bar函数
2.重定位表中的bar函数位置是空的,最终会调用回来,也就是bar@plt的第二个指定
3.jmpq 600,调用一个全局函数,这个函数会确定bar的地址
pushq $0x0可能是类似传递参数的意思,got_func会重新重定位表中bar函数的地址
4.bar@plt再次调用bar函数的时候去重定位表中bar就有正确地址了,于是可以直接调用了

pic.so的重定位表内容如下:

DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE 
0000000000200df0 R_X86_64_RELATIVE  *ABS*+0x0000000000000700
0000000000200df8 R_X86_64_RELATIVE  *ABS*+0x00000000000006c0
0000000000200e08 R_X86_64_RELATIVE  *ABS*+0x0000000000200e08
0000000000200fd0 R_X86_64_GLOB_DAT  _ITM_deregisterTMCloneTable
0000000000200fd8 R_X86_64_GLOB_DAT  b
0000000000200fe0 R_X86_64_GLOB_DAT  __gmon_start__
0000000000200fe8 R_X86_64_GLOB_DAT  _Jv_RegisterClasses
0000000000200ff0 R_X86_64_GLOB_DAT  _ITM_registerTMCloneTable
0000000000200ff8 R_X86_64_GLOB_DAT  __cxa_finalize
0000000000201018 R_X86_64_JUMP_SLOT  bar
0000000000201020 R_X86_64_JUMP_SLOT  __gmon_start__
0000000000201028 R_X86_64_JUMP_SLOT  ext
0000000000201030 R_X86_64_JUMP_SLOT  __cxa_finalize
                                                                   

真实的ELF文件中有 got section和 got.plt section,前者是保存全局变量的引用,后者保存全局函数的引用

 

动态连接的程序在启动时,操作系统会先启动一个动态连接器
ELF文件中包含的 .interp 段中有ld.so的信息
通过objdump -s a.out 反编译的结果


Contents of section .interp:
 400238 2f6c6962 36342f6c 642d6c69 6e75782d  /lib64/ld-linux-
 400248 7838362d 36342e73 6f2e3200           x86-64.so.2. 
                                                                

动态链接格式

动态链接文件中有一个 .dynamic的段,这个段中包含了依赖哪些共享对象,动态链接符号表位置,动态链接重定位表的位置,共享对象初始代码位置,代码开始位置,代码结束位置,有点类似ELF的头文件信息
通过命令 readelf -d pic.so查看


Dynamic section at offset 0xe10 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000c (INIT)               0x5e0
 0x000000000000000d (FINI)               0x76c
 0x0000000000000019 (INIT_ARRAY)         0x200df0
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x200df8
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x1f0
 0x0000000000000005 (STRTAB)             0x3b0
 0x0000000000000006 (SYMTAB)             0x230
 0x000000000000000a (STRSZ)              177 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x201000
 0x0000000000000002 (PLTRELSZ)           96 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x580
 0x0000000000000007 (RELA)               0x4a8
 0x0000000000000008 (RELASZ)             216 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x488
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x462
 0x000000006ffffff9 (RELACOUNT)          3
 0x0000000000000000 (NULL)               0x0
                                                                             

通过 ldd pic.so查看 这个共享库依赖哪些其他库,linux-vdso.so是跟系统内核有关的


        linux-vdso.so.1 =&amp;amp;gt;  (0x00007ffdb249b000)
        libc.so.6 =&amp;amp;gt; /lib64/libc.so.6 (0x00007f8bc7e5c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8bc842a000)

通过 readelf -r libfoo.so 查看重定位表

Relocation section '.rela.dyn' at offset 0x490 contains 8 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000200df8  000000000008 R_X86_64_RELATIVE                    6d0
000000200e00  000000000008 R_X86_64_RELATIVE                    690
000000200e10  000000000008 R_X86_64_RELATIVE                    200e10
000000200fd8  000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTMClone + 0
000000200fe0  000400000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000200fe8  000500000006 R_X86_64_GLOB_DAT 0000000000000000 _Jv_RegisterClasses + 0
000000200ff0  000600000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCloneTa + 0
000000200ff8  000800000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize + 0
 
Relocation section '.rela.plt' at offset 0x550 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000201018  000300000007 R_X86_64_JUMP_SLO 0000000000000000 printf + 0
000000201020  000400000007 R_X86_64_JUMP_SLO 0000000000000000 __gmon_start__ + 0
000000201028  000700000007 R_X86_64_JUMP_SLO 0000000000000000 sleep + 0
000000201030  000800000007 R_X86_64_JUMP_SLO 0000000000000000 __cxa_finalize + 0
                                                                                                  

libfoo.so中包含了plt相关的函数表的段


  [21] .got              PROGBITS         0000000000200fd8  00000fd8
       0000000000000028  0000000000000008  WA       0     0     8
  [22] .got.plt          PROGBITS         0000000000201000  00001000
       0000000000000038  0000000000000008  WA       0     0     8
                                                                        

got.plt的前三项是被系统占据的,第四项是_gmon_start_,第五项是printf,第六项是sleep,当链接器确定了printf的函数地址时,就会重写got.plt表中的printf地址,也就是0x000015d8这一栏

 

动态链接启动是依靠 动态链接器,这个链接器会自己初始化自己
由于使用地址无关的方式编译共享对象,模块内部的函数调用都是采用plt方式,所以动态连接器内部不能调用全局变量,也不能调用其他函数
动态连接器完成重定位之后会初始化共享对象的 .init段,当程序退出时执行 .finit段

一个例子,演示引用多个共享对象,共享对象之间函数名有冲突的情况

//cat a1.c
#include <stdio.h>
void a() {
    printf("a1.c \n");
}
 
 
//cat a2.c 
#include <stdio.h>
void a() {
    printf("a2.c\n ");
}
 
 
//cat b1.c 
void a();
 
void b1() {
    a();
}
 
 
//cat b2.c 
void a();
 
void b2() {
    a();
}
 
 
//cat main.c
#include <stdio.h>
 
void b1();
void b2();
 
int main() {
    b1();
    b2();
    sleep(-1);
    return 0;
}

编译这些文件

gcc -fPIC -shared a1.c -o a1.so
gcc -fPIC -shared a2.c -o a2.so 
gcc -fPIC -shared b1.c a1.so -o b1.so
gcc -fPIC -shared b2.c a2.so -o b2.so 
gcc main.c b1.so b2.so -o main -Xlinker
 
ldd b1.so 
        linux-vdso.so.1 =>  (0x00007ffcaede8000)
        a1.so => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f9f23a58000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f9f24027000)
 
 ldd b2.so 
        linux-vdso.so.1 =>  (0x00007fffd7fcc000)
        a2.so => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f6f7bbc1000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f6f7c190000)
                                                                    

运行main程序之后, cat /proc/[PID]/maps 结果

00400000-00401000 r-xp 00000000 fd:01 1063486                            /root/c/book_link/7/conflect/main
00600000-00601000 r--p 00000000 fd:01 1063486                            /root/c/book_link/7/conflect/main
00601000-00602000 rw-p 00001000 fd:01 1063486                            /root/c/book_link/7/conflect/main
7f74b25b2000-7f74b25b3000 r-xp 00000000 fd:01 1063483                    /root/c/book_link/7/conflect/a2.so
7f74b25b3000-7f74b27b2000 ---p 00001000 fd:01 1063483                    /root/c/book_link/7/conflect/a2.so
7f74b27b2000-7f74b27b3000 r--p 00000000 fd:01 1063483                    /root/c/book_link/7/conflect/a2.so
7f74b27b3000-7f74b27b4000 rw-p 00001000 fd:01 1063483                    /root/c/book_link/7/conflect/a2.so
7f74b27b4000-7f74b27b5000 r-xp 00000000 fd:01 1063478                    /root/c/book_link/7/conflect/a1.so
7f74b27b5000-7f74b29b4000 ---p 00001000 fd:01 1063478                    /root/c/book_link/7/conflect/a1.so
7f74b29b4000-7f74b29b5000 r--p 00000000 fd:01 1063478                    /root/c/book_link/7/conflect/a1.so
7f74b29b5000-7f74b29b6000 rw-p 00001000 fd:01 1063478                    /root/c/book_link/7/conflect/a1.so
7f74b29b6000-7f74b2b6e000 r-xp 00000000 fd:01 1049812                    /usr/lib64/libc-2.17.so
7f74b2b6e000-7f74b2d6e000 ---p 001b8000 fd:01 1049812                    /usr/lib64/libc-2.17.so
7f74b2d6e000-7f74b2d72000 r--p 001b8000 fd:01 1049812                    /usr/lib64/libc-2.17.so
7f74b2d72000-7f74b2d74000 rw-p 001bc000 fd:01 1049812                    /usr/lib64/libc-2.17.so
7f74b2d74000-7f74b2d79000 rw-p 00000000 00:00 0 
7f74b2d79000-7f74b2d7a000 r-xp 00000000 fd:01 1063485                    /root/c/book_link/7/conflect/b2.so
7f74b2d7a000-7f74b2f79000 ---p 00001000 fd:01 1063485                    /root/c/book_link/7/conflect/b2.so
7f74b2f79000-7f74b2f7a000 r--p 00000000 fd:01 1063485                    /root/c/book_link/7/conflect/b2.so
7f74b2f7a000-7f74b2f7b000 rw-p 00001000 fd:01 1063485                    /root/c/book_link/7/conflect/b2.so
7f74b2f7b000-7f74b2f7c000 r-xp 00000000 fd:01 1063484                    /root/c/book_link/7/conflect/b1.so
7f74b2f7c000-7f74b317b000 ---p 00001000 fd:01 1063484                    /root/c/book_link/7/conflect/b1.so
7f74b317b000-7f74b317c000 r--p 00000000 fd:01 1063484                    /root/c/book_link/7/conflect/b1.so
7f74b317c000-7f74b317d000 rw-p 00001000 fd:01 1063484                    /root/c/book_link/7/conflect/b1.so
7f74b317d000-7f74b319e000 r-xp 00000000 fd:01 1049801                    /usr/lib64/ld-2.17.so
7f74b3391000-7f74b3394000 rw-p 00000000 00:00 0 
7f74b339b000-7f74b339e000 rw-p 00000000 00:00 0 
7f74b339e000-7f74b339f000 r--p 00021000 fd:01 1049801                    /usr/lib64/ld-2.17.so
7f74b339f000-7f74b33a0000 rw-p 00022000 fd:01 1049801                    /usr/lib64/ld-2.17.so
7f74b33a0000-7f74b33a1000 rw-p 00000000 00:00 0 
7ffdae24c000-7ffdae26d000 rw-p 00000000 00:00 0                          [stack]
7ffdae2c1000-7ffdae2c3000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
                                                                                                                   

这里能看到,链接器将 a1.so,a2.so, b1.so, b2.so都加载进来了
当符号名冲突的时候,链接器则规定先加载进来的有效,于是就打印出两次  a1.c
像 LD_PRELOAD 这样的方式引入自定义的动态库时,就可以覆盖掉系统的函数
/lib64/ld-2.17.so  这个文件就是链接器,而这个程序本身也是一个可执行程序

 

动态链接程序

dlopen,dlsym, dlclose,dlerror 函数例子

#include <stdio.h>
#include <dlfcn.h>
typedef void (*func)(int);
 
int main(int argc, char **argv) {    
        void *handle;
        char *error;
 
        handle = dlopen("libfoo.so", RTLD_LAZY);
        if (!handle) {
            fprintf (stderr, "%s ", dlerror());
            exit(1);
        }
 
        func func_ = dlsym(handle, "foo");
        if ((error = dlerror()) != NULL)  {
            fprintf (stderr, "%s ", error);
            exit(1);
        }
 
        func_(10);
        dlclose(handle);
        return 0;
}
                                                         

编译时需要加上 -ldl,否则会出错
gcc -o dd dlopen.c -ldl
另外设置LD_LIBRARY_PATH,把libfoo.so的所在的路径加上,否则会运行时会报找不到动态库的错误

 

 

0 次阅读

发表评论

电子邮件地址不会被公开。 必填项已用*标注