从目标文件结构,加载、执行阶段,汇编角度来理解C程序内存分区

一口Linux 2022-01-19 00:00

击上方“一口Linux”,选择“星标公众号

干货福利,第一时间送达!

每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段(segment):

代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。

数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

1 目标文件结构

目标文件是源代码编译但未链接的中间文件(Windows的.obj和Linux的.o),Windows的.obj采用 PE 格式,Linux 采用 ELF 格式,两种格式均是基于通用目标文件格式(COFF,Common Object File Format)变化而来,所以二者大致相同。

目标文件一般包含编译后的机器指令代码、数据、调试信息,还有链接时所需要的一些信息,比如重定位信息和符号表等,而且一般目标文件会将这些不同的信息按照不同的属性,以“节(section)”也叫“段(segment)”的形式进行存储。

#include 
#include

int gInitVar = 1; // .data 加载阶段加载
int gUninitVar; // .bss 加载阶段加载
const int gConstVar = 2; // .rdata 加载阶段加载
// extern可以修饰const用于扩展文件链接性,const默认是文件内链接的
// static修饰全局变量可以限制其文件链接性,其存储属性不变
void foo(int i) // .text 加载阶段加载
{
static int staticLocalInitVar = 3; // .data 加载阶段加载
static int staticLocalUninitVar; // .bss 加载阶段加载
int stack_localVar = 4; // 栈帧(每个程序运行时会加载1-2M栈空间)
const int LocalConstVar = 5; // 栈帧(运行时自动分配)
staticLocalUninitVar = LocalConstVar + gConstVar;
gUninitVar = staticLocalInitVar + gInitVar;
int *dynamic_heapData = (int*)malloc(sizeof(int)*10000000);
dynamic_heapData[10000000-1] = 9; // 堆区(运行时动态申请)
i += stack_localVar + staticLocalUninitVar + gUninitVar;
printf("%d\n",i+dynamic_heapData[10000000-1]);// .rdata,加载阶段加载"%d\n"
free(dynamic_heapData); // 堆内存需要显式释放
} // 栈内存在超出作用域后自动释放
int main()
{
foo(6);
getchar();
return 0;
} // 加载阶段加载的内存要等到程序结束才释放

文件的内容分割为不同的区块(Setion,又称区段,节等),区段中包含代码数据,各个区块按照页边界来对齐,区块没有限制大小,是一个连续的结构。每块都有他自己在内存中的属性,比如:这个块是否可读可写,或者只读等等。

① .text 代码

代码段存放程序的机器指令;

② .data 初始化数据段
初始化数据段存放已初始化的全局变量与局部静态变量;

③ .bss 未初始化数据段

未初始化局部静态变量(或初始化为0的局部静态变量)放到.bss段,对于未初始化全局变量(或初始化为0的全局变量)不同语言与编译器的实现有不同的处理,有的只是在.baa段预留一个未定义的全局变量符号,等到最终链接成可执行文件的时候再在 .bss 段分配空间。编译器会把未初始化的全局变量标记为一个 COMMON 符号,不为其在 .bss 段分配空间的原因是现在的编译器和链接器支持弱符号机制,即允许同一个弱符号定义在多个目标文件中,因为未初始化的全局变量属于弱符号,编译时无法确定符号大小,所以此时无法在 .bss 段为未初始化的全局变量分配空间。

④ .rdata或.rodata 只读数据段

只读数据段存放程序中只读变量,如const修饰的常量和字符串常量;

单独设立.rodata段的好处有很多,比如语义上支持了C的const常量,而且操作系统在加载的时候可以将.rodata段的内容映射为只读区,这样对于这个段的任何修改都会被判为非法,保证了程序的安全性。

⑤ .symtab 符号表段

.symtab段用于存符号表。每个目标文件都有一个相应的符号表(Symbol Table),记录了目标文件中用到的所有符号。每个符号都有一个对应的值,叫做符号值(Symbol Value),符号值可以是符号所对应的数据在段中的偏移量,也可以是该符号的对齐属性。

链接过程的本质就是要把多个不同的目标文件之间像拼图一样拼起来,相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。比如目标文件B用到了目标文件A中的函数foo,那么称目标文件A定义了函数foo,目标文件B引用了函数foo。定义与引用这两个概念同样适用于变量。每个函数和变量都有自己独一的名字,才能避免链接过程中不同变量和函数之间的混淆。在链接中,我们将函数和变量统称为符号(Symbol),函数或变量名就是符号名(Symbol Name)。

符号是链接的粘合剂,没有符号就无法完成链接。每一个目标文件都会有一个相应的符号表(Symbol Table),这个表里记录了目标文件中所用到的所有符号。每个定义的符号有一个对应的值叫做符号值(Symbol Value),对于变量和函数来说,符号值就是它们的地址。

除了函数和变量之外,还存在其它几种不常用到的符号。符号表中的符号可分为全局符号、局部符号、段名、行号等,对于链接而言,只关心全局符号。

⑥ .strtab 字符串表

因为字符串的长度往往是不定的,所以用固定的结构来表示它比较困难。一种很常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串(在汇编中使用offset)

7:        char * stringLiteral = "stringLiteral";
00401038 mov dword ptr [ebp-4],offset string "stringLiteral" (004230c0)

 .rela.text 代码段重定位表

重定位表,也叫作重定位段,用于链接器在处理目标文件时,重定位代码段中那些对绝对地址的引用的位置。比如 .text 段中对外部 printf() 函数的调用。每个要被重定位的地方叫重定位入口(Relocation Entry),OFFSET 表示该入口在所在段中的偏移位置,TYPE 表示重定位入口的类型,VALUE 表示重定位入口的符号名称。

2 加载与执行

项目全部相关文件最终会由链接器链接到一起形成一个可执行文件,Linux 系统中的每个可执行文件都运行在一个进程上下文中,有自己的虚拟地址空间。当shell 运行一个程序时,父shell 进程生成一个子进程,它是父进程的一个复制。子进程通过系统调用启动加载器。加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零。通过将虚拟地址空间中的页映射到可执行文件的页大小的片(chunk), 新的代码和数据段被初始化为可执行文件的内容。最后,加载器跳转到_start地址,它最终会调用应用程序的main 函数。


一个系统中的进程是与其他进程共享CPU和主存资源的。然而,共享主存会形成一些特殊的挑战。如果太多的进程需要太多的内存,那么它们中的一些就根本无法运行。当一个程序没有空间可用时,那就是它运气不好了。内存还很容易被破坏。如果某个进程不小心写了另一个进程使用的内存,它就可能以某种完全和程序逻辑无关的令人迷惑的方式失败。

为了更加有效地管理内存并且少出错,现代系统提供了一种对主存的抽象概念,叫做虚拟内存(VM)。虚拟内存是硬件异常、硬件地址翻译、主存、磁盘文件和内核软件的完美交互,它为每个进程提供了一个大的、一致的和私有的地址空间。通过一个很清晰的机制,虚拟内存提供了三个重要的能力:

1) 它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效地使用了主存。

2) 它为每个进程提供了一致的地址空间,从而简化了内存管理。

3) 它保护了每个进程的地址空间不被其他进程破坏。

虚拟内存是计算机系统最重要的概念之一。它成功的一个主要原因就是因为它是沉默地、自动地工作的,不需要应用程序员的任何干涉。


3 汇编代码分析

看以下源代码与汇编代码的对应,以及数据(变量)对应的地址值:

1:    #include 
2: #include
3:
4: int gInitVar = 1; // .data 加载阶段加载,所以这里无汇编对应
5: int gUninitVar; // .bss 加载阶段加载
6: const int gConstVar = 2; // .rdata 加载阶段加载
7:
8: void foo(int i) // .text 加载阶段加载
9: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,4Ch
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-4Ch]
0040102C mov ecx,13h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
10: static int staticLocalInitVar = 3; // .data 加载阶段加载,所以这里无汇编对应
11: static int staticLocalUninitVar; // .bss 加载阶段加载
12: int stack_localVar = 4; // 栈帧(每个程序运行时会加载若干M栈空间)
00401038 mov dword ptr [ebp-4],4 // 局部变量保存在栈上,由ebp及其偏移表示
13: const int LocalConstVar = 5; // 栈帧(运行时自动分配)
0040103F mov dword ptr [ebp-8],5 // 局部常量放在栈区
14: staticLocalUninitVar = LocalConstVar + gConstVar;
00401046 mov dword ptr [gUninitVar+4 (00428e40)],7
15: gUninitVar = staticLocalInitVar + gInitVar;
00401050 mov eax,[global_data+4 (00425a34)]
00401055 add eax,dword ptr [gInitVar (00425a30)]
0040105B mov [gUninitVar (00428e3c)],eax
16: int *dynamic_heapData = (int*)malloc(sizeof(int)*10000000);
00401060 push 2625A00h
00401065 call malloc (00401190)
0040106A add esp,4
0040106D mov dword ptr [ebp-0Ch],eax
17: dynamic_heapData[10000000-1] = 9; // 堆区(运行时动态申请)
00401070 mov ecx,dword ptr [ebp-0Ch]
00401073 mov dword ptr [ecx+26259FCh],9
18: i += stack_localVar + staticLocalUninitVar + gUninitVar;
0040107D mov edx,dword ptr [ebp-4]
00401080 add edx,dword ptr [gUninitVar+4 (00428e40)]
00401086 add edx,dword ptr [gUninitVar (00428e3c)]
0040108C mov eax,dword ptr [ebp+8]
0040108F add eax,edx
00401091 mov dword ptr [ebp+8],eax
19: printf("%d\n",i+dynamic_heapData[10000000-1]);// .rdata,加载阶段加载"%d\n"
00401094 mov ecx,dword ptr [ebp-0Ch]
00401097 mov edx,dword ptr [ebp+8]
0040109A add edx,dword ptr [ecx+26259FCh]
004010A0 push edx
004010A1 push offset string "\xd4\xcb\xd0\xd0\xbd\xd7\xb6\xce\xa3\xba\xb6\xaf\xcc\xac\xc9\xea\xc7\xeb\
004010A6 call printf (00403110)

004010AB add esp,8
20: free(dynamic_heapData); // 堆内存需要显式释放
004010AE mov eax,dword ptr [ebp-0Ch]
004010B1 push eax
004010B2 call free (00401c10)
004010B7 add esp,4
21: } // 栈内存在超出作用域后自动释放

 

end



一口Linux 


关注,回复【1024】海量Linux资料赠送

精彩文章合集

文章推荐

【专辑】ARM
【专辑】粉丝问答
【专辑】所有原创
专辑linux入门
专辑计算机网络
专辑Linux驱动
【干货】嵌入式驱动工程师学习路线
【干货】Linux嵌入式所有知识点-思维导图


点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看

一口Linux 写点代码,写点人生!
评论
  • 首先在gitee上打个广告:ad5d2f3b647444a88b6f7f9555fd681f.mp4 · 丙丁先生/香河英茂工作室中国 - Gitee.com丙丁先生 (mr-bingding) - Gitee.com2024年对我来说是充满挑战和机遇的一年。在这一年里,我不仅进行了多个开发板的测评,还尝试了多种不同的项目和技术。今天,我想分享一下这一年的故事,希望能给大家带来一些启发和乐趣。 年初的时候,我开始对各种开发板进行测评。从STM32WBA55CG到瑞萨、平头哥和平海的开发板,我都
    丙丁先生 2024-12-11 20:14 68浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-11 17:58 83浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-12 10:13 21浏览
  • 天问Block和Mixly是两个不同的编程工具,分别在单片机开发和教育编程领域有各自的应用。以下是对它们的详细比较: 基本定义 天问Block:天问Block是一个基于区块链技术的数字身份验证和数据交换平台。它的目标是为用户提供一个安全、去中心化、可信任的数字身份验证和数据交换解决方案。 Mixly:Mixly是一款由北京师范大学教育学部创客教育实验室开发的图形化编程软件,旨在为初学者提供一个易于学习和使用的Arduino编程环境。 主要功能 天问Block:支持STC全系列8位单片机,32位
    丙丁先生 2024-12-11 13:15 63浏览
  • 近日,搭载紫光展锐W517芯片平台的INMO GO2由影目科技正式推出。作为全球首款专为商务场景设计的智能翻译眼镜,INMO GO2 以“快、准、稳”三大核心优势,突破传统翻译产品局限,为全球商务人士带来高效、自然、稳定的跨语言交流体验。 INMO GO2内置的W517芯片,是紫光展锐4G旗舰级智能穿戴平台,采用四核处理器,具有高性能、低功耗的优势,内置超微高集成技术,采用先进工艺,计算能力相比同档位竞品提升4倍,强大的性能提供更加多样化的应用场景。【视频见P盘链接】 依托“
    紫光展锐 2024-12-11 11:50 69浏览
  • 铁氧体芯片是一种基于铁氧体磁性材料制成的芯片,在通信、传感器、储能等领域有着广泛的应用。铁氧体磁性材料能够通过外加磁场调控其导电性质和反射性质,因此在信号处理和传感器技术方面有着独特的优势。以下是对半导体划片机在铁氧体划切领域应用的详细阐述: 一、半导体划片机的工作原理与特点半导体划片机是一种使用刀片或通过激光等方式高精度切割被加工物的装置,是半导体后道封测中晶圆切割和WLP切割环节的关键设备。它结合了水气电、空气静压高速主轴、精密机械传动、传感器及自动化控制等先进技术,具有高精度、高
    博捷芯划片机 2024-12-12 09:16 80浏览
  • RK3506 是瑞芯微推出的MPU产品,芯片制程为22nm,定位于轻量级、低成本解决方案。该MPU具有低功耗、外设接口丰富、实时性高的特点,适合用多种工商业场景。本文将基于RK3506的设计特点,为大家分析其应用场景。RK3506核心板主要分为三个型号,各型号间的区别如下图:​图 1  RK3506核心板处理器型号场景1:显示HMIRK3506核心板显示接口支持RGB、MIPI、QSPI输出,且支持2D图形加速,轻松运行QT、LVGL等GUI,最快3S内开
    万象奥科 2024-12-11 15:42 83浏览
  • 全球知名半导体制造商ROHM Co., Ltd.(以下简称“罗姆”)宣布与Taiwan Semiconductor Manufacturing Company Limited(以下简称“台积公司”)就车载氮化镓功率器件的开发和量产事宜建立战略合作伙伴关系。通过该合作关系,双方将致力于将罗姆的氮化镓器件开发技术与台积公司业界先进的GaN-on-Silicon工艺技术优势结合起来,满足市场对高耐压和高频特性优异的功率元器件日益增长的需求。氮化镓功率器件目前主要被用于AC适配器和服务器电源等消费电子和
    电子资讯报 2024-12-10 17:09 98浏览
  • 我的一台很多年前人家不要了的九十年代SONY台式组合音响,接手时只有CD功能不行了,因为不需要,也就没修,只使用收音机、磁带机和外接信号功能就够了。最近五年在外地,就断电闲置,没使用了。今年9月回到家里,就一个劲儿地忙着收拾家当,忙了一个多月,太多事啦!修了电气,清理了闲置不用了的电器和电子,就是一个劲儿地扔扔扔!几十年的“工匠式”收留收藏,只能断舍离,拆解不过来的了。一天,忽然感觉室内有股臭味,用鼻子的嗅觉功能朝着臭味重的方向寻找,觉得应该就是这台组合音响?怎么会呢?这无机物的东西不会腐臭吧?
    自做自受 2024-12-10 16:34 168浏览
  • 时源芯微——RE超标整机定位与解决详细流程一、 初步测量与问题确认使用专业的电磁辐射测量设备,对整机的辐射发射进行精确测量。确认是否存在RE超标问题,并记录超标频段和幅度。二、电缆检查与处理若存在信号电缆:步骤一:拔掉所有信号电缆,仅保留电源线,再次测量整机的辐射发射。若测量合格:判定问题出在信号电缆上,可能是电缆的共模电流导致。逐一连接信号电缆,每次连接后测量,定位具体哪根电缆或接口导致超标。对问题电缆进行处理,如加共模扼流圈、滤波器,或优化电缆布局和屏蔽。重新连接所有电缆,再次测量
    时源芯微 2024-12-11 17:11 106浏览
  • 一、SAE J1939协议概述SAE J1939协议是由美国汽车工程师协会(SAE,Society of Automotive Engineers)定义的一种用于重型车辆和工业设备中的通信协议,主要应用于车辆和设备之间的实时数据交换。J1939基于CAN(Controller Area Network)总线技术,使用29bit的扩展标识符和扩展数据帧,CAN通信速率为250Kbps,用于车载电子控制单元(ECU)之间的通信和控制。小北同学在之前也对J1939协议做过扫盲科普【科普系列】SAE J
    北汇信息 2024-12-11 15:45 108浏览
  • 本文介绍瑞芯微RK3588主板/开发板Android12系统下,APK签名文件生成方法。触觉智能EVB3588开发板演示,搭载了瑞芯微RK3588芯片,该开发板是核心板加底板设计,音视频接口、通信接口等各类接口一应俱全,可帮助企业提高产品开发效率,缩短上市时间,降低成本和设计风险。工具准备下载Keytool-ImportKeyPair工具在源码:build/target/product/security/系统初始签名文件目录中,将以下三个文件拷贝出来:platform.pem;platform.
    Industio_触觉智能 2024-12-12 10:27 29浏览
  • 全球智能电视时代来临这年头若是消费者想随意地从各个通路中选购电视时,不难发现目前市场上的产品都已是具有智能联网功能的智能电视了,可以宣告智能电视的普及时代已到临!Google从2021年开始大力推广Google TV(即原Android TV的升级版),其他各大品牌商也都跟进推出搭载Google TV操作系统的机种,除了Google TV外,LG、Samsung、Panasonic等大厂牌也开发出自家的智能电视平台,可以看出各家业者都一致地看好这块大饼。智能电视的Wi-Fi连线怎么消失了?智能电
    百佳泰测试实验室 2024-12-12 17:33 33浏览
  • 应用环境与极具挑战性的测试需求在服务器制造领域里,系统整合测试(System Integration Test;SIT)是确保产品质量和性能的关键步骤。随着服务器系统的复杂性不断提升,包括:多种硬件组件、操作系统、虚拟化平台以及各种应用程序和服务的整合,服务器制造商面临着更有挑战性的测试需求。这些挑战主要体现在以下五个方面:1. 硬件和软件的高度整合:现代服务器通常包括多个处理器、内存模块、储存设备和网络接口。这些硬件组件必须与操作系统及应用软件无缝整合。SIT测试可以帮助制造商确保这些不同组件
    百佳泰测试实验室 2024-12-12 17:45 25浏览
  • 在智能化技术快速发展当下,图像数据的采集与处理逐渐成为自动驾驶、工业等领域的一项关键技术。高质量的图像数据采集与算法集成测试都是确保系统性能和可靠性的关键。随着技术的不断进步,对于图像数据的采集、处理和分析的需求日益增长,这不仅要求我们拥有高性能的相机硬件,还要求我们能够高效地集成和测试各种算法。我们探索了一种多源相机数据采集与算法集成测试方案,能够满足不同应用场景下对图像采集和算法测试的多样化需求,确保数据的准确性和算法的有效性。一、相机组成相机一般由镜头(Lens),图像传感器(Image
    康谋 2024-12-12 09:45 74浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦