静态链接原理以及过程

2023-11-17

通常程序的编译中,或多或少会调用其它库中的函数接口,本篇blog就是讲静态库的调用流程。通常我们知道编译一个可执行程序会有这四个过程:预处理、编译、汇编以及链接。前面三步就是产生目标文件.o的过程,链接就是把各个.o文件粘在一起,构成一个可执行文件。而链接主要分为两步:第一是空间和地址的分配,第二是符号解析与重定位

1.空间和地址的分配

每个.o文件都有自己的段属性,比如.text、.data等等这些,链接的第一步就是将这些段属性合并在一起。下面将以a.c和b.c为示范说明链接中空间和地址的分配。

/*a.c*/
extern int shared;

int main(void)
{
	int a = 100;
	swap(&a, &shared);
	
	return 0;
}
/*b.c*/
int shared = 1;

void swap(int* a, int* b)
{
	*a ^= *b ^= *a ^= *b;
}

1.1 相似段的合并

使用objdump -h 来查看每个.o文件的段属性,并且使用链接器ld将a.o和b.o合并成可执行文件ab:ld a.o b.o -e main -o ab,采用相似段合并的方法,这里主要看.text和.data的段,.text一般是程序代码,.data是数据存放。从下面可以看出:.text ab(size 0x66)=a.o(size 0x2c) + b.o(size 0x3a)           .data ab(size 0x4)=a.o(size 0x0) + b.o(size 0x4)


a.o:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000002c  00000000  00000000  00000034  2**0
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000000  00000000  00000000  00000060  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00000000  00000000  00000060  2**0
                  ALLOC
  3 .comment      0000002c  00000000  00000000  00000060  2**0
                  CONTENTS, READONLY
  4 .note.GNU-stack 00000000  00000000  00000000  0000008c  2**0
                  CONTENTS, READONLY
  5 .eh_frame     00000038  00000000  00000000  0000008c  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

b.o:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000003a  00000000  00000000  00000034  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000004  00000000  00000000  00000070  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00000000  00000000  00000074  2**0
                  ALLOC
  3 .comment      0000002c  00000000  00000000  00000074  2**0
                  CONTENTS, READONLY
  4 .note.GNU-stack 00000000  00000000  00000000  000000a0  2**0
                  CONTENTS, READONLY
  5 .eh_frame     0000003c  00000000  00000000  000000a0  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

ab:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00000066  08048094  08048094  00000094  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .eh_frame     0000005c  080480fc  080480fc  000000fc  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .data         00000004  08049158  08049158  00000158  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  3 .comment      0000002b  00000000  00000000  0000015c  2**0
                  CONTENTS, READONLY

1.2 符号地址的确定

在上面的段属性合并之后,那么符号地址也将差不多确定好了,也就是上面的VMA(Virtual Memory Address)。这里为啥可以将main函数和swap的地址区分开来了?这是因为编译中以main为程序的执行入口,所以main的地址比swap要先,又根据前面目标文件中的函数的大小,可以将main和swap的地址确定下来。

符号 类型 虚拟地址
main 函数 0x08048094
swap 函数 0x080480c0
shared 变量 0x08048158

2.符号解析与重定位

2.1重定位

前面知道a.c中引用了b.c中的函数,那么肯定在链接过程中需要将a.o中的swap和shared重定位的。先看a.o文件的反汇编objdump -d a.o ,可以看出在11处,使用shared的变量,在20处引用外部函数swap。本来是想shared的值赋值到寄存器%esp中,但是不知道shared的地址,所以使用0x00000000来代替。同理,在20处调用swap,可以看出e8 fc ff ff ff,其中e8是操作码,是一个近址相对位移调用指令,也就是下一条指令的地址0x25+0xfffffffc(-4)=0x21。

在ab中可以看出,这两处的地址都被修正了,重定位到相应的地址上去了。

a.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
   0:	55                   	push   %ebp
   1:	89 e5                	mov    %esp,%ebp
   3:	83 e4 f0             	and    $0xfffffff0,%esp
   6:	83 ec 20             	sub    $0x20,%esp
   9:	c7 44 24 1c 64 00 00 	movl   $0x64,0x1c(%esp)
  10:	00 
  11:	c7 44 24 04 00 00 00 	movl   $0x0,0x4(%esp)
  18:	00 
  19:	8d 44 24 1c          	lea    0x1c(%esp),%eax
  1d:	89 04 24             	mov    %eax,(%esp)
  20:	e8 fc ff ff ff       	call   21 <main+0x21>
  25:	b8 00 00 00 00       	mov    $0x0,%eax
  2a:	c9                   
ab:     file format elf32-i386


Disassembly of section .text:

08048094 <main>:
 8048094:	55                   	push   %ebp
 8048095:	89 e5                	mov    %esp,%ebp
 8048097:	83 e4 f0             	and    $0xfffffff0,%esp
 804809a:	83 ec 20             	sub    $0x20,%esp
 804809d:	c7 44 24 1c 64 00 00 	movl   $0x64,0x1c(%esp)
 80480a4:	00 
 80480a5:	c7 44 24 04 58 91 04 	movl   $0x8049158,0x4(%esp)
 80480ac:	08 
 80480ad:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 80480b1:	89 04 24             	mov    %eax,(%esp)
 80480b4:	e8 07 00 00 00       	call   80480c0 <swap>
 80480b9:	b8 00 00 00 00       	mov    $0x0,%eax
 80480be:	c9                   	leave  
 80480bf:	c3                   	ret   

2.2 重定位表

那么在合并ab时候,怎么会知道那个文件中需要重定位了?这个时候,需要重定位的文件中会有重定位表,使用如下的命令来查看重定位表:objdump -r a.o。其中R_386_32是表示绝对地址修正,R_386_PC32是表示相对地址修正。

a.o:     file format elf32-i386

RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE 
00000015 R_386_32          shared
00000021 R_386_PC32        swap

其实我们也可以通过符号表来查看哪些函数或者变量在本文件中没有定义:readelf -s a.o,比如奥看下表就知道其中的shared和swap没有在a.o中定义,需要重定。位到其他的文件中

Symbol table '.symtab' contains 11 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS a.c
     2: 00000000     0 SECTION LOCAL  DEFAULT    1 
     3: 00000000     0 SECTION LOCAL  DEFAULT    3 
     4: 00000000     0 SECTION LOCAL  DEFAULT    4 
     5: 00000000     0 SECTION LOCAL  DEFAULT    6 
     6: 00000000     0 SECTION LOCAL  DEFAULT    7 
     7: 00000000     0 SECTION LOCAL  DEFAULT    5 
     8: 00000000    44 FUNC    GLOBAL DEFAULT    1 main
     9: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND shared
    10: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND swap

3.链接脚本

一般程序的运行时在操作系统中运行,那么可以使用编译器中默认的链接脚本,一般放在/usr/lib/ldscripts/下。但是想kernel、BootLoader等这些程序怎么办,因为他们本身就是操作系统,所在这些程序的编译需要指定链接脚本,当然,自己编译一些程序的时候,也可以不使用编译器提供的链接脚本,而使用自己的,使用如下的命令:ld  -T  link.script

比如:下面的一个简单的链接脚本,其中ENTRY是表示程序的入口是main函数,后面的SECTIONS命令一般是链接脚本的主体,这个命令指定了各种输入段到输出段的变换,SECTIONS后面紧跟一对大括号,里面包括了SECTIONS的变换规则,其中有三条语句,每条语句一行。第一条是赋值语句,确定当前虚拟地址;第二条是转换规则,将所有输入文件中的名字为.text、.data、.rodata都合并到输出文件的tinytext中;第三条规则是将输入文件中.comment的段都丢弃,不保存到输出文件中。

ENTRY(main)

SECTIONS
{
    . = 0x0804800 + SIZEOF_HEADERS;

    tinytext : { *(.text) *(.data) *(.rodate) }
    
    /DISCARD/ : { *(.comment) }
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

静态链接原理以及过程 的相关文章

  • JUnit->Mockito->PowerMock->持续更新

    最近在公司做需求 要求开发需要有相应的单元测试代码 第一次做单测相关的知识 就在这做一篇总结 一 JUnit JUnit是Java最基础的测试框架 主要的作用就是断言 方法名 方法描述 assertEquals 断言传入的预期值与实际值是相
  • 求救,在频域分析语音信号谐波成分的方法有哪些

    求救 在频域分析语音信号谐波成分的方法有哪些 有一段语音信号 经过FFT之后变换到频域 目前想在频域分析其谐波成分 并找到谐波能量最大的K次谐波 matlab里可以用仿真powergui生成仿真的信号 然后FFT分析得到各谐波成分及能量 但
  • vi命令修改文件及保存的使用方法

    简单点 vi文件名 按 I 进入insert模式 可以正常文本编辑 编辑好之后按 esc 退出到 命令模式 再按 shift 进入 底行模式 按 wq 保存退出 还一种 把文件复制到本地修改好上传上去 vi编辑器是所有Unix及Linux系
  • 每日一练:用java打印水仙花数

    需求 在控制台输出所有的 水仙花数 解释 什么是水仙花数 水仙花数 指的是一个三位数 个位 十位 百位的数字立方和等于原数 例如153 3 3 3 5 5 5 1 1 1 153 思路 获取所有的三位数 准备进行筛选 最小的三位数为100
  • cp1.php969.net,eDrawings

    OzsgSFNGIFYxMy4wNSAKSQAAAABCAGDlUL0Spb29AAAAAGXlUD13vp89x0rNPVp42uy9B0AUSdMw3LMzsxHYJeec8 6SdoGVJagoiAFBMAOimNDDhBHMeqen
  • 怎么批量安装服务器的操作系统,批量安装服务器操作系统

    弹性云服务器 ECS 弹性云服务器 Elastic Cloud Server 是一种可随时自助获取 可弹性伸缩的云服务器 帮助用户打造可靠 安全 灵活 高效的应用环境 确保服务持久稳定运行 提升运维效率 三年低至5折 多种配置可选了解详情
  • 数组(持续更新后续)

    目录 数组定义 数组的组成部门 案例一 案例二 案例三 增强for循环 语法结构 执行规律 注意 案例 案例 案例 数组定义 变量 存储数据的空间 装数据的容器 变量中只能存储一个数据 数组 存储数据的空间 装数据的容器 数组中可以存储多个
  • sojson JS 逆向一 (简单版)

    背景 现在市面上很多web网页都是使用sojson加密的 所以 爬虫小伙伴对sojson的学习迫在眉睫 js 加密源码 var a b function w d w info 这是一个一系列js操作 d warning 如果您的JS里嵌套了
  • 202206-3 角色授权

    第三题 题干 角色授权 include
  • svn更新有问题svn: The working copy at' ' is too old

    SVN the working copy needs to be upgraded svn 低版本SVN检出代码 高版本SVN提交不了解决方法如下 项目右键 team Upgrade 即可 如下图 参考URL https blog csdn
  • Python:流动爱心图案

    from turtle import 导入了Python标准库中的turtle模块 并使用通配符 导入了该模块中的所有函数和变量 turtle模块提供了一个绘图窗口和一些绘图函数 可以用来绘制简单的图形 from math import s
  • python-爬虫初识-采集汽车资讯信息案例(一)

    目录 一 什么是爬虫 二 初识爬虫 采集汽车资讯信息 三 requests和BeautifulSoup模块基本使用 requests import requests BeautifulSoup from bs4 import Beautif
  • 数学模型——数学与人类文明的桥梁

    序言 数统治着宇宙 Pythagoras 数学一词在西方源于古希腊语 意思是通过学习获得知识 显然 早期数学涵盖的范围比我们今天要广得多 人类科学发展至今 衍生出生物科学 信息科学 金融学 计算机科学等不胜枚举的领域与分支 而数学模型正是数
  • Word打印或打印预览或另存为PDF时出现“错误!未定义书签!”的解决办法

    出处 http blog sina com cn s blog 5ee0924f0101a05l html 今天在单独打印一份三页的目录Word文档时 所有目录的页码全部变为 错误 未定义书签 很是奇妙 一开始还以为是打印问题 又重新打印了
  • 如何使用Google Compute Engine入门指南快速创建和配置您的云虚拟机实例

    文章目录 步骤1 创建 Google Cloud Platform GCP 账户 步骤2 设置 GCP 项目 步骤3 启用 Google Compute Engine API 步骤4 安装 Google Cloud SDK 步骤5 创建虚拟
  • sql中使用union 或者union all语句时,两边的列的顺序必须保持一致

    sql中使用union 或者union all语句时 两边的列的顺序必须保持一致
  • [HashMap源码学习之路]---hashcode的作用及数组长度为什么是2的n次幂

    HashMap中的hashcode作用 HashMap是Java 中很重要的一个概念 工作中使用的频率也非常广泛 需要对其进行了解 看源码是很枯燥的 但是看懂了 却有种豁然开朗的感觉 觉得特别棒 本篇只说hashcode的作用及数组长度为什
  • Java Thread synchronized同步锁简介说明

    转自 Java Thread synchronized同步锁简介说明 下文笔者讲述Thread synchronized同步锁的简介说明 如下所示 java中 每一个对象只有一个同步锁 同步锁放置在对象头上 当我们调用一个对象的synchr
  • VS2019中搭建QT 5.15.2开发环境

    接触QT VS一年多 对于环境搭建的一些细节已有些遗忘 最近又要重新搭建环境 就以此文章来记录一下搭建的细节 方便日后查询 1 VS2019与QT5 14 2的安装 VS2019的安装链接下载 Visual Studio Tools 免费安

随机推荐

  • 在keil工程中删除编译文件的方法

    在实际工作中coding的工作是一方面 但是作为最终的输出是要提交源码作为成果来管理和上传的 不管是Git也好SVN也好 一般都要求只保存源码和工程文件 针对单片机MCU类的项目 对于Linux下的项目没有工程文件一说 只有配置信息和整个工
  • unity粒子系统

    添加粒子系统 按照GameObject gt Effect gt Particle System顺序 添加粒子系统 效果如下 粒子系统有其独特的组件 Particle System 展开上面的一系列属性中的Particle System 调
  • 机器学习加速器文献整理

    William J Dally 团队 文献一SCNN An Accelerator for Compresse d sparse Convolutional Neural Networks1 文献二EIE Efficient Inferen
  • DirectX9 SDK Samples(12) CustomUI Sample

    这一次的例子是关于DXUT的UI 下面先翻译文档中的比较重要的说明 这个例子开始时定义了两个CDXUTDialog对象 g HUD和g SampleUI 一个CDXUTDialog是一个装入了一个或多个控件 按钮等 的容器 对话框 CDXU
  • 部分HTTPS网站无法访问的可能原因

    最近访问一些HTTPS的网站 总有一些网站无法正常访问 总是提示证书过期 查看了下对应网站的证书 没到期呀 于是总认为是自己系统或者浏览器的问题 可查来查去 改来改去也无法解决问题 直到仔细观察了下证书颁发机构 才发现都是一个机构的 Let
  • java swing 日志_springBoot swing 界面实现配置和日志打印

    packagecom adao simulater swing importcom adao simulater common Constant importcom adao simulater common PropertiesUtil
  • http请求与Request常用方法

    一 http请求 HTTP请求报文由3部分组成 请求行 请求头 请求体 是请求方法 GET和POST是最常见的HTTP方法 除此以外还包括DELETE HEAD OPTIONS PUT TRACE 不过 当前的大多数浏览器只支持GET和PO
  • 安装cnpm(傻瓜式通俗移动)

    1 首先确保自己安装好node并且npm能正常使用 2 以管理员身份打开cmd 3 输入npm install g cnpm registry https registry npm taobao org并运行 4 等待安装结束后 输入 cn
  • PWM调光调色温技术学习(笔记)

    前言 在智能化的浪潮中 智能照明是智能家居中非常重要的一部分 由于LED照明的大量普及 相对于传统的节能灯和白炽灯 LED照明的可塑性强很多 这其中LED灯的亮度调节和色温调节已经成为智能照明的主流需求 本文就从LED照明的亮度调节 色温调
  • [网络安全自学篇] 三十一.文件上传之Upload-labs靶场及CTF题目01-10(四)

    这是作者的系列网络安全自学教程 主要是关于安全工具和实践操作的在线笔记 特分享出来与博友们学习 希望您们喜欢 一起进步 前文分享了编辑器漏洞和IIS高版本文件上传漏洞 包括FCKeditor eWebEditor 畸形解析漏洞等 本篇文章将
  • Linux 如何快速查看 IP 地址

    查看IP 1 进入Linux 系统 在主页面空旷的地方右键 选择 打开终端 2 在显示的界面中输入 ifconfig a 就可以查看到Linux 的地址了 快速查看IP 和修改 1 点击应用程序 gt 选择系统工具 选择设置 gt 找到网络
  • Ubuntu 安装 zshell

    一 检查系统中原来的shell cat etc shells 二 安装 zsh apt install zsh 安装zsh chsh s bin zsh 将zsh设置成默认shell 不设置的话启动zsh只有直接zsh命令即可 三 安装oh
  • 二叉树知识总结

    一 前言 数组的搜索比较方便 可以直接用下标 但删除和插入就比较麻烦 链表与之相反 删除和插入元素很快 但查找比较慢 此时 二叉树应运而生 二叉树既有链表的好处 也有数组的好处 在处理大批量的动态数据时比较好用 是一种折中的选择 文件系统和
  • 线程——一个计数器计数到100,在每个数字之间暂停1秒,每隔10个数字输出一个字符串

    16 一个计数器计数到100 在每个数字之间暂停1秒 每隔10个数字输出一个字符串 public class MyThread extends Thread public void run for int i 0 i lt 100 i if
  • Qt 环境搭建

    安装QtCreator 进入Qt官网https www qt io zh cn 点击下载按钮 然后选择试用Qt 这里下载的是免费版本 也就是社区版本 如果点击购买则下载专业版 点击下载后需要填写个人信息 填好邮箱和手机 还需要填写用途 并选
  • 【基于深度学习的生活垃圾分类识别管理可视化系统-哔哩哔哩】 https://b23.tv/0UBohX2

    基于深度学习的生活垃圾分类识别管理可视化系统 哔哩哔哩 https b23 tv 0UBohX2 https b23 tv 0UBohX2
  • 【前端】Vue+Element UI案例:通用后台管理系统-Header+导航栏折叠

    文章目录 目标 代码 0 创建组件 1 按钮 2 头像下拉框 3 去除左右缝隙 4 点击按钮折叠导航栏 Vuex 5 折叠标题和Header效果 总代码 CommonHeader vue store的index js store的tab j
  • 误区 一下代码是曾经误认为 radio的onclick 事件在 发生 以下是实例代码

    实际上onclick事件还是在radio上发生 只不过是通过js把 a 标签的href属性的 值 给动态的发生该表了而已 误以为是在 a 标签上发生了onclick事件 a a
  • playwright自动化项目搭建

    具备功能 关键技术 pylaywright测试库 pytest单元测试框架 pytest playwright插件 非关键技术 pytest html插件 pytest rerunfailures插件 seldom 测试框架 实现功能 元素
  • 静态链接原理以及过程

    通常程序的编译中 或多或少会调用其它库中的函数接口 本篇blog就是讲静态库的调用流程 通常我们知道编译一个可执行程序会有这四个过程 预处理 编译 汇编以及链接 前面三步就是产生目标文件 o的过程 链接就是把各个 o文件粘在一起 构成一个可