Linux内存管理--系列文章肆

一、引子
上篇文章介绍了目标文件,也就是讲到编译过程中的汇编这个阶段。本篇要讲目标文件怎么变成一个可执行文件的,介绍编译过程中的链接
链接主要分为两种,静态链接动态链接。它们本质上的区别,是在程序的编译和运行过程中使用库的方式不同。

特性静态链接动态链接
文件大小较大较小
运行独立性高(无需外部库)低(需要外部库)
启动速度较快稍慢
内存使用不共享库,可能使用更多内存库共享,节省内存
更新和维护更新库需要重新编译和分发整个程序更新库只需替换库文件
兼容性无库版本问题,较高可能存在库版本冲突问题

二、静态链接要做什么

在编译时将所有必要的库和模块直接嵌入到生成的可执行文件中,这是静态链接的定义。这意味着可执行文件包含了所有运行所需的代码,因此无需依赖外部库文件。
下面将用两个Demo来示范,1.c2.c

这是1.c
extern int a;
extern void func1(void);int main()
{int b = 0;func1();return 0;
}
这是2.c
int a = 0;void func1(void)
{int c = 0;c++;
}
 objdump -h 1.o1.o:     文件格式 elf64-x86-64节:
Idx Name          Size      VMA               LMA               File off  Algn0 .text         0000001f  0000000000000000  0000000000000000  00000040  2**0CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data         00000000  0000000000000000  0000000000000000  0000005f  2**0CONTENTS, ALLOC, LOAD, DATA2 .bss          00000000  0000000000000000  0000000000000000  0000005f  2**0ALLOC3 .comment      0000002c  0000000000000000  0000000000000000  0000005f  2**0CONTENTS, READONLY4 .note.GNU-stack 00000000  0000000000000000  0000000000000000  0000008b  2**0CONTENTS, READONLY5 .note.gnu.property 00000020  0000000000000000  0000000000000000  00000090  2**3CONTENTS, ALLOC, LOAD, READONLY, DATA6 .eh_frame     00000038  0000000000000000  0000000000000000  000000b0  2**3CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATAobjdump -h 2.o2.o:     文件格式 elf64-x86-64节:
Idx Name          Size      VMA               LMA               File off  Algn0 .text         00000016  0000000000000000  0000000000000000  00000040  2**0CONTENTS, ALLOC, LOAD, READONLY, CODE1 .data         00000000  0000000000000000  0000000000000000  00000056  2**0CONTENTS, ALLOC, LOAD, DATA2 .bss          00000004  0000000000000000  0000000000000000  00000058  2**2ALLOC3 .comment      0000002c  0000000000000000  0000000000000000  00000058  2**0CONTENTS, READONLY4 .note.GNU-stack 00000000  0000000000000000  0000000000000000  00000084  2**0CONTENTS, READONLY5 .note.gnu.property 00000020  0000000000000000  0000000000000000  00000088  2**3CONTENTS, ALLOC, LOAD, READONLY, DATA6 .eh_frame     00000038  0000000000000000  0000000000000000  000000a8  2**3CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

ld 1.o 2.o -e main -o a.out

objdump -h a.out a.out:     文件格式 elf64-x86-64节:
Idx Name          Size      VMA               LMA               File off  Algn0 .note.gnu.property 00000020  00000000004001c8  00000000004001c8  000001c8  2**3CONTENTS, ALLOC, LOAD, READONLY, DATA1 .text         00000035  0000000000401000  0000000000401000  00001000  2**0CONTENTS, ALLOC, LOAD, READONLY, CODE2 .eh_frame     00000058  0000000000402000  0000000000402000  00002000  2**3CONTENTS, ALLOC, LOAD, READONLY, DATA3 .bss          00000008  0000000000404000  0000000000404000  00003000  2**2ALLOC4 .comment      0000002b  0000000000000000  0000000000000000  00002058  2**0CONTENTS, READONLY

2.1 地址和空间的分配

我们将上面两个文件变成目标文件后,每个目标文件都是有自己的代码段,数据段,bss段等。但可执行文件是一个文件,那也就是说链接时要找到各文件中的各符号地址和各段地址,并将它们合并起来,并且分配运行时候的空间。
链接器会为可执行文件或共享库分配内存空间,还会为每个符号分配一个唯一的内存地址。

2.1.1 各段依次堆叠(Sequential Stacking)

最简单的一个方案,是将输入的目标文件按照次序依次叠加起来,生成输出的可执行文件或共享库。
步骤
扫描目标文件: 链接器首先会扫描所有输入的目标文件,提取每个目标文件的信息,包括代码段、数据段、BSS 段以及符号表等。
地址分配: 链接器会为可执行文件或共享库分配内存空间,并为每个符号分配一个唯一的内存地址。
生成输出文件: 链接器会将所有输入的目标文件的内容依次复制到输出文件中,并根据符号表修正符号地址。
特点
实现简单: 按序叠加的实现代码比较简单,易于理解和开发。
效率低下: 按序叠加会导致输出文件体积较大,并且可能会产生大量的内存碎片,降低程序的运行效率。对于一些硬件来讲,段的装载和空间对齐单位是页,也就是4KB大小。即便只有1B大小也会分配出4KB的内存。
不适用于大型项目: 对于大型项目,按序叠加会导致链接过程非常耗时,并且可能无法有效利用内存空间。

2.1.2 相似段合并(Merge Similar Segments)

各段依次堆叠会产生各种问题。所以在链接目标文件的过程中,可能会执行相似段合并的优化步骤,以减少可执行文件的大小并提高执行效率。相似段合并的步骤通常在优化和合并阶段进行,其主要目标是识别并合并具有相似代码或数据的段,以消除重复并节省空间。
步骤
识别相似段:链接器分析目标文件中的代码段和数据段,寻找具有相似内容的段。相似内容可能是相同的代码片段、常量数据或其他类型的数据。
合并相似段:对于找到的相似段,链接器将其合并成一个单一的段。合并过程可能涉及将重复的代码片段或数据项替换为单个引用,并确保所有引用指向合并后的段。
更新引用
在合并后的段被创建后,链接器需要更新目标文件中的所有引用,以便指向合并后的段。这可能涉及修改目标文件中的地址引用或符号表条目。
删除冗余段:完成相似段的合并后,链接器可能会删除原始目标文件中的冗余段,以释放空间并简化可执行文件的结构。

2.1.3 示例分析

a.out.text段的size就是目标文件的.text段段的size总和。a.out的VMA和LMA是有数值的,但目标文件并没有的。VMA是虚拟地址,是在链接后根据各段的大小被分配的。LMA是加载地址,在确定每个段的大小、对齐要求以及其他系统布局策略并并保证各段在内存中不会相互冲突的情况下,链接器为每个段分配一个加载地址。

有些资料会讲Linux中的.text段默认会分配到0x08048094.data段默认会分配到0x08049108,说是ELF文件默认从该地址分配。但目前新一点版本的静态链接的可执行文件的默认加载地址也不是固定的,它的加载地址同样是由操作系统的动态链接器 ld.so(或者其替代品)在可执行文件被加载时动态确定的。如果强制指定可执行文件的加载地址,可以使用一些链接选项来指定加载地址,例如-Wl,-Ttext-segment=地址 。该技术是地址空间布局随机化(ASLR,Address Space Layout Randomization),主要目的是在每次加载可执行文件时,随机选择加载地址,从而增加攻击者利用程序漏洞的难度。

File off是各个段的起始偏移量。这个偏移量表示了每个段在文件中的位置,即相对于文件的起始位置的偏移量。在链接时,File off的作用主要是用来确定每个段在最终生成的可执行文件中的位置,并且在重定位阶段被使用。在生成最终的可执行文件时,File off 信息通常会被包含在可执行文件的头部或段表中,以便操作系统在加载可执行文件时正确地映射每个段到内存中的位置。

2.2 符号解析和重定位

符号解析和重定位对于程序的正确运行至关重要。如果没有符号解析,程序将无法找到符号定义的变量、函数等对象。如果没有重定位,程序将无法在不同的内存地址上运行。
一般出现在该步骤的错误
符号未定义: 程序中使用了未定义的符号。
符号重复定义: 程序中有多个符号定义使用了相同的名称。
符号地址冲突: 两个或多个符号被分配了相同的地址。

2.2.1 符号解析(Symbol Resolution)

通过符号解析,链接器能够确保所有的符号引用都能正确指向其在其他目标文件或库中的定义,从而使得最终生成的可执行文件能够正确地执行。

链接器首先解析所有目标文件中的符号(函数和变量)。每个目标文件包含的符号可以分为以下两类
定义的符号:在目标文件中定义的符号(例如函数实现或变量定义)。
引用的符号:目标文件中使用但未定义的符号(例如外部函数或变量)。
链接器需要确保所有引用的符号都能在某个目标文件或库中找到定义。

主要过程
定义和引用:在程序中,符号可以是函数、变量或其他命名实体。在目标文件中,这些符号可以被定义(有实际的实现)或引用(被使用但未定义)。
扫描目标文件:链接器首先会扫描所有的目标文件和库文件,从中提取出所有的符号。这个过程包括解析目标文件的符号表、重定位表等信息。
符号解析:对于每个引用的符号,链接器需要在其他目标文件或库中找到对应的定义。这可能涉及搜索多个目标文件和库文件,直到找到符号的定义。如果找不到符号的定义,链接器将会报错并提示未定义的符号。
符号表管理:链接器维护一个符号表,记录了每个符号的定义和引用情况。对于每个引用的符号,链接器会在符号表中查找其定义,以确定其在可执行文件中的实际位置。
生成符号表:在符号解析过程完成后,链接器会生成一个最终的符号表,其中包含了所有解析后的符号信息。这个符号表将被用于后续的符号重定位等操作。

2.2.2 符号重定位(Symbol Relocation)

目的是将程序中使用的符号引用(例如函数地址、变量地址等)修正为实际的内存地址。符号重定位是确保程序在不同环境下能够正确运行的关键步骤之一。

在目标文件中,符号的地址通常是相对的或未确定的。链接器需要将这些符号地址调整到它们在最终可执行文件中的实际内存地址。这一步包括以下过程
地址计算:在符号解析完成后,链接器需要计算每个符号在最终可执行文件中的内存地址。这个地址通常是相对于程序的起始地址的偏移量。
地址调整:链接器将目标文件中的所有引用符号的地址调整为其在最终可执行文件中的实际内存地址。这包括修改目标文件中的指令、数据、符号表等,以便在程序执行时正确地指向符号的位置。
重定位表:重定位表记录了需要进行重定位的符号及其偏移量。链接器根据重定位表中的信息,将引用的符号地址进行调整,以确保程序能够正确执行。
代码和数据重定位:对于代码段和数据段中的引用符号,链接器会修改指令或数据中的地址,使其指向正确的内存位置。这可能涉及修改指令中的跳转地址、函数调用地址,或者修改数据中的符号地址引用。
符号表更新:在完成符号重定位后,链接器可能会更新符号表中的符号信息,以反映符号在最终可执行文件中的实际位置。

2.2.3 示例分析

objdump -d a.out 
a.out:     文件格式 elf64-x86-64
Disassembly of section .text:
0000000000401000 <main>:401000:	f3 0f 1e fa          	endbr64 401004:	55                   	push   %rbp401005:	48 89 e5             	mov    %rsp,%rbp401008:	48 83 ec 10          	sub    $0x10,%rsp40100c:	c7 45 fc 00 00 00 00 	movl   $0x0,-0x4(%rbp)401013:	e8 07 00 00 00       	callq  40101f <func1>401018:	b8 00 00 00 00       	mov    $0x0,%eax40101d:	c9                   	leaveq 40101e:	c3                   	retq   000000000040101f <func1>:40101f:	f3 0f 1e fa          	endbr64 401023:	55                   	push   %rbp401024:	48 89 e5             	mov    %rsp,%rbp401027:	c7 45 fc 00 00 00 00 	movl   $0x0,-0x4(%rbp)40102e:	83 45 fc 01          	addl   $0x1,-0x4(%rbp)401032:	90                   	nop401033:	5d                   	pop    %rbp401034:	c3                   	retq   

例子1.o.text段的大小是1F,2.o中的.text段的大小是16。并且我们将程序的入口函数定位了main函数。所以a.out.text段的起始位置也就是main函数的位置。那func1函数的位置就是main + 1F了。查看a.out的汇编代码,发现callq 40101f ,这就是调用了func1函数。这样可执行文件就可以正常运行了。

三、静态链接过程注意点

3.1 强符号和弱符号不一致

在链接器的世界中,符号可以被分为两种类型:强符号(Strong Symbol) 弱符号(Weak Symbol)。这些术语通常用于描述符号的可见性和优先级。
强符号(Strong Symbol):强符号是具有全局可见性和固定地址的符号。当多个目标文件或库文件中存在相同名称的强符号时,链接器会选择其中的一个作为最终的定义。通常情况下,全局变量函数都是强符号。强符号的定义具有最高的优先级,将覆盖所有弱符号和普通符号。
弱符号(Weak Symbol):弱符号是具有全局可见性但没有固定地址的符号。当多个目标文件或库文件中存在相同名称的弱符号时,链接器会选择其中的一个作为最终的定义。弱符号的定义具有较低的优先级,当没有找到强符号时,链接器才会考虑使用弱符号。通常情况下,全局变量的声明函数的声明会被视为弱符号,因为它们没有固定的地址。
在程序中强弱符号定义不一致,主要分三种
两个及以上强符号类型不一致:多个强符号定义,会在链接时报重定义,因为链接器无法判断该符号的类型。
类型不一致的一个强符号和多个弱符号:在程序分配内存时,会按强符号的类型分配内存,同时报告警:size of symbole 符号 change from 。在连接的时候,要是弱符号的类型大于强符号,将报alignment 1 of symbol 符号 in 目标文件 is smaller than 8 in 目标文件
两个及以上弱符号类型不一致:这个时候,编译器不会给出要符号内存,只能到链接过程中看有没有强符号,在分配内存。这种情况下不会报告警和错误,但运行时会有未定义符号或者直接段错误。
综上,我们应该可以清楚了,为什么目标文件的bss段在磁盘中的大小是0。因为编译器无法判断其他编译单元中是否有比本单元该符号所占的内存更大的符号,所以给不出内存空间。

3.2 静态库链接

将静态库(Static Library)嵌入到可执行文件(Executable File)中的过程。静态库是一种预编译的代码库,包含函数、变量和其他代码对象,但并非直接可执行。在静态库链接过程中,链接器会将静态库中的所有代码和数据复制到可执行文件中,使其成为可执行文件的一部分。
优点
可执行文件独立性: 静态链接的可执行文件无需依赖外部库,即使在没有库的情况下也能运行。
提高安全性: 静态链接的可执行文件包含所有代码,因此更不容易受到外部库漏洞的影响。
减小可执行文件大小: 在某些情况下,静态链接可使可执行文件更小,因为无需包含额外的库文件。

缺点:
增加可执行文件大小: 在大多数情况下,静态链接会使可执行文件更大,因为包含了所有静态库代码。
浪费资源: 如果多个可执行文件使用相同的静态库,则每个可执行文件都包含该库的副本,这会导致资源浪费。
更新困难: 当静态库更新时,需要重新编译所有依赖它的可执行文件。

本篇大致讲述了静态链接的情况,下一篇文章将会对动态链接的情况进行阐述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/16140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自动化您的任务——crewAI 初学者教程

今天&#xff0c;我写这篇文章是为了分享您开始使用一个非常流行的多智能体框架所需了解的所有信息&#xff1a;crewAI。 我将在这里或那里跳过一些内容&#xff0c;使本教程成为一个精炼的教程&#xff0c;概述帮助您入门的关键概念和要点 今天&#xff0c;我写这篇文章是为了…

【代码随想录训练营】【Day 31】【回溯-5】| Leetcode 491, 46, 47

【代码随想录训练营】【Day 31】【回溯-5】| Leetcode 491, 46, 47 需强化知识点 排列问题和组合分割子集问题的区别&#xff1a; 排列是讲究顺序的&#xff0c;不同顺序的组合是不同的&#xff0c;因此不能使用startIndex来限制选择顺序&#xff0c;需要used来判断是否已被选…

easy-rule规则引擎使用

简介 轻量级的规则引擎&#xff0c;易于学习的api 简单来说&#xff0c;规则引擎就是一个函数&#xff1a;yf(x1,x2,…,xn) 将业务代码和业务规则分离&#xff0c;解耦业务决策和业务代码的绑定关系 入门示例 依赖引入 <dependency><groupId>org.jeasy</grou…

页面<html>上多了一个滚动条,定位发现是<body>里面多了一个id为trans-tooltip的div

现象分析&#xff1a; 页面根标签html多了一个滚动条&#xff0c;发现body里面多了一个id为trans-tooltip的div&#xff0c;虽然width为0&#xff0c;height为0&#xff0c;但是其子元素还是有高度&#xff0c;占据了空间&#xff0c;最终导致了滚动条&#xff1b; 根本原因&…

C++ 并查集模板

搬运jiangly鸽鸽的板子 #include <iostream> #include <string> #include <vector> #include <numeric>struct DSU {std::vector<int> f, siz;DSU() {}DSU(int n) {init(n);}void init(int n) {f.resize(n);std::iota(f.begin(), f.end(), 0);s…

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(十三)

本系列课程&#xff0c;将重点讲解Phpsploit-Framework框架软件的基础使用&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 接上一篇文章内容&#xff0c;讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 我们&#xff0c;继续讲一…

Java面试题--JVM大厂篇(1-10)

引言&#xff1a; 在这个信息时代&#xff0c;对于准备进入大厂工作的朋友们来说&#xff0c;对于JVM&#xff08;Java虚拟机&#xff09;的掌握是面试中的一项重要内容。下面是一些精选的JVM面试题&#xff0c;希望对大家能有所帮助。 正文&#xff1a; 1. JVM有哪几种垃圾收…

[CISCN2024]-PWN:gostack解析(go语言程序,syscall)

查看保护 ida比较复杂&#xff0c;建议动调配合静态分析程序运行 这里函数返回不用leave和ret&#xff0c;而是利用add rsp和ret&#xff0c;所以要动调查看到底要覆盖哪里。 完整exp&#xff1a; from pwn import* pprocess(./gostack) syscall0x4616c9 pop_rax0x40f984 po…

GB报文中的Cseq值的注意点

一、 问题现象 【问题现象】NVR使用GB接三方平台发现倍速回放时&#xff0c; 【现场拓扑】现场拓扑如下 &#xff08;1&#xff09; NVR侧使用家用宽带的方式&#xff0c;通过国标跨公网接入三方平台。 图1.1&#xff1a;网络拓扑 二、 抓包分析 INVITE sip:420000004013200…

OpenAI安全系统负责人:从头构建视频生成扩散模型

作者 | Lilian Weng OneFlow编译 翻译&#xff5c;杨婷、宛子琳、张雪聃 题图由SiliconFlow MaaS平台生成 过去几年&#xff0c;扩散模型&#xff08;Diffusion models&#xff09;在图像合成领域取得了显著成效。目前&#xff0c;研究界已开始尝试更具挑战性的任务——将该技术…

深度学习Day-18:ResNet50V2算法实战与解析

&#x1f368; 本文为&#xff1a;[&#x1f517;365天深度学习训练营] 中的学习记录博客 &#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制] 要求&#xff1a; 根据本文Tensorflow代码&#xff0c;编写对应的Pytorch代码了解ResNetV2与ResNetV的区别 一、 基础…

【实战JVM】-基础篇-03-Java内存结构

【实战JVM】-基础篇-03-Java内存结构 1 运行时数据区1.1 总览1.2 程序计数器1.2.1 是否会内存溢出 1.3 java虚拟机栈1.3.1 栈帧的组成1.3.1.1 局部变量表1.3.1.2 操作数栈1.3.1.3 帧数据1.3.1.3.1 动态链接1.3.1.3.2 方法出口1.3.1.3.3 异常表 1.3.2 是否会内存溢出1.3.3 设置虚…

Day50 | 309.最佳买卖股票时机含冷冻期 714.买卖股票的最佳时机含手续费 总结

代码随想录算法训练营Day50 | 309.最佳买卖股票时机含冷冻期 714.买卖股票的最佳时机含手续费 总结 LeetCode 309.最佳买卖股票时机含冷冻期 题目链接&#xff1a;LeetCode 309.最佳买卖股票时机含冷冻期 思路&#xff1a; 四个状态。 保持持有股票&#xff0c;保持卖出股票…

Ardupilot开源飞控之FollowMe计划

Ardupilot开源飞控之FollowMe计划 1. 源由2. 分析3. 计划4. 话题5. 设备6. 后续 1. 源由 随着现在的智能设备越来越智能&#xff0c;FollowMe就是一个最基本的特性。&#xff08;注&#xff1a;远程遥控&#xff0c;传感联动&#xff0c;这些都是最基本的自动化控制技术&#…

【神经网络结构可视化】使用 Visualkeras 可视化 Keras / TensorFlow 神经网络结构

文章目录 Visualkeras介绍下载安装代码示例1、导入必要的库2、创建VGG16神经网络模型3、可视化神经网络结构4、完整代码5、使用教程 可视化自己创建的神经网络结构1、导入要的库2、创建自己的神经网络模型3、可视化神经网络结构图4、完整代码 Visualkeras介绍 Visualkeras是一…

C++代码错误解决1(函数模板)

1、代码如下 //示例函数模板的使用 #include <iostream> #include <string> using namespace std; template <typename T>//函数模板 T max(T a,T b) {return a>b?a:b; } int main() {int a,b;cout<<"input two integers to a&b:"…

【微机原理及接口技术】可编程并行接口芯片8255A

【微机原理及接口技术】可编程并行接口芯片8255A 文章目录 【微机原理及接口技术】可编程并行接口芯片8255A前言一、8255A的内部结构和引脚1.与外设接口&#xff08;数据端口&#xff09;2.与处理器接口 二、8255A的工作方式三、8255A的编程1. 写入方式控制字&#xff1a;控制字…

从0开始回顾ElasticSearch

1 elasticsearch概述 1.1 elasticsearch简介 官网: https://www.elastic.co/ ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff0c;并作为Apache许可条款下的…

【动手学强化学习】第 6 章 Dyna-Q 算法知识点总结

【动手学强化学习】第 6 章 Dyna-Q 算法知识点总结 本章知识点基于模型的强化学习与无模型的强化学习方法简介无模型的强化学习方法基于模型的强化学习方法 强化学习算法的评价指标Dyna-Q算法Dyna-Q 算法的具体流程Dyna-Q 代码实践 本章知识点 基于模型的强化学习与无模型的强…