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

库的认识和制作

目录

库的概念

库的基本原理

库的认识

库的分类

静态库

(1)静态库原理

(2)静态库的打包

(2)静态库的使用

(3)总结

动态库

(1)动态库的打包

(3)动态库的使用

动静态库的选择

ELF格式

ELF Header

理解ELF Header

如何看待文件位置

Section

Program header table 

总结

理解链接与加载

静态链接

结论

动态库链接


库的概念

库是写好的现成的,成熟的,可以复用的代码,现实中每个程序都要依赖基础的底层库,不可能每个人的代码都是从零开始的(指定路径下的文件)

库的基本原理

将一个文件最终形成头文件需要经历以下四个阶段

  • 预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成xxx.i文件。
  • 编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成xxx.s文件。
  • 汇编: 将汇编指令转换成二进制指令,最终形成xxx.o文件。
  • 链接: 将生成的各个xxx.o文件进行链接,最终形成可执行程序。

有一些文件我们在编译的时候是非常常用的,如果都经历这种阶段,太过于麻烦,我们就可以将他们的目标文件(.o)形成一个库,如果需要使用这些文件就可以一起进行最后的链接

库的认识

 例:

这份代码我们可以通过printf将代码从helloworld打印到显示器上,本质是因为我们在生成可执行文件的时候将C标准库也链接过来了

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007c01ae800000)libc.so.6 是 C 标准库(包含 malloc、printf 等基础函数),这里显示它的实际路径是 /lib/x86_64-linux-gnu/libc.so.6,表明程序依赖于该 C 库。

  • 在Linux当中,以.so为后缀的是动态库,以.a为后缀的是静态库。
  • 在Windows当中,以.dll为后缀的是动态库,以.lib为后缀的是静态库

当我们去掉一个动静态库的前缀lib,再去掉后缀.so或者.a及其后面的版本号,剩下的就是这个库的名字。

库的分类

静态库  动态库

静态库

本质对库源代码形成的obj进行打包

  • 在Linux当中,以.so为后缀的是动态库,以.a为后缀的是静态库。
  • 在Windows当中,以.dll为后缀的是动态库,以.lib为后缀的是静态库
(1)静态库原理

静态库是程序在编译链接的时候把库的代码复制到可执行文件当中的,生成的可执行程序在运行的时候将不再需要静态库,因此使用静态库生成的可执行程序的大小一般比较大。

(2)静态库的打包

add.h

#pragma once
extern int add(int x, int y);

add .c

#include "add.h"
int add(int x, int y)
{return x + y;
}

sub.h

#pragma once
extern int sun(int x, int y);

sub.c

#include "sub.h"
int sub(int x, int y)
{return x-y;
}

第一步:生成对应的目标文件(.o)

第二步:使用ar将目标文件打包成动态库

ar : 将目标文件打包成静态库

-r (replace ) : 若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件。

-c (creat):  建立静态文件

-t : 列出静态库中的文件

-v:显示详细的信息

第三步:将头文件和生成的静态库组织起来

我们将库给他人的时候只需要给两个文件夹(一个文件夹放着头文件,一个文件夹中放着库文件)

该行为也可以让Makefile做

(2)静态库的使用
  • 如果使用的不是标准库,而是自己形成的库,需要我们指明库的名称和那个路径下(gcc/g++ 编译器找库,一般会在默认的路径下查找,所谓的库的安装就是把库文件拷贝到系统默认的路径下/ lib64)

为什么编译c语言的时候不需要指明库的名称

  • 因为gcc是专门用来编译C语言的,默认会认识(c语言)所以当我们使用第三方库的时候必须要使用-l来表明库的名称
  • 我们知道当我们使用系统路径下的头文件的时候,我们一般用<>,不是系统路径下的头文件我们一般使用“”。如果我们也想使用<>, -I(大i)路径  指定一个搜索路径,表明头文件搜索路径

(3)总结

         库 == 头文件 + 库文件

           库使用需要搜索,1、头文件(-I(大i))2、库路径(-L)3、库是谁(-l)

           库如果不行使用上方的选项,可以将头文件(/usr/include)和库文件(/lib64)拷贝到系统             指定默认的路径下

动态库

(1)动态库的打包

第一步:形成对应的目标文件

fPIC : 产生位置无关码

第二步:使用-shared将所有目标文件打包成动态库

shared : 表示生成共享库格式

第三步:将头文件和生成动态库 组织起来

(3)动态库的使用

使用第三方库:1、头文件(-I(大i))2、库路径(-L)3、库是谁(-l)

静态库和动态库的不同

当我们使用上述的方式运行动态库的时候,我们会发现运行的时候操作系统找不到动态库 ,因为我们是告诉编译器我们的库在哪里但是一旦我们形成可执行程序之后,和编译器就没有关系了,所以操作系统会报错

而静态库会将库文件拷贝到可执行程序中,所以不需要操作系统寻找

解决方法:

拷贝到.so文件到系统共享库路径下(/lib64)

 sudo cp mlib/lib/libcal.so /lib64

软连接 (ln  当前的动态库,/lib64/libmyc.so) 

 LD_LIBRARY_PATH(导入环境变量)   


export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:路径

将动态库,查找路径,全局有效

动静态库的选择

  • 动态库和静态库同时存在的时候默认使用动态库
  • 一个可执行程序,可能会依赖多个库,如果我们只提供静态库,即使我们是动态链接,gcc也只能使用静态链接
  • 如果我们在使用的尾部加上-static ,我们必须使用静态链接,如果没有静态库就会报错(注意:大部分的系统值给我们安装了C/C++的动态库,如果我们使用-static会报错必须手动安装静态库

ELF格式

.o .a .so .exe 都是一种ELF格式

如下:二进制文件有自己的格式

文件 = 属性 + 内容,内容分成ELF格式的四部分

如下图:二进制文件格式是ELF

ELF Header

作用

ELF Header: 包含ELF文件的基本信息 比如文件类型,机器类型,入口地址

查看

readelf - h 文件名

理解ELF Header
  • ELF Header本质是一个结构体,编译代码的时候在文件中构建一个ELF Header结构体,将属性写入结构体,然后将二进制的文件结构体写入文件开头处
  • 操作系统和编译器都认识ELF Header
如何看待文件位置
  • 磁盘文件,就是一个一维数组,无论是二进制还是文本文件
  • ELF文件内容就是一个一维数组,char arr[N]   
  • 标识每一个ELF任意一个片段都需要 起始偏移量 和 该片段大小

Section

Section 

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

Section Header Table 

显示ELF文件的节点信息,接地点描述了ELF文件的各个节的起始地址、大小、标志

查看节点信息

readelf -S -文件名

Program header table 

列举所有效的和他们的属性,表里记着每个段的开始位置和位移、长度(其实是一个合并成segment的方法表,那些应该合并,并不会记录合并完的,合并是在加载的时候完成的,给操作系统给看的

段:文件系统和磁盘进行IO的时候是以4KB为单位的,但是section不一定是4KB的,且多个section可能具有相同的属性所以我们需要多个section以4KB进行合并形成数据段segment,所以ELF加载到内存的时候会被OS自动和合并成多个segment,然后加载到内存中

查看: readelf -l myexe

总结

一个ELF文件,要有两种视角

1、编译器视角:section

2、OS视角:Segment

ELF形成可执行

(1)将多份C/C++源代码,翻译成为目标.o文件+动静态库(ELF)

 (2)将多份.o文件section进行合并

理解链接与加载

静态链接

  • 无论是自己的.o,还是静态库的.o,本质都是把.o进行链接的过程 
  • 所以研究静态链接,本质就是研究.o是如何链接的
  • objdump 代码段进行反汇编

  • call表示调用函数 因为在没有链接之前并不是到函数的位置所以地址为全零

  • 所以链接的时候要把调用的函数地址,从0重定位到最终函数的地址,所以我们把.o叫做可重定位目标文件
  • 为了让链接器将来在链接时能够正确定位到这些被修正的地址,在代码块(.data)中还存在⼀个重定位表,这张表将来在链接的时候,就会根据表⾥记录的地址将其修正。
     
  • 所以我们程序链接静态库和.o文件链接叫做地址重定位

readelsf -s 文件名:查看文件表

myexe:

两个.o链接之后,得到地址

结论

1、两个.o代码段合并在了一起,并进行了统一的编址

2、链接的时候,会修改.o中没有确定的函数地址,在合并完成之后,进行相关的call地址,完成          代 码调用(链接是的地址重定位)

静态链接的过程

一个ELF可执行程序,再没有加载到内存的时候,地址是什么地址

  • Linux系统编译形成可执行文件,需要对代码和数据进行编址,当代计算机和操作系统,对ELF编址的时候,采用的都是平坦模式(000000..0000~ ffff..ffff)
  • 所以函数的本质,就是相邻地址的集合
  • 老计算机编地址:段地址+偏移量
  • 平坦模式:段起始地址(起始地址为0,想象C语言只有一个段) + 偏移量(逻辑地址)

  • 其实上述的线性编址得到的地址,就是我们之前讲的虚拟地址
  • 磁盘可执行文件,起始地址+偏移量的这种地址,叫做逻辑地址
  • 平坦模式,起始地址是从0开始的
  • 所以虚拟地址 == 逻辑地址

进程mm_struct、vm_area_struct在进程刚刚创建的时候,初始化数据从哪⾥来的

从ELF各个segment来,每个segment有⾃⼰的起始地址和⾃⼰的⻓度,⽤来初始化内核结构中的[start, end]等范围数据,另外在⽤详细地址,填充⻚表.

  • CPU指向的时候会将Entry point address 移到EIP中,执行下一步指令的时候使用MMU利用CR3利用EIP的虚拟地址找到物理地址,等到具体的指令,拿到CPU中如果人仍然是虚拟地址重复上述操作

动态库链接

进程如何看到动态库的

程序的运行

结论:让我们自己的程序跑起来,除了加载自身的程序,还有程序依赖的库

动态库加载到内存会映射到共享区中,进程是通过自己的虚拟地址空间中的共享区看到的

多进程如何看待动态库

  • 每一个进程都要把库映射到自己的地址空间中
  • 动态库的本质:通过地址空间映射,对公共代码进行去重

加载时地址重定向

如何映射

如何进行库函数调用

  • 编译形成动态库的时候,每个函数的偏移量已经有了
  • 我们形成我们的程序进行链接的时候,库名称保留,修改我们自己程序中形成库名字加偏移量的形式
  • 在映射之后得到虚拟地址,将库名字替换为虚拟地址,得到起始地址加偏移量
  • 库函数调用,也是在进程的虚拟地址范围内调用的
  • 动态库被映射到进程的任意位置(一般是共享区)我们的进程都能调用
  • 每一个进程中都会把动态库映射到地址空间,虽然起始地址可能不同但是不妨碍任意进程访问库函数
  • 动态链接实际上将链接的整个过程推迟到加载的时候
  • 在C++/C程序中我们当程序开始执行的时候,它首先并不会跳转到main函数,程序的入口地址点是_start,_start是库提供的,所以必须首先将这个库先加载到内存
  • _start:设置一个堆栈环境,初始化数据端 动态链接(_start函数会调用动态链接器的代码来解析和加载程序所依赖的动态库,动态链接器会处理所有的符号解析和重定位)
  • 所以上述操作是操作系统和动态链接器做的

为什么代码区会被改了

  • 在数据区开辟了一片区域,形成.got表里面存着所有代码中所需到的库的偏移量和库的名字
  • 链接的时候将偏移量写入.got
  • 所以代码区变成了call.got地址+.got中的偏移量,所以修改时代码区不用动,只动数据区

http://www.dtcms.com/a/532351.html

相关文章:

  • 在线做英语题的网站html5手机网站返回顶部
  • 开源项目详解3-ParserSpel
  • 系统性学习C++-第八讲-vector类
  • 什么是Nginx?:掌握高性能 Web 服务器核心技术
  • 江西网站开发公司模板网婚纱
  • 快速搭建Docker私有仓库指南
  • 网站禁用右键wordpress mycred汉化
  • 音视频处理(二): 一文讲清楚音频处理流程:采样、压缩和播放
  • 基于单片机的篮球比赛计时与比分控制系统设计
  • C++容器set
  • 网站建设主机耗电量怎么写代码自己制作网站
  • 超越低功耗:TMS320C6000 DSP的能效架构设计与IoT节点部署实践
  • 西安网站开发工资首都之窗门户网站首页
  • 中药电商平台是什么?主要具有哪些创新特征与应用场景?
  • Python模块(Module)详解:从基础使用到工程化实践
  • DTD 属性详解
  • 随身WiFi助手
  • 安卓网络请求详解:Retrofit + OkHttp 高效通信方案
  • centos建设网站营销系统平台
  • 华为OD机试双机位A卷 - 统计差异值大于相似值二元组个数 (C++ Python JAVA JS GO)
  • bug:realsense-viewer 找不到已识别的设备
  • Mac安装VisualVM 2.2启动闪退
  • 在macOS上搭建C#集成开发环境指南
  • 郑州市城乡建设规划网站苏州园区两学一做网站
  • 音乐网站 模板手游app平台排行榜
  • vue通信加密解密完整方案实现
  • 大模型模板输出与优化技术指南
  • 2026蓝桥杯
  • 让我用一个非常通俗易懂的方式来解
  • 搞一个卖东西的网站怎么做婚庆网站开发计划书