库的制作与原理

1.什么是库

C/C++的标准库

使用ldd命令可以查看我们的可执行程序都使用了哪些库。

库名字必须以lib为前缀,实际库的名字就是去掉去前缀lib,在去掉后缀

C/C++代码使用gcc或g++编译链接的时候,默认使用动态链接。

想要静态链接,就需要 -static 选项

如果编译失败,说明当前系统中没有安装静态库。

bash 复制代码
# 更新软件源(可选,但建议执行)
sudo apt update
# 安装编译工具链(gcc/g++、make等)和基础静态库
sudo apt install build-essential

库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。

本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:

静态库.a[Linux]、.lib[windows]

动态库.so[Linux]、.dll[windows]

编译链接过程:

在进行多文件编译时,推荐先把.c文件编译成.o文件,再把.o文件链接形成可执行文件。

库文件就是".o"文件的集合。

2.静态库

2.1静态库的生成

静态库生成命令:ar -rc [静态库名] [静态库包含的.o文件]

库文件就是把多个.o文件进行打包。

2.2静态库的使用

  • 使用静态库时,必须要有头文件,头文件提供声明,库文件提供所有方法的实现。
  • 操作系统在查找库时默认到lib64目录下寻找。
  • 在使用静态库形成可执行程序时,链接所有.o文件,要将主函数.c文件先编译形成.o文件再链接静态库。
  • 库文件中不能包含main函数
bash 复制代码
libmy.a:mystdio.o mystr.o
	ar rcs $@ $^
%.o:%.c
	gcc -c $<
.PHONY:clean
clean:
	rm -f *.o *.a

2.2.1方法一:使用当前目录的库

gcc编译时选项:

  • -l:表示当前.o文件链接形成可执行程序所需要的静态库的**[文件名]**,该文件要去掉前缀lib和后缀.a。
  • -L: 静态库的路径
  • 我们在使用c标准库进程编译时,gcc认识c标准库,也知道c标准库在哪个路径下。
  • 所以只用到c标准库时,不需要带任何选项。
  • 当我们以一个库的制作者去发布一个库时,提供给别人的是一个这样的目录:

2.2.2方法二:将下载的库/自己做的库拷贝到系统目录下

  • 虽然我们把下载的库安装到系统路径下,但是在链接时还是需要告诉gcc要链接哪个库。

2.2.3方法三:当库和头文件不在当前路径下时,指定gcc的寻找路径

  • -I 告诉系统除了当前和系统目录,-I指定的路径下也需要搜索头文件
  • I(大写i)用来寻找头文件

2.2.4方法四:在系统目录下建立自己的库的软链接

3.动态库

3.1动态库的打包

bash 复制代码
libmy.so:mystdio.o mystr.o
	gcc -shared -o $@ $^
%.o:%.c
	gcc -fPIC -c $<
.PHONY:clean
clean:
	rm -f *.o *.so mylib
。PHONY:output
output:
	mkdir -p mylib
	mkdir -p mylib/include
	mkdir -p mylib/lib
	cp *.h mylib/include
	cp *.so mylib/lib
  • 细节:形成动态库,不再使用ar,而是使用gcc/g++,说明:动态库是最常见的场景,一款合格的编译器本身,就能形成动态库。
  • 静态库则是使用ar命令,说明静态库是shall帮我们完成的。

3.2动态库的使用

  • 从上图我们可以看到,main可执行程序找不到我们的动态库
  • 我们使用gcc带上库和头文件的路径形成的main,相当于我们告诉了gcc编译器我们要用到的库在哪
  • 当我们要运行时,是操作系统来执行这个程序,操作系统并不知道我们的动态库在哪。
    • c标准库是在系统默认路径下的,所以操作系统可以找到
  • 使用静态库,静态库的方法拷贝到程序内部,程序运行就不需要库了。

动态库加载(运行)时的查找问题

1.直接把库拷贝到系统的lib64下
  • 动态库要在编译时能被编译器找到
  • 运行时要能被操作系统找到
2.在系统目录下建立软链接
3.通过更改环境变量

环境变量LD_LIBRARY_PATH能帮助操作系统找到动态库。

bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ echo $LD_LIBRARY_PATH

wx@VM-8-14-ubuntu:~/linux-learning/friend$ ldd main
        linux-vdso.so.1 (0x00007ffcc0194000)
        libmy.so => not found
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000073d7eba00000)
        /lib64/ld-linux-x86-64.so.2 (0x000073d7ebd0e000)
wx@VM-8-14-ubuntu:~/linux-learning/friend$ pwd
/home/ubuntu/linux-learning/friend
wx@VM-8-14-ubuntu:~/linux-learning/friend$ tree ./
./
├── main
├── main.c
└── mylib
    ├── include
    │   ├── mystdio.h
    │   └── mystr.h
    └── lib
        └── libmy.so

4 directories, 5 files
wx@VM-8-14-ubuntu:~/linux-learning/friend$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ubuntu/linux-learning/friend/mylib/lib
wx@VM-8-14-ubuntu:~/linux-learning/friend$ ldd main
        linux-vdso.so.1 (0x00007fffbf38b000)
        libmy.so => /home/ubuntu/linux-learning/friend/mylib/lib/libmy.so (0x000072d7c7e6d000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000072d7c7c00000)
        /lib64/ld-linux-x86-64.so.2 (0x000072d7c7e79000)
4.更改配置文件

配置文件位置:/etc/ld.so.conf.d/

将动态库的绝对路径写入配置文件中,更改配置文件需要root用户

bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ ls /etc/ld.so.conf.d/
libc.conf  x86_64-linux-gnu.conf
wx@VM-8-14-ubuntu:~/linux-learning/friend$ pwd
/home/ubuntu/linux-learning/friend
wx@VM-8-14-ubuntu:~/linux-learning/friend$ tree ./
./
├── main
├── main.c
└── mylib
    ├── include
    │   ├── mystdio.h
    │   └── mystr.h
    └── lib
        └── libmy.so

4 directories, 5 files
wx@VM-8-14-ubuntu:~/linux-learning/friend$ sudo touch /etc/ld.so.conf.d/l13.conf
wx@VM-8-14-ubuntu:~/linux-learning/friend$ ls -l /etc/ld.so.conf.d/
total 8
-rw-r--r-- 1 root root   0 Jan  9 14:15 l13.conf
-rw-r--r-- 1 root root  44 Aug  2  2022 libc.conf
-rw-r--r-- 1 root root 100 Mar 30  2024 x86_64-linux-gnu.conf

root用户:
root@VM-8-14-ubuntu:~# echo "/home/ubuntu/linux-learning/friend/mylib/lib" > /etc/ld.so.conf.d/l13.conf
root@VM-8-14-ubuntu:/etc/ld.so.conf.d# ldconfig
root@VM-8-14-ubuntu:/home/ubuntu/linux-learning/friend# ldd main
        linux-vdso.so.1 (0x00007ffd5f3fa000)
        libmy.so => /home/ubuntu/linux-learning/friend/mylib/lib/libmy.so (0x00007b85d54db000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007b85d5200000)
        /lib64/ld-linux-x86-64.so.2 (0x00007b85d54ee000)
__vsc_prompt_cmd_original: command not found
当动静态库同时存在时,编译器如何选择?
bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ tree .
.
├── main
├── main.c
└── mylib
    ├── include
    │   ├── mystdio.h
    │   └── mystr.h
    └── lib
        ├── libmy.a
        └── libmy.so

4 directories, 6 files
wx@VM-8-14-ubuntu:~/linux-learning/friend$ gcc main.c -o main -I ./mylib/include/ -L ./mylib/lib/ -l my
wx@VM-8-14-ubuntu:~/linux-learning/friend$ ldd main
        linux-vdso.so.1 (0x00007ffd7c472000)
        libmy.so => not found
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000070eaa7000000)
        /lib64/ld-linux-x86-64.so.2 (0x000070eaa7267000)
  • 同时存在两种库时,编译器默认链接动态库。
  • 如果强制链接静态库,在编译时要额外添加-static
  • 只有动态库时,如果添加-static选项,会报错。

只有静态库时,不带-static选项,编译器如何选择?

cpp 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ gcc main.c -o main -I ./mylib/include/ -L ./mylib/lib/ -l my
wx@VM-8-14-ubuntu:~/linux-learning/friend$ ls -l
total 24
-rwxr-xr-x 1 wx ubuntu 15984 Jan  9 15:31 main
-rw-r--r-- 1 wx ubuntu    95 Jan  9 10:37 main.c
drwxr-xr-x 4 wx ubuntu  4096 Jan  9 11:57 mylib
wx@VM-8-14-ubuntu:~/linux-learning/friend$ tree .
.
├── main
├── main.c
└── mylib
    ├── include
    │   ├── mystdio.h
    │   └── mystr.h
    └── lib
        ├── libmy.a
        └── libmy.so

4 directories, 6 files
bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ tree .
.
├── main
├── main.c
└── mylib
    ├── include
    │   ├── mystdio.h
    │   └── mystr.h
    └── lib
        ├── libm.so
        └── libmy.a

4 directories, 6 files
wx@VM-8-14-ubuntu:~/linux-learning/friend$ gcc main.c -o main -I ./mylib/include/ -L ./mylib/lib/ -l my
wx@VM-8-14-ubuntu:~/linux-learning/friend$ ls -al
total 32
drwxr-xr-x  3 wx ubuntu  4096 Jan  9 15:23 .
drwxr-xr-x 14 wx ubuntu  4096 Jan  9 10:17 ..
-rwxr-xr-x  1 wx ubuntu 16088 Jan  9 15:23 main
-rw-r--r--  1 wx ubuntu    95 Jan  9 10:37 main.c
drwxr-xr-x  4 wx ubuntu  4096 Jan  9 11:57 mylib
wx@VM-8-14-ubuntu:~/linux-learning/friend$ gcc main.c -o main -I ./mylib/include/ -L ./mylib/lib/ -l my -static
wx@VM-8-14-ubuntu:~/linux-learning/friend$ ls -l
total 776
-rwxr-xr-x 1 wx ubuntu 785488 Jan  9 15:23 main
-rw-r--r-- 1 wx ubuntu     95 Jan  9 10:37 main.c
drwxr-xr-x 4 wx ubuntu   4096 Jan  9 11:57 mylib
  • 如果只提供静态库,即便采用默认动态链接,对于该库来说,编译器也只能静态链接。
  • 但是c语言标准库有动态库,所以这种情况动态链接其他库,只静态链接我们提供的静态库
  • -static作用:强制要求所以库都静态链接

.o文件被称为目标文件

4.ELF文件

可执行文件,是一个二进制文件,这些二进制位有自己的结构和格式---ELF格式

目标.o文件,动静态库都是ELF格式的文件。

4.1elf文件的组成部分

elf文件核心由4部分构成:

**1.ELF头(ELFheader):**描述文件的主要特性。其位于文件的开始位置,它的主要目的是定位文件的其他部分(比如从哪个位置开始是"程序头表",从哪个位置开始是"节头表")。

**2.程序头表(Programheadertable)**列举了所有有效的段(segments)和他们的属性。表里记着每个段的开始的位置和位移(offset)、长度,毕竟这些段,都是紧密的放在二进制文件中,需要段表的描述信息,才能把他们每个段分割开。

可执行程序天然有各种各种各样的数据节,与虚拟地址空间中各个区域的划分有关

**3.节头表(Sectionheadertable):**包含对节(sections)的描述(各个节的其实位置和内容说明)。

4.节(Section): ELF文件中的基本组成单位,包含了特定类型的数据。ELF文件的各种信息和数据都存储在不同的节中,如代码节存储了可执行代码,数据节存储了全局变量和静态数据等**。**

反汇编查看节中的内容:objdump -S [文件名]

最常见的节:

代码节(text):用于保存机器指令,是程序的主要执行部分(程序在形成进程后,加载到虚拟地址空间的代码区)。

数据节(data):保存已初始化的全局变量和局部静态变量(虚拟地址空间的各种数据区)。

节和段关系

elf中没有段信息

elf中会有一个个的数据节,不同的节大小不一,权限可以有相同的。

  • elf也是文件,其内容也都是以4kb为单位进行存储,OS读取磁盘内容以4kb为单位导入到内存中。
  • 一个节要占用一个4kb的data block,如果不进行加工,一个节可能只有128字节,但是还要占用4kb的内存,会很浪费空间
  • 为了更好的实现内存加载和不浪费空间,多个相同权限的节,在加载的时候,由加载器帮我们进行节合并,形成段。
  • 这些由相同权限的节结合成的段,加载到内存后就是虚拟地址空间中的代码段,未初始化数据段,已初始化数据段,共享区等。
program header

该区域保存了,有多少段需要加载,以及每个段相关的节有哪些。

elf中的节:

4.2ELF文件形成可执行文件

  • .o文件,库文件,可执行程序都是ELF格式
  • .o文件和库文件(.o文件的集合)形成可执行文件的过程,就是这些文件相同部位的内容合并在一起。
  • 例如:不同目标文件的elf header区域的二进制内容,结合到一起,形成可执行文件的elf header区域的内容。

4.3可执行程序加载

  • 创建一个进程,先创建内核数据结构,然后加载elf格式的二进制文件。
  • 程序在编译好之后,还没加载到内存,程序中的每一行代码都有自己的"地址",反汇编中可以看出来。
  • 我们当前的编译器,编译可执行程序,它会对我们的每一行代码采用"平坦模式",对我们可执行程序中的每一行代码都进行编址,原则上从零号地址开始,在32位系统下,到32位全1结束,这些地址都是虚拟地址(逻辑地址)。
可执行程序的加载
  • 每一行代码加载到内存中后,既要有自己的虚拟地址也要有物理地址
  • 虚拟地址从二进制文件中来,加载到内存后,形成对应的物理地址。
cpp 复制代码
    unsigned long total_vm, locked_vm, shared_vm, exec_vm;
	unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
	unsigned long start_code, end_code, start_data, end_data;
	unsigned long start_brk, brk, start_stack;
	unsigned long arg_start, arg_end, env_start, env_end;
  • 创建进程需要先创建内核数据结构,那么就要创建虚拟地址空间,那么其中的数据怎么初始化
  • 虚拟地址空间(mm_struct)中各种区域的划分就是从磁盘中可执行程序中读取。
  • program header中virtaddr memsiz部分就已经说明了,虚拟地址空间各个区域的划分由哪开始到哪结束
  • 进程数据结构中的地址都是虚拟地址,cpu在执行进程时,进入cpu的地址都是虚拟地址,经过mmu转化成物理地址。
  • 得到物理地址,就可以直接访问内存中的数据了。

5.动静态库的链接与加载

在linux中程序运行的第一个入口是CRTStartup函数,整个函数会调用main函数

库也是elf格式的文件。

动态库加载到内存,是要被映射到共享区。

  • 动态库会被多个程序所需要,动态库也只需要在内存中存在一份即可。

静态链接过程

本质是把库中的相关代码拷贝到程序中。

cpp 复制代码
code.c
#include<stdio.h>
void run();
int main()
{
printf("hello world!\n");
run();
return 0;
}


hello.c
#include<stdio.h>
void run()
{
printf("running...\n");
}
bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ gcc -c *.c
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ ls
code.c  code.o  hello.c  hello.o
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ gcc *.o -o myexe
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ ls
code.c  code.o  hello.c  hello.o  myexe
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ ./myexe
hello world!
running...
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ file code.o
code.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ objdump -d code.o

code.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:   f3 0f 1e fa             endbr64
   4:   55                      push   %rbp
   5:   48 89 e5                mov    %rsp,%rbp
   8:   48 8d 05 00 00 00 00    lea    0x0(%rip),%rax        # f <main+0xf>
   f:   48 89 c7                mov    %rax,%rdi
  12:   e8 00 00 00 00          call   17 <main+0x17>
  17:   b8 00 00 00 00          mov    $0x0,%eax
  1c:   e8 00 00 00 00          call   21 <main+0x21>
  21:   b8 00 00 00 00          mov    $0x0,%eax
  26:   5d                      pop    %rbp
  27:   c3                      ret
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ objdump -d hello.o

hello.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <run>:
   0:   f3 0f 1e fa             endbr64
   4:   55                      push   %rbp
   5:   48 89 e5                mov    %rsp,%rbp
   8:   48 8d 05 00 00 00 00    lea    0x0(%rip),%rax        # f <run+0xf>
   f:   48 89 c7                mov    %rax,%rdi
  12:   e8 00 00 00 00          call   17 <run+0x17>
  17:   90                      nop
  18:   5d                      pop    %rbp
  19:   c3                      ret
  • 第三列是反汇编,第二列是反汇编对应的cpu能看懂的指令集,e8就是call命令。
  • 但是e8后面跟的是全0,说明并不知道需要调用哪个方法,因为我们只是看的是.o文件,还没有链接,所以调用函数不确定。
  • code.c代码中调用了printf和run函数,所以有两个call命令,由于没有链接,所以e8后面是全0(调用的目标函数地址不知道)
  • 没有找到调用函数的地址,但是编译.c文件到.o不报错,是暂时忽略了,目标调用函数的地址

查看.o文件的符号表:

bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ readelf -s code.o

Symbol table '.symtab' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS code.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 .text
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 .rodata
     4: 0000000000000000    40 FUNC    GLOBAL DEFAULT    1 main
     5: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts
     6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND run
bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ readelf -s hello.o

Symbol table '.symtab' contains 6 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 .text
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 .rodata
     4: 0000000000000000    26 FUNC    GLOBAL DEFAULT    1 run
     5: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts
  • 因为还没链接,所以代码中的符号的地址也都是未知的。
链接所有的.o文件
cpp 复制代码
Symbol table '.dynsym' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _[...]@GLIBC_2.34 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (3)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
     6: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND [...]@GLIBC_2.2.5 (3)

Symbol table '.symtab' contains 38 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS Scrt1.o
     2: 000000000000038c    32 OBJECT  LOCAL  DEFAULT    4 __abi_tag
     3: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
     4: 0000000000001090     0 FUNC    LOCAL  DEFAULT   16 deregister_tm_clones
     5: 00000000000010c0     0 FUNC    LOCAL  DEFAULT   16 register_tm_clones
     6: 0000000000001100     0 FUNC    LOCAL  DEFAULT   16 __do_global_dtors_aux
     7: 0000000000004010     1 OBJECT  LOCAL  DEFAULT   26 completed.0
     8: 0000000000003dc0     0 OBJECT  LOCAL  DEFAULT   22 __do_global_dtor[...]
     9: 0000000000001140     0 FUNC    LOCAL  DEFAULT   16 frame_dummy
    10: 0000000000003db8     0 OBJECT  LOCAL  DEFAULT   21 __frame_dummy_in[...]
    11: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS code.c
    12: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c
    13: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    14: 0000000000002120     0 OBJECT  LOCAL  DEFAULT   20 __FRAME_END__
    15: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS 
    16: 0000000000003dc8     0 OBJECT  LOCAL  DEFAULT   23 _DYNAMIC
    17: 000000000000201c     0 NOTYPE  LOCAL  DEFAULT   19 __GNU_EH_FRAME_HDR
    18: 0000000000003fb8     0 OBJECT  LOCAL  DEFAULT   24 _GLOBAL_OFFSET_TABLE_
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_mai[...]
    20: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
    21: 0000000000004000     0 NOTYPE  WEAK   DEFAULT   25 data_start
    22: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5
    23: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   25 _edata
    24: 0000000000001171    26 FUNC    GLOBAL DEFAULT   16 run
    25: 000000000000118c     0 FUNC    GLOBAL HIDDEN    17 _fini
    26: 0000000000004000     0 NOTYPE  GLOBAL DEFAULT   25 __data_start
    27: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    28: 0000000000004008     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle
    29: 0000000000002000     4 OBJECT  GLOBAL DEFAULT   18 _IO_stdin_used
    30: 0000000000004018     0 NOTYPE  GLOBAL DEFAULT   26 _end
    31: 0000000000001060    38 FUNC    GLOBAL DEFAULT   16 _start
    32: 0000000000004010     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start
    33: 0000000000001149    40 FUNC    GLOBAL DEFAULT   16 main
    34: 0000000000004010     0 OBJECT  GLOBAL HIDDEN    25 __TMC_END__
    35: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
    36: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@G[...]
    37: 0000000000001000     0 FUNC    GLOBAL HIDDEN    12 _init
bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ objdump -d myexe

myexe:     file format elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
    1000:       f3 0f 1e fa             endbr64
    1004:       48 83 ec 08             sub    $0x8,%rsp
    1008:       48 8b 05 d9 2f 00 00    mov    0x2fd9(%rip),%rax        # 3fe8 <__gmon_start__@Base>
    100f:       48 85 c0                test   %rax,%rax
    1012:       74 02                   je     1016 <_init+0x16>
    1014:       ff d0                   call   *%rax
    1016:       48 83 c4 08             add    $0x8,%rsp
    101a:       c3                      ret

Disassembly of section .plt:

0000000000001020 <.plt>:
    1020:       ff 35 9a 2f 00 00       push   0x2f9a(%rip)        # 3fc0 <_GLOBAL_OFFSET_TABLE_+0x8>
    1026:       ff 25 9c 2f 00 00       jmp    *0x2f9c(%rip)        # 3fc8 <_GLOBAL_OFFSET_TABLE_+0x10>
    102c:       0f 1f 40 00             nopl   0x0(%rax)
    1030:       f3 0f 1e fa             endbr64
    1034:       68 00 00 00 00          push   $0x0
    1039:       e9 e2 ff ff ff          jmp    1020 <_init+0x20>
    103e:       66 90                   xchg   %ax,%ax

Disassembly of section .plt.got:

0000000000001040 <__cxa_finalize@plt>:
    1040:       f3 0f 1e fa             endbr64
    1044:       ff 25 ae 2f 00 00       jmp    *0x2fae(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    104a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

Disassembly of section .plt.sec:

0000000000001050 <puts@plt>:
    1050:       f3 0f 1e fa             endbr64
    1054:       ff 25 76 2f 00 00       jmp    *0x2f76(%rip)        # 3fd0 <puts@GLIBC_2.2.5>
    105a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

Disassembly of section .text:

0000000000001060 <_start>:
    1060:       f3 0f 1e fa             endbr64
    1064:       31 ed                   xor    %ebp,%ebp
    1066:       49 89 d1                mov    %rdx,%r9
    1069:       5e                      pop    %rsi
    106a:       48 89 e2                mov    %rsp,%rdx
    106d:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
    1071:       50                      push   %rax
    1072:       54                      push   %rsp
    1073:       45 31 c0                xor    %r8d,%r8d
    1076:       31 c9                   xor    %ecx,%ecx
    1078:       48 8d 3d ca 00 00 00    lea    0xca(%rip),%rdi        # 1149 <main>
    107f:       ff 15 53 2f 00 00       call   *0x2f53(%rip)        # 3fd8 <__libc_start_main@GLIBC_2.34>
    1085:       f4                      hlt
    1086:       66 2e 0f 1f 84 00 00    cs nopw 0x0(%rax,%rax,1)
    108d:       00 00 00 

0000000000001090 <deregister_tm_clones>:
    1090:       48 8d 3d 79 2f 00 00    lea    0x2f79(%rip),%rdi        # 4010 <__TMC_END__>
    1097:       48 8d 05 72 2f 00 00    lea    0x2f72(%rip),%rax        # 4010 <__TMC_END__>
    109e:       48 39 f8                cmp    %rdi,%rax
    10a1:       74 15                   je     10b8 <deregister_tm_clones+0x28>
    10a3:       48 8b 05 36 2f 00 00    mov    0x2f36(%rip),%rax        # 3fe0 <_ITM_deregisterTMCloneTable@Base>
    10aa:       48 85 c0                test   %rax,%rax
    10ad:       74 09                   je     10b8 <deregister_tm_clones+0x28>
    10af:       ff e0                   jmp    *%rax
    10b1:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
    10b8:       c3                      ret
    10b9:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

00000000000010c0 <register_tm_clones>:
    10c0:       48 8d 3d 49 2f 00 00    lea    0x2f49(%rip),%rdi        # 4010 <__TMC_END__>
    10c7:       48 8d 35 42 2f 00 00    lea    0x2f42(%rip),%rsi        # 4010 <__TMC_END__>
    10ce:       48 29 fe                sub    %rdi,%rsi
    10d1:       48 89 f0                mov    %rsi,%rax
    10d4:       48 c1 ee 3f             shr    $0x3f,%rsi
    10d8:       48 c1 f8 03             sar    $0x3,%rax
    10dc:       48 01 c6                add    %rax,%rsi
    10df:       48 d1 fe                sar    $1,%rsi
    10e2:       74 14                   je     10f8 <register_tm_clones+0x38>
    10e4:       48 8b 05 05 2f 00 00    mov    0x2f05(%rip),%rax        # 3ff0 <_ITM_registerTMCloneTable@Base>
    10eb:       48 85 c0                test   %rax,%rax
    10ee:       74 08                   je     10f8 <register_tm_clones+0x38>
    10f0:       ff e0                   jmp    *%rax
    10f2:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
    10f8:       c3                      ret
    10f9:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001100 <__do_global_dtors_aux>:
    1100:       f3 0f 1e fa             endbr64
    1104:       80 3d 05 2f 00 00 00    cmpb   $0x0,0x2f05(%rip)        # 4010 <__TMC_END__>
    110b:       75 2b                   jne    1138 <__do_global_dtors_aux+0x38>
    110d:       55                      push   %rbp
    110e:       48 83 3d e2 2e 00 00    cmpq   $0x0,0x2ee2(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    1115:       00 
    1116:       48 89 e5                mov    %rsp,%rbp
    1119:       74 0c                   je     1127 <__do_global_dtors_aux+0x27>
    111b:       48 8b 3d e6 2e 00 00    mov    0x2ee6(%rip),%rdi        # 4008 <__dso_handle>
    1122:       e8 19 ff ff ff          call   1040 <__cxa_finalize@plt>
    1127:       e8 64 ff ff ff          call   1090 <deregister_tm_clones>
    112c:       c6 05 dd 2e 00 00 01    movb   $0x1,0x2edd(%rip)        # 4010 <__TMC_END__>
    1133:       5d                      pop    %rbp
    1134:       c3                      ret
    1135:       0f 1f 00                nopl   (%rax)
    1138:       c3                      ret
    1139:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001140 <frame_dummy>:
    1140:       f3 0f 1e fa             endbr64
    1144:       e9 77 ff ff ff          jmp    10c0 <register_tm_clones>

0000000000001149 <main>:
    1149:       f3 0f 1e fa             endbr64
    114d:       55                      push   %rbp
    114e:       48 89 e5                mov    %rsp,%rbp
    1151:       48 8d 05 ac 0e 00 00    lea    0xeac(%rip),%rax        # 2004 <_IO_stdin_used+0x4>
    1158:       48 89 c7                mov    %rax,%rdi
    115b:       e8 f0 fe ff ff          call   1050 <puts@plt>
    1160:       b8 00 00 00 00          mov    $0x0,%eax
    1165:       e8 07 00 00 00          call   1171 <run>
    116a:       b8 00 00 00 00          mov    $0x0,%eax
    116f:       5d                      pop    %rbp
    1170:       c3                      ret

0000000000001171 <run>:
    1171:       f3 0f 1e fa             endbr64
    1175:       55                      push   %rbp
    1176:       48 89 e5                mov    %rsp,%rbp
    1179:       48 8d 05 91 0e 00 00    lea    0xe91(%rip),%rax        # 2011 <_IO_stdin_used+0x11>
    1180:       48 89 c7                mov    %rax,%rdi
    1183:       e8 c8 fe ff ff          call   1050 <puts@plt>
    1188:       90                      nop
    1189:       5d                      pop    %rbp
    118a:       c3                      ret

Disassembly of section .fini:

000000000000118c <_fini>:
    118c:       f3 0f 1e fa             endbr64
    1190:       48 83 ec 08             sub    $0x8,%rsp
    1194:       48 83 c4 08             add    $0x8,%rsp
    1198:       c3                      ret
  • run方法和main方法等都有了自己的地址
  • 其他方法或指令还没有自己的地址是因为c标准库是动态链接的。
  • UND表示没找到

静态链接就是把库中的.o进行合并,和上述过程一样

所以链接其实就是将编译之后的所有目标文件连同用到的一些静态库运行时库组合,拼装成一个独立的可执行文件。其中就包括我们之前提到的地址修正,当所有模块组合在一起之后,链接器会根据我们的.o文件或者静态库中的重定位表找到那些需要被重定位的函数全局变量,从而修正它们的地址。这其实就是静态链接的过程

动态链接与动态库加载

  • 动态链接的可执行程序,是知道自己要链接哪些库的。
  • 虽然形成了可执行程序,但是其中的一些动态链接的方法和符号表是UND的
  • 所以这个程序在运行之前,库方法不确定,动态链接的可执行程序在加载到内存之前,要先把该程序链接的动态库加载到内存中。
  • 库加载到内存后,然后在内存中让这些UND方法变成确定的动态库中的地址。
  • 要加载对应的库,就要先找到对应的库,而一个个的库本质就是磁盘文件,所以找库就是找文件的过程,所以就需要库的路径。
  • 库本质就是.o文件的集合,加载到内存后库中的函数都有自己的物理地址。
  • 需要在进程的页表中建立虚拟地址与库函数物理地址的映射
  • 动态库的物理地址会被页表映射到进程虚拟地址空间中的共享区
  • 库函数的调用本质是在进程的虚拟地址空间内进行函数跳转
  • 如果OS中又有一个新的进程,也需要使用到与上一个进程相同的动态库,这个库以经被上一个进程加载到内存中了,同时上一个进程没有结束。
  • 所以新的进程就不需要再次加载这个动态库。
  • 只需要把新进程需要用到的代码区的库函数的地址改成实际库函数物理地址经过页表映射后的共享区的虚拟地址,以及修改符号表中的GND即可
  • 所以同一个库可能会被多个进程共享使用,这种动态库也叫做共享库。
  • 库本身也是代码和数据,多个进程需要用到的资源,只需要在内存中形成一份。
  • 库被使用的本质是把库映射到了进程的地址空间中

既然第二个进程需要的库如果已经被加载到内存中,就不需要再加载一次,那么OS是怎么知道哪些库已经被加载了?

内存中有很多不同的库,OS就要对这些库进行管理(先描述,再组织),所以在操作系统内部就会有一种数据结构来描述这些库。

动态链接

动态链接其实远比静态链接要常用得多。比如我们查看下hel1o这个可执行程序依赖的动态库,会发现它就用到了一个c动态链接库:

cpp 复制代码
ldd myexe
        linux-vdso.so.1 (0x00007ffc63b34000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007243ad600000)
        /lib64/ld-linux-x86-64.so.2 (0x00007243ad970000)
  • 这里的libc.so是C语言的运行时库,里面提供了常用的标准输入输出文件字符串处理等等这些功能。
  • 那为什么编译器默认不使用静态链接呢?静态链接会将编译产生的所有目标文件,连同用到的各种库,合并形成一个独立的可执行文件,它不需要额外的依赖就可以运行。照理来说应该更加方便才对
  • 静态链接最大的问题在于生成的文件体积大,并且相当耗费内存资源。随着软件复杂度的提升,我们的操作系统也越来越臃肿,不同的软件就有可能都包含了相同的功能和代码,显然会浪费大量的硬盘空间。
  • 这个时候,动态链接的优势就体现出来了,我们可以将需要共享的代码单独提取出来,保存成一个独立的动态链接库,等到程序运行的时候再将它们加载到内存,这样不但可以节省空间,因为同一个模块在内存中只需要保留一份副本,可以被不同的进程所共享。
  • 动态链接到底是如何工作的??
  • 首先要交代一个结论,**动态链接实际上将链接的整个过程推迟到了程序加载的时候。**比如我们去运行一个程序,操作系统会首先将程序的数据代码连同它用到的一系列动态库先加载到内存,其中每个动态库的加载地址都是不固定的,操作系统会根据当前地址空间的使用情况为它们动态分配一段内存。
  • 当动态库被加载到内存以后,一旦它的内存地址被确定,我们就可以去修正动态库中的那些函数跳转地址了。

动态库自身是没有main函数的。动态库内部是包含了大量的方法的,每一个方法都要有自己的地址,这些方法的编址也是平坦编址,全0到全F的,我们把这个地址被叫做逻辑偏移地址。

编译器对可执行程序的操作

bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ ldd myexe
        linux-vdso.so.1 (0x00007ffc63b34000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007243ad600000)
        /lib64/ld-linux-x86-64.so.2 (0x00007243ad970000)
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ ldd /usr/bin/ls
        linux-vdso.so.1 (0x00007ffe4298a000)
        libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x0000711eddb5b000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x0000711edd800000)
        libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x0000711eddac1000)
        /lib64/ld-linux-x86-64.so.2 (0x0000711eddbb6000)

在C/C++程序中,当程序开始执行时,它首先并不会直接跳转到main函数。实际上,程序的入口点

是_start,这是一个由C运行时库(通常是glibc)或链接器(如ld)提供的特殊函数。

在_start函数中,会执行一系列初始化操作,这些操作包括:

1.设置堆栈:为程序创建一个初始的堆栈环境。

2.初始化数据段:将程序的数据段(如全局变量和静态变量)从初始化数据段复制到相应的内存位

置,并清零未初始化的数据段。

3.动态链接:这是关键的一步,start函数会调用动态链接器的代码来解析和加载程序所依赖的

动态库(sharedlibraries)。动态链接器会处理所有的符号解析和重定位,确保程序中的函数调

用和变量访问能够正确地映射到动态库中的实际地址

动态链接器:

动态链接器(如ld-linux.so)负责在程序运行时加载动态库。

  • 当程序启动时,动态链接器会解析程序中的动态库依赖,并加载这些库到内存中。环境变量和配置文件
  • Linux系统通过环境变量(如LD_LIBRARY_PATH)和配置文件(如/etc/ld.sO.conf及其子配置文件)来指定动态库的搜索路径。
  • 这些路径会被动态链接器在加载动态库时搜索。

缓存文件:

  • 为了提高动态库的加载效率,Linux系统会维护一个名为/etc/ld.so.cache的缓存文件。
  • 该文件包含了系统中所有已知动态库的路径和相关信息,动态链接器在加载动态库时会首先搜索这个缓存文件

4.调用__libc_start_main:一旦动态链接完成,_start函数会调用__libc_start_main(这是glibc提供的一个函数)。__libc_start_main函数负责执行一些额外的初始化工作,比如设置信号处理函数、初始化线程库人如果使用了线程)等。

5.调用main函数:最后,__libc_start_main函数会调用程序的main函数,此时程序的执行控制权才正式交给用户编写的代码。

6.处理main函数的返回值:当main函数返回时,libc_start_main会负责处理这个返回值,并最终调用_exit函数来终止程序。

上述过程描述了C/C++程序在main函数之前执行的一系列操作,但这些操作对于大多数程序员来说是透明的。程序员通常只需要关注main函数中的代码,而不需要关心底层的初始化过程。然而,了解这些底层细节有助于更好地理解程序的执行流程和调试问题

动态库中的相对地址

动态库为了随时进行加载,为了支持并映射到任意进程的任意位置,对动态库中的方法,统一编址,采用相对编址的方案进行编制的(其实可执行程序也一样,都要遵守平坦模式,只不过exe是直接加载的)。

bash 复制代码
# ubuntu下查看任意⼀个库的反汇编
objdump -S /lib/x86_64-linux-gnu/libc-2.31.so | less
# Cetnos下查看任意⼀个库的反汇编
$ objdump -S /lib64/libc-2.17.so | less
可执行程序和库的映射
  • 动态库也是一个文件,要访问也是要被先加载,要加载也是要被打开的
  • 让我们的进程找到动态库的本质:也是文件操作,不过我们访问库函数,通过虚拟地址进行跳转访问的,所以需要把动态库映射到进程的地址空间中
程序进行库函数调用的过程
  • 库已经被我们映射到了当前进程的地址空间中
  • 库的虚拟起始地址我们也已经知道了
  • 库中每一个方法的偏移量地址我们也知道
  • 所有:访问库中任意方法,只需要知道库的起始虚拟地址+方法偏移量即可定位库中的方法
  • 而且:整个调用过程,是从代码区跳转到共享区,调用完毕在返回到代码区,整个过程完全在进程地址空间中进行的。
全局偏移量表GOT
  • 我们的程序运行之前,先把所有库加载并映射,所有库的起始虚拟地址都应该提前知道
  • 然后对我们加载到内存中的程序的库函数调用进行地址修改,在内存中二次完成地址设置(这个叫做加载地址重定位)

所以:动态链接采用的做法是在.data(可执行程序或者库自己)中专门预留一片区域用来存放函数的跳转地址,它也被叫做全局偏移表GOT,表中每一项都是本运行模块要引用的一个全局变量或函数的地址。

因为.data区域是可读写的,所以可以⽀持动态进⾏修改

1.由于代码段只读,我们不能直接修改代码段。但有了GOT表,代码便可以被所有进程共享。但在不同进程的地址空间中,各动态库的绝对地址、相对位置都不同。反映到GOT表上,就是每个进程的每个动态库都有独立的GOT表,所以进程间不能共享GOT表。

2.在单个.so下,由于GOT表与text的相对位置是固定的,我们完全可以利用CPU的相对寻址来找

到GOT表。

3.在调用函数的时候会首先查表,然后根据表中的地址来进行跳转,这些地址在动态库加载的时候会被修改为真正的地址。

4.这种方式实现的动态链接就被叫做PIC地址无关代码 。换句话说,我们的动态库不需要做任何修

改,被加载到任意内存地址都能够正常运行,并且能够被所有进程共享,这也是为什么之前我们给

编译器指定-fPIC参数的原因,PIC=相对编址+GOT。

bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/leasson14$ objdump -S myexe

myexe:     file format elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
    1000:       f3 0f 1e fa             endbr64
    1004:       48 83 ec 08             sub    $0x8,%rsp
    1008:       48 8b 05 d9 2f 00 00    mov    0x2fd9(%rip),%rax        # 3fe8 <__gmon_start__@Base>
    100f:       48 85 c0                test   %rax,%rax
    1012:       74 02                   je     1016 <_init+0x16>
    1014:       ff d0                   call   *%rax
    1016:       48 83 c4 08             add    $0x8,%rsp
    101a:       c3                      ret

Disassembly of section .plt:

0000000000001020 <.plt>:
    1020:       ff 35 9a 2f 00 00       push   0x2f9a(%rip)        # 3fc0 <_GLOBAL_OFFSET_TABLE_+0x8>
    1026:       ff 25 9c 2f 00 00       jmp    *0x2f9c(%rip)        # 3fc8 <_GLOBAL_OFFSET_TABLE_+0x10>
    102c:       0f 1f 40 00             nopl   0x0(%rax)
    1030:       f3 0f 1e fa             endbr64
    1034:       68 00 00 00 00          push   $0x0
    1039:       e9 e2 ff ff ff          jmp    1020 <_init+0x20>
    103e:       66 90                   xchg   %ax,%ax

Disassembly of section .plt.got:

0000000000001040 <__cxa_finalize@plt>:
    1040:       f3 0f 1e fa             endbr64
    1044:       ff 25 ae 2f 00 00       jmp    *0x2fae(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    104a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

Disassembly of section .plt.sec:

0000000000001050 <puts@plt>:
    1050:       f3 0f 1e fa             endbr64
    1054:       ff 25 76 2f 00 00       jmp    *0x2f76(%rip)        # 3fd0 <puts@GLIBC_2.2.5>
    105a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

Disassembly of section .text:

0000000000001060 <_start>:
    1060:       f3 0f 1e fa             endbr64
    1064:       31 ed                   xor    %ebp,%ebp
    1066:       49 89 d1                mov    %rdx,%r9
    1069:       5e                      pop    %rsi
    106a:       48 89 e2                mov    %rsp,%rdx
    106d:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
    1071:       50                      push   %rax
    1072:       54                      push   %rsp
    1073:       45 31 c0                xor    %r8d,%r8d
    1076:       31 c9                   xor    %ecx,%ecx
    1078:       48 8d 3d ca 00 00 00    lea    0xca(%rip),%rdi        # 1149 <main>
    107f:       ff 15 53 2f 00 00       call   *0x2f53(%rip)        # 3fd8 <__libc_start_main@GLIBC_2.34>
    1085:       f4                      hlt
    1086:       66 2e 0f 1f 84 00 00    cs nopw 0x0(%rax,%rax,1)
    108d:       00 00 00 

0000000000001090 <deregister_tm_clones>:
    1090:       48 8d 3d 79 2f 00 00    lea    0x2f79(%rip),%rdi        # 4010 <__TMC_END__>
    1097:       48 8d 05 72 2f 00 00    lea    0x2f72(%rip),%rax        # 4010 <__TMC_END__>
    109e:       48 39 f8                cmp    %rdi,%rax
    10a1:       74 15                   je     10b8 <deregister_tm_clones+0x28>
    10a3:       48 8b 05 36 2f 00 00    mov    0x2f36(%rip),%rax        # 3fe0 <_ITM_deregisterTMCloneTable@Base>
    10aa:       48 85 c0                test   %rax,%rax
    10ad:       74 09                   je     10b8 <deregister_tm_clones+0x28>
    10af:       ff e0                   jmp    *%rax
    10b1:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
    10b8:       c3                      ret
    10b9:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

00000000000010c0 <register_tm_clones>:
    10c0:       48 8d 3d 49 2f 00 00    lea    0x2f49(%rip),%rdi        # 4010 <__TMC_END__>
    10c7:       48 8d 35 42 2f 00 00    lea    0x2f42(%rip),%rsi        # 4010 <__TMC_END__>
    10ce:       48 29 fe                sub    %rdi,%rsi
    10d1:       48 89 f0                mov    %rsi,%rax
    10d4:       48 c1 ee 3f             shr    $0x3f,%rsi
    10d8:       48 c1 f8 03             sar    $0x3,%rax
    10dc:       48 01 c6                add    %rax,%rsi
    10df:       48 d1 fe                sar    $1,%rsi
    10e2:       74 14                   je     10f8 <register_tm_clones+0x38>
    10e4:       48 8b 05 05 2f 00 00    mov    0x2f05(%rip),%rax        # 3ff0 <_ITM_registerTMCloneTable@Base>
    10eb:       48 85 c0                test   %rax,%rax
    10ee:       74 08                   je     10f8 <register_tm_clones+0x38>
    10f0:       ff e0                   jmp    *%rax
    10f2:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
    10f8:       c3                      ret
    10f9:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001100 <__do_global_dtors_aux>:
    1100:       f3 0f 1e fa             endbr64
    1104:       80 3d 05 2f 00 00 00    cmpb   $0x0,0x2f05(%rip)        # 4010 <__TMC_END__>
    110b:       75 2b                   jne    1138 <__do_global_dtors_aux+0x38>
    110d:       55                      push   %rbp
    110e:       48 83 3d e2 2e 00 00    cmpq   $0x0,0x2ee2(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    1115:       00 
    1116:       48 89 e5                mov    %rsp,%rbp
    1119:       74 0c                   je     1127 <__do_global_dtors_aux+0x27>
    111b:       48 8b 3d e6 2e 00 00    mov    0x2ee6(%rip),%rdi        # 4008 <__dso_handle>
    1122:       e8 19 ff ff ff          call   1040 <__cxa_finalize@plt>
    1127:       e8 64 ff ff ff          call   1090 <deregister_tm_clones>
    112c:       c6 05 dd 2e 00 00 01    movb   $0x1,0x2edd(%rip)        # 4010 <__TMC_END__>
    1133:       5d                      pop    %rbp
    1134:       c3                      ret
    1135:       0f 1f 00                nopl   (%rax)
    1138:       c3                      ret
    1139:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001140 <frame_dummy>:
    1140:       f3 0f 1e fa             endbr64
    1144:       e9 77 ff ff ff          jmp    10c0 <register_tm_clones>

0000000000001149 <main>:
    1149:       f3 0f 1e fa             endbr64
    114d:       55                      push   %rbp
    114e:       48 89 e5                mov    %rsp,%rbp
    1151:       48 8d 05 ac 0e 00 00    lea    0xeac(%rip),%rax        # 2004 <_IO_stdin_used+0x4>
    1158:       48 89 c7                mov    %rax,%rdi
    115b:       e8 f0 fe ff ff          call   1050 <puts@plt>
    1160:       b8 00 00 00 00          mov    $0x0,%eax
    1165:       e8 07 00 00 00          call   1171 <run>
    116a:       b8 00 00 00 00          mov    $0x0,%eax
    116f:       5d                      pop    %rbp
    1170:       c3                      ret

0000000000001171 <run>:
    1171:       f3 0f 1e fa             endbr64
    1175:       55                      push   %rbp
    1176:       48 89 e5                mov    %rsp,%rbp
    1179:       48 8d 05 91 0e 00 00    lea    0xe91(%rip),%rax        # 2011 <_IO_stdin_used+0x11>
    1180:       48 89 c7                mov    %rax,%rdi
    1183:       e8 c8 fe ff ff          call   1050 <puts@plt>
    1188:       90                      nop
    1189:       5d                      pop    %rbp
    118a:       c3                      ret

Disassembly of section .fini:

000000000000118c <_fini>:
    118c:       f3 0f 1e fa             endbr64
    1190:       48 83 ec 08             sub    $0x8,%rsp
    1194:       48 83 c4 08             add    $0x8,%rsp
    1198:       c3                      ret
库间依赖
  • 不仅仅有可执行程序调用库
  • 库也会调用其他库!!库之间是有依赖的,如何做到库和库之间互相调用也是与地址无关的呢??
  • 库中也有.GOT,和可执行一样!这也就是为什么大家为什么都是ELF的格式!

由于动态链接在程序加载的时候需要对大量函数进行重定位,这一步显然是非常耗时的。为了进一

步降低开销,我们的操作系统还做了一些其他的优化,比如延迟绑定,或者也叫PLT(过程连接表

(ProcedureLinkageTable))。与其在程序一开始就对所有函数进行重定位,不如将这个过程

推迟到函数第一次被调用的时候,因为绝大多数动态库中的函数可能在程序运行期间一次都不会被

使用到。

思路是:GOT中的跳转地址默认会指向一段辅助代码,它也被叫做桩代码/stup。在我们第一次

调用函数的时候,这段代码会负责查询真正函数的跳转地址,并且去更新GOT表。于是我们再次

调用函数的时候,就会直接跳转到动态库中真正的函数实现

总而言之,动态链接实际上将链接的整个过程,比如符号查询、地址的重定位从编译时推迟到了程序的运行时,它虽然牺牲了一定的性能和程序加载时间,但绝对是物有所值的。因为动态链接能够更有效的利用磁盘空间和内存资源,以极大方便了代码的更新和维护,更关键的是,它实现了二进制级别的代码复用。
解析依赖关系的时候,就是加载并完善互相之间的GOT表的过程.

6.查看elf格式文件中各个部分内容的操作

6.1查看ELF header

bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ readelf -h main
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Position-Independent Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1080
  Start of program headers:          64 (bytes into file)
  Start of section headers:          14000 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 30
cpp 复制代码
// 内核中关于ELF Header相关的数据结构
// 没错,操作系统⾃⼰必须能够识别特定格式的可执⾏程序:/linux/include/elf.h
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry; /* Entry point */
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;

typedef struct elf64_hdr {
unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;

6.2ELF Program Header Table

bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ readelf -l main

Elf file type is DYN (Position-Independent Executable file)
Entry point 0x1080
There are 13 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000002d8 0x00000000000002d8  R      0x8
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000668 0x0000000000000668  R      0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x0000000000000199 0x0000000000000199  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x00000000000000e4 0x00000000000000e4  R      0x1000
  LOAD           0x0000000000002da0 0x0000000000003da0 0x0000000000003da0
                 0x0000000000000270 0x0000000000000278  RW     0x1000
  DYNAMIC        0x0000000000002db0 0x0000000000003db0 0x0000000000003db0
                 0x0000000000000200 0x0000000000000200  RW     0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000030 0x0000000000000030  R      0x8
  NOTE           0x0000000000000368 0x0000000000000368 0x0000000000000368
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000030 0x0000000000000030  R      0x8
  GNU_EH_FRAME   0x0000000000002004 0x0000000000002004 0x0000000000002004
                 0x0000000000000034 0x0000000000000034  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002da0 0x0000000000003da0 0x0000000000003da0
                 0x0000000000000260 0x0000000000000260  R      0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt 
   03     .init .plt .plt.got .plt.sec .text .fini 
   04     .rodata .eh_frame_hdr .eh_frame 
   05     .init_array .fini_array .dynamic .got .data .bss 
   06     .dynamic 
   07     .note.gnu.property 
   08     .note.gnu.build-id .note.ABI-tag 
   09     .note.gnu.property 
   10     .eh_frame_hdr 
   11     
   12     .init_array .fini_array .dynamic .got 
cpp 复制代码
// 内核中关于ELF Program Header相关的数据结构
typedef struct elf32_phdr{
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
typedef struct elf64_phdr {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment, file & memory */
} Elf64_Phdr;

6.3ELF Section Header Table

cpp 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ readelf -S main
There are 31 section headers, starting at offset 0x36b0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.pr[...] NOTE             0000000000000338  00000338
       0000000000000030  0000000000000000   A       0     0     8
  [ 3] .note.gnu.bu[...] NOTE             0000000000000368  00000368
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000038c  0000038c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003b0  000003b0
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003d8  000003d8
       00000000000000c0  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000498  00000498
       000000000000009f  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           0000000000000538  00000538
       0000000000000010  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          0000000000000548  00000548
       0000000000000030  0000000000000000   A       7     1     8
  [10] .rela.dyn         RELA             0000000000000578  00000578
       00000000000000c0  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             0000000000000638  00000638
       0000000000000030  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020
       0000000000000030  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001050  00001050
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001060  00001060
       0000000000000020  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         0000000000001080  00001080
       000000000000010c  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         000000000000118c  0000118c
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       0000000000000004  0000000000000004  AM       0     0     4
  [19] .eh_frame_hdr     PROGBITS         0000000000002004  00002004
       0000000000000034  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002038  00002038
       00000000000000ac  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003da0  00002da0
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003da8  00002da8
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003db0  00002db0
       0000000000000200  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003fb0  00002fb0
       0000000000000050  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004010  00003010
       0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00003010
       000000000000002b  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00003040
       0000000000000378  0000000000000018          29    18     8
  [29] .strtab           STRTAB           0000000000000000  000033b8
       00000000000001d7  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  0000358f
       000000000000011a  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), l (large), p (processor specific)
cpp 复制代码
// 内核中关于ELF Section Header相关的数据结构
typedef struct {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
typedef struct elf64_shdr {
Elf64_Word sh_name; /* Section name, index in string tbl */
Elf64_Word sh_type; /* Type of section */
Elf64_Xword sh_flags; /* Miscellaneous section attributes */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Size of section in bytes */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;

6.4查看具体的sections信息

bash 复制代码
wx@VM-8-14-ubuntu:~/linux-learning/friend$ objdump -S main

main:     file format elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
    1000:       f3 0f 1e fa             endbr64
    1004:       48 83 ec 08             sub    $0x8,%rsp
    1008:       48 8b 05 d9 2f 00 00    mov    0x2fd9(%rip),%rax        # 3fe8 <__gmon_start__@Base>
    100f:       48 85 c0                test   %rax,%rax
    1012:       74 02                   je     1016 <_init+0x16>
    1014:       ff d0                   call   *%rax
    1016:       48 83 c4 08             add    $0x8,%rsp
    101a:       c3                      ret

Disassembly of section .plt:

0000000000001020 <.plt>:
    1020:       ff 35 92 2f 00 00       push   0x2f92(%rip)        # 3fb8 <_GLOBAL_OFFSET_TABLE_+0x8>
    1026:       ff 25 94 2f 00 00       jmp    *0x2f94(%rip)        # 3fc0 <_GLOBAL_OFFSET_TABLE_+0x10>
    102c:       0f 1f 40 00             nopl   0x0(%rax)
    1030:       f3 0f 1e fa             endbr64
    1034:       68 00 00 00 00          push   $0x0
    1039:       e9 e2 ff ff ff          jmp    1020 <_init+0x20>
    103e:       66 90                   xchg   %ax,%ax
    1040:       f3 0f 1e fa             endbr64
    1044:       68 01 00 00 00          push   $0x1
    1049:       e9 d2 ff ff ff          jmp    1020 <_init+0x20>
    104e:       66 90                   xchg   %ax,%ax

Disassembly of section .plt.got:

0000000000001050 <__cxa_finalize@plt>:
    1050:       f3 0f 1e fa             endbr64
    1054:       ff 25 9e 2f 00 00       jmp    *0x2f9e(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    105a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

Disassembly of section .plt.sec:

0000000000001060 <mystr@plt>:
    1060:       f3 0f 1e fa             endbr64
    1064:       ff 25 5e 2f 00 00       jmp    *0x2f5e(%rip)        # 3fc8 <mystr@Base>
    106a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

0000000000001070 <mystdio@plt>:
    1070:       f3 0f 1e fa             endbr64
    1074:       ff 25 56 2f 00 00       jmp    *0x2f56(%rip)        # 3fd0 <mystdio@Base>
    107a:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

Disassembly of section .text:

0000000000001080 <_start>:
    1080:       f3 0f 1e fa             endbr64
    1084:       31 ed                   xor    %ebp,%ebp
    1086:       49 89 d1                mov    %rdx,%r9
    1089:       5e                      pop    %rsi
    108a:       48 89 e2                mov    %rsp,%rdx
    108d:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
    1091:       50                      push   %rax
    1092:       54                      push   %rsp
    1093:       45 31 c0                xor    %r8d,%r8d
    1096:       31 c9                   xor    %ecx,%ecx
    1098:       48 8d 3d ca 00 00 00    lea    0xca(%rip),%rdi        # 1169 <main>
    109f:       ff 15 33 2f 00 00       call   *0x2f33(%rip)        # 3fd8 <__libc_start_main@GLIBC_2.34>
    10a5:       f4                      hlt
    10a6:       66 2e 0f 1f 84 00 00    cs nopw 0x0(%rax,%rax,1)
    10ad:       00 00 00 

00000000000010b0 <deregister_tm_clones>:
    10b0:       48 8d 3d 59 2f 00 00    lea    0x2f59(%rip),%rdi        # 4010 <__TMC_END__>
    10b7:       48 8d 05 52 2f 00 00    lea    0x2f52(%rip),%rax        # 4010 <__TMC_END__>
    10be:       48 39 f8                cmp    %rdi,%rax
    10c1:       74 15                   je     10d8 <deregister_tm_clones+0x28>
    10c3:       48 8b 05 16 2f 00 00    mov    0x2f16(%rip),%rax        # 3fe0 <_ITM_deregisterTMCloneTable@Base>
    10ca:       48 85 c0                test   %rax,%rax
    10cd:       74 09                   je     10d8 <deregister_tm_clones+0x28>
    10cf:       ff e0                   jmp    *%rax
    10d1:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)
    10d8:       c3                      ret
    10d9:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

00000000000010e0 <register_tm_clones>:
    10e0:       48 8d 3d 29 2f 00 00    lea    0x2f29(%rip),%rdi        # 4010 <__TMC_END__>
    10e7:       48 8d 35 22 2f 00 00    lea    0x2f22(%rip),%rsi        # 4010 <__TMC_END__>
    10ee:       48 29 fe                sub    %rdi,%rsi
    10f1:       48 89 f0                mov    %rsi,%rax
    10f4:       48 c1 ee 3f             shr    $0x3f,%rsi
    10f8:       48 c1 f8 03             sar    $0x3,%rax
    10fc:       48 01 c6                add    %rax,%rsi
    10ff:       48 d1 fe                sar    $1,%rsi
    1102:       74 14                   je     1118 <register_tm_clones+0x38>
    1104:       48 8b 05 e5 2e 00 00    mov    0x2ee5(%rip),%rax        # 3ff0 <_ITM_registerTMCloneTable@Base>
    110b:       48 85 c0                test   %rax,%rax
    110e:       74 08                   je     1118 <register_tm_clones+0x38>
    1110:       ff e0                   jmp    *%rax
    1112:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
    1118:       c3                      ret
    1119:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001120 <__do_global_dtors_aux>:
    1120:       f3 0f 1e fa             endbr64
    1124:       80 3d e5 2e 00 00 00    cmpb   $0x0,0x2ee5(%rip)        # 4010 <__TMC_END__>
    112b:       75 2b                   jne    1158 <__do_global_dtors_aux+0x38>
    112d:       55                      push   %rbp
    112e:       48 83 3d c2 2e 00 00    cmpq   $0x0,0x2ec2(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    1135:       00 
    1136:       48 89 e5                mov    %rsp,%rbp
    1139:       74 0c                   je     1147 <__do_global_dtors_aux+0x27>
    113b:       48 8b 3d c6 2e 00 00    mov    0x2ec6(%rip),%rdi        # 4008 <__dso_handle>
    1142:       e8 09 ff ff ff          call   1050 <__cxa_finalize@plt>
    1147:       e8 64 ff ff ff          call   10b0 <deregister_tm_clones>
    114c:       c6 05 bd 2e 00 00 01    movb   $0x1,0x2ebd(%rip)        # 4010 <__TMC_END__>
    1153:       5d                      pop    %rbp
    1154:       c3                      ret
    1155:       0f 1f 00                nopl   (%rax)
    1158:       c3                      ret
    1159:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

0000000000001160 <frame_dummy>:
    1160:       f3 0f 1e fa             endbr64
    1164:       e9 77 ff ff ff          jmp    10e0 <register_tm_clones>

0000000000001169 <main>:
    1169:       f3 0f 1e fa             endbr64
    116d:       55                      push   %rbp
    116e:       48 89 e5                mov    %rsp,%rbp
    1171:       b8 00 00 00 00          mov    $0x0,%eax
    1176:       e8 f5 fe ff ff          call   1070 <mystdio@plt>
    117b:       b8 00 00 00 00          mov    $0x0,%eax
    1180:       e8 db fe ff ff          call   1060 <mystr@plt>
    1185:       b8 00 00 00 00          mov    $0x0,%eax
    118a:       5d                      pop    %rbp
    118b:       c3                      ret

Disassembly of section .fini:

000000000000118c <_fini>:
    118c:       f3 0f 1e fa             endbr64
    1190:       48 83 ec 08             sub    $0x8,%rsp
    1194:       48 83 c4 08             add    $0x8,%rsp
    1198:       c3                      ret
相关推荐
tmacfrank7 小时前
Binder 预备知识
linux·运维·binder
cnstartech7 小时前
esxi-vmware 虚拟机互相打开
linux·运维·服务器
mcdx8 小时前
bootm的镜像加载地址与uImage镜像的加载地址、入口地址之间的关系
linux
不知疲倦的仄仄8 小时前
第四天:Netty 核心原理深度解析&EventLoop、Future/Promise 与 Pipeline
linux·服务器·网络
橘颂TA8 小时前
【Linux 网络编程】网络是怎么 “跑” 起来的?从协议入门到 TCP/ IP 模型的底层逻辑
linux·运维·服务器·网络
晴虹8 小时前
lecen:一个更好的开源可视化系统搭建项目--数据、请求、寄连对象使用--全低代码|所见即所得|利用可视化设计器构建你的应用系统-做一个懂你的人
前端·后端·低代码
looking_for__8 小时前
【Linux】进程间通信
linux
oMcLin8 小时前
如何在 Oracle Linux 8.3 上通过配置 LVM 与 RAID 结合,提升存储系统的性能与数据冗余性
linux·数据库·oracle
码农小卡拉8 小时前
深度解析 Spring Boot 启动运行机制
java·spring boot·后端