当前位置: 首页 > news >正文

动静态库链接生成和使用以及认识ELF文件

目录

  • 1.什么是库?
  • 2.静态库
    • 2.1 静态库生成
    • 2.2 静态库的使用
  • 3.动态库
    • 3.1 生成动态库
    • 3.2 动态库使用
  • 4.结论
  • 5.ELF文件
  • 6.ELF从形成到加载
    • 6.1 ELF形成可执行
    • 6.2 ELF可执行文件加载

1.什么是库?

  • 库是写好的现有的,成熟的,可以复用的代码。
  • 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行
  • 库有两种:
    • 静态库:.a [linux]、.lib [windows]
    • 动态库:.so [linux]、.dll [windows]

2.静态库

  • 静态库(.a) :程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库

  • 特点:

    • 体积较大:因为静态库的代码被直接嵌入到可执行文件中
    • 更新困难:如果静态库中的代码需要更新,必须重新编译所有使用该库的程序
  • 一个可执行程序可能用到许多的库,这些库运行有的是静态库,有的是动态库,而我们的编译默认为动态链接库,只有在该库下找不到动态.so的时候才会采用同名静态库。我们也可以使用gcc-static强转设置链接静态库

  • 静态库,本质是一种归档文件,不需要使用者解包,而是gcc/g++直接进行链接

2.1 静态库生成

ar -rc libmyc.a *.o       #将所有的.o文件一起打包成静态库
  • 其中生成的静态库的名字是myc
  • ar:在linux 系统中,ar是生成静态库的标准工具
  • -rc参数:是ar命令中常用的选项组合,用于创建和更新静态库文件
    • r(replace):表示替换或添加目标文件到归档文件中。如果归档文件中已经存在同名的目标文件,则会被新文件替换
    • c(create):表示创建一个新的归档文件。如果归档文件已经存在,c选项会覆盖它
$ ar -tv mylib/lib/libmyc.a
rw-r--r-- 0/0   1408 Jan  1 08:00 1970 my_strlen.o
rw-r--r-- 0/0   1560 Jan  1 08:00 1970 my_print.o
  • t:列出静态库中的文件
  • v:详细信息

2.2 静态库的使用

# 场景1:头文件和库文件和我们自己的源文件在同一路径下
gcc main.c  -L.  -lmyc
#场景2:头文件和库文件有自己的独立路径
gcc  main.c  -I头文件路径  -L库文件路径  -lmyc
  • -L:指定库路径
  • -I:指定头文件搜素路径
  • -l:指定库名

3.动态库

  • 动态库(.so)是一种包含可重用代码和数据的文件,它在程序运行时被加载到内存中,多个程序共享使用库
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。
  • 操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用。

3.1 生成动态库

$ gcc -fPIC -c main.c -o main.o
$ gcc -shared -o libmyc.so main.o
  • -shared:用于指定生成动态链接库(共享库)
  • -fPIC:用于生成与位置无关代码,确保动态库可以在运营时被加载到任意内存位置

3.2 动态库使用

#场景1:头文件和库文件和我们自己的源文件在同一路径下
$ gcc main.c -L. -lmyc
#场景2:头文件和库文件有自己独立路径
$ gcc main.c -I头文件路径 -L库文件路径 -lmyc

问题:

$ ldd a.out
        linux-vdso.so.1 (0x00007ffd48fd6000)
        libmyc.so => not found
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbfd8b43000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fbfd8d44000)

解决方案:

  • 拷贝.so文件到系统共享库路径下,一般是/usr/bin/lib64
$ sudo cp libmyc.so /lib64
  • 向系统共享库路径下建立同名软链接
$ sudo ln -fs /home/tc/113/linux/0221/zhangsan/mylib/lib/libmyc.so /lib64/libmyc.so
  • 更改环境变量:LD_LIBRARY_PATH
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:动态库路径
  • 配置/etc/ld.so.conf.d/
$ sudo touch /etc/ld.so.conf.d/XXX.conf 
$ sudo nano /etc/ld.so.conf.d/XXX.conf
#向XXX.conf文件中添加动态库的路径
$ sudo ldconfig

4.结论

  • gcc/g++默认使用动态库
  • 如果非得静态链接,只能-static一旦-static,就必须存在对应的静态库
  • 只存在静态库,可执行程序,对于该库,只能静态链接了
  • linux系统下,默认安装的大部分库,默认都优先安装的是动态库!
  • 库:应用程序=1:n
  • 动静态库对比
特性动态库静态库
链接时机运行时链接编译时来链接
可执行文件大小较小(不包含代码)较大(包含代码)
内存占用多个程序共享内存每个程序独立占用内存
更新和维护更新库文件即可,不需要重新编译程序需要重新编译所有的使用该库的程序
运行时依赖需要动态库文件存在无运行时依赖
适用场景需要频繁更新、插件系统、节省内存嵌入式、运行时独立、安全性要求高

在这里插入图片描述

目录

  • 1.什么是库?
  • 2.静态库
    • 2.1 静态库生成
    • 2.2 静态库的使用
  • 3.动态库
    • 3.1 生成动态库
    • 3.2 动态库使用
  • 4.结论
  • 5.ELF文件
  • 6.ELF从形成到加载
    • 6.1 ELF形成可执行
    • 6.2 ELF可执行文件加载

5.ELF文件

  • 可重定位文件(xxx.o文件):包含适合于与其他目标文件链接来创建执行文件或者共享目标文件的代码和数据
  • 可执行文件:即可执行程序
  • 共享目标文件:即xxx.so文件

一个ELF文件由以下四部分组成:

  • ELF头(ELF header):描述文件的主要特性。其位于文件的开始位置,它的主要目的是定位文件的其他部分。包含了文件的基本信息,如文件类型、架构、入口点地址、程序头表和节头表的位置等
  • 程序头表(Program header table):列举了所有有效的段(segments)和它们的属性。
  • 节头表(Section header table):包含对节的描述
  • 节(Section):ELF文件中的基本组成单位,包含了特定类型的数据。ELF文件的各种信息和数据都存储在不同的节中
  • 常见的节:
    • 代码节(.text):用于保存机器指令,是程序的主要执行部分
    • 数据节(.data):保存已初始化的全局变量和局部静态变量

在这里插入图片描述

6.ELF从形成到加载

6.1 ELF形成可执行

  • 将多份C/C++源代码,翻译成为目标.o文件
  • 将多份.o文件section进行合并

在这里插入图片描述

6.2 ELF可执行文件加载

  • 一个ELF会有多种不同的section,在加载到内存的时候,也会进行section合并,形成segment
  • 合并原则:相同属性,比如:可读、可写等
#查看可执行程序的section
$ readelf -S a.out
There are 31 section headers, starting at offset 0x3978:

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.propert NOTE             0000000000000338  00000338
       0000000000000020  0000000000000000   A       0     0     8
  [ 3] .note.gnu.build-i NOTE             0000000000000358  00000358
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000037c  0000037c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003a0  000003a0
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003c8  000003c8
       00000000000000a8  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000470  00000470
       0000000000000082  0000000000000000   A       0     0     1
	...

#查看section合并的segment
$ readelf -l a.out

Elf file type is DYN (Shared object file)
Entry point 0x1060
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
                 0x00000000000005f8 0x00000000000005f8  R      0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x00000000000001f5 0x00000000000001f5  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x0000000000000160 0x0000000000000160  R      0x1000
  LOAD           0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
                 0x0000000000000258 0x0000000000000260  RW     0x1000
  DYNAMIC        0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc8
                 0x00000000000001f0 0x00000000000001f0  RW     0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000020 0x0000000000000020  R      0x8
  NOTE           0x0000000000000358 0x0000000000000358 0x0000000000000358
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000020 0x0000000000000020  R      0x8
  GNU_EH_FRAME   0x0000000000002014 0x0000000000002014 0x0000000000002014
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
                 0x0000000000000248 0x0000000000000248  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 

为什么要将section合并成为segment??

  • 减少页面碎片,提高内存使用效率。 比如,假设页面大小为4096字节,如果.text部分为4097字节,.init部分为512字节,那么它们将占用3个页面,而合并后,它们只需2个页面

程序头表和节头表又有什么用呢??

  • 程序头表:主要用于操作系统将程序从磁盘加载到内存
  • 节头表:主要在链接时起作用

在这里插入图片描述
从链接视图来看:

  • 命令 readelf -S code.c可以帮助查看ELF文件的节头表
  • .text节:是保存了程序代码指令的代码节
  • .data节:保存了初始化的全局变量和局部静态变量等数据
  • .rodata节:保存了只读的数据
  • .bss节:为未初始化的全局变量和局部静态变量预留位置
  • .symtab节:Symbol Table符号表,就是源码里面那些函数名、变量名和代码的对应关系
  • .got.plt节.got节保存了全局偏移量。.got节和.plt节一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时进行修改

从执行视图来看:

  • 告诉OS哪些模块可以被加载到内存
  • 加载进内存之后哪些分段是可读可写,哪些分段是只读

在这里插入图片描述

相关文章:

  • FPGA 实验报告:四位全加器与三八译码器仿真实现
  • 【NLP 31、文本匹配任务 —— 深度学习】
  • 跟着 Lua 5.1 官方参考文档学习 Lua (11)
  • 线性代数之矩阵特征值与特征向量的数值求解方法
  • 2022年《申论》第二题(河北A卷)
  • Java高频面试之集合-07
  • 剖析Manus:AI领域的创新先锋还是虚假泡沫?
  • 【leetcode hot 100 141】环形链表
  • 解锁AIGC新时代:通义万相2.1与蓝耘智算平台的完美结合引领AI内容生成革命
  • 掌握Linux基础:从Shell提示符到文件管理的全面指南
  • JavaWeb-Servlet6 入门
  • 低纹波高效率DCDC电源芯片ASP4644技术解析
  • 行为模式---责任链模式
  • window 11亲测NodeMCU-8266 开发板+Micropython点灯实验
  • Feign 核心规则与最佳实践:避免入坑指南
  • 正向代理与反向代理
  • Redis- 哨兵
  • Restful 接口设计规范
  • springcloud sentinel教程
  • Amazon Trust Services证书体系解析与应用实战指南
  • 用爱奇艺会员做视频网站违法吗/开发一个app平台大概需要多少钱?
  • 163企业邮箱官网入口/武汉seo公司排名
  • 上海网站建设v芯ee8888e/手机端竞价恶意点击能防止吗
  • 视频做网站/如何建一个自己的网站
  • 网站模版化配置/2345网址导航下载桌面
  • 装饰公司网站源码下载/拓客最有效方案