《程序员的自我修养》读书笔记 -- 第三章

第三章 目标文件里有什么

3.1 目标文件的格式

1、目标文件就是源代码编译后还未进行链接的中间文件。因为目标文件与可执行文件的内容和结构很相似,所以一般跟可执行文件的存储形式相同,Linux下统称为ELF可执行文件。动态链接库与动态链接库也使用可执行文件格式存储。

2、ELF文件标准里面把ELF文件归为4类:

l  可重定位文件(这类文件包含代码和数据,可被用来链接成可执行文件,静态链接库属于此类。如linux下的.o文件和windows下的.obj )

l  可执行文件(这类文件包含可直接执行的程序,ELF可执行文件属于此类,一般没有扩展名。如/bin/bash文件,windows下的.exe文件)

l  共享目标文件(包含代码和数据,可以在以下两种情况下使用。A.链接器可以使用这种文件跟其它的可重定位文件和共享目标文件链接,产生新的目标文件。B、动态链接器可以将几个这种共享目标文件与可执行文件结合,作为进程映像的一部分来运行。如linux的.so,windows的DLL)

l  核心转储文件(进程意外终止时,系统将该进程的地址空间的内容及终止的其他信息转储到核心转储文件。如linux下的core dump文件)

3.2 目标文件是什么样的

1、目标文件将指令代码、数据、符号表、字符串等信息按段存储。

说明:未初始化的全局变量和静态变量默认值都是0,本来应该存在.data段,但是开辟空间存放0是没有必要的。因此存在.bss段,只是为未初始化的全局变量和静态变量预留位置而已,它并没有内容,在文件中也不占空间。

2、总体来说,程序被编译之后主要分为两种段:程序指令和程序数据。代码段属于指令,.bss和数据段属于程序数据。

3、指令和数据分开存放的原因:

(1)程序被装载之后,数据和指令分别被映射到两个虚存区域。数据区对进程来说可读写,但指令区对进程只可读。因此分开存放,可以防止程序的指令被改写。

(2)现在的CPU有强大的缓存体系,所以程序必须尽量提高缓存的命中率。指令和数据的分离有利于提高程序的局部性。CPU的缓存一般都被设计成数据缓存与指令缓存分离。

(3)当系统运行多个该程序的副本时,它们的指令是一样的,因此内存中只需要保存一份该程序的指令部分。共享指令,可以节省大量的内存。

3.3 挖掘simpleSection.o

我们编写一个例子程序,simpleSection.c,代码如下。

接着,使用gcc来编译这个文件。gcc –c simpleSection.c (-c参数表示只编译不链接)。现在我们已经得到了simpleSection.o目标文件,使用objdump工具来查看目标文件的内部结构。执行下面的命令:参数“-h”就是把ELF文件的各个段的基本信息打印出来。

在打印出来的信息中,我们可以看到,simpleSection.o一共包含6个段,分别是代码段、数据段、bss段,只读数据段(rodata),注释信息段(comment),堆栈提示段(.note.GUN-stack)。至于段的具体信息,最上面一行的Size和File off分别表示短的长度和位置。每段第二行的CONTENTS,ALLOC等,表示段的各种属性。CONTENTS,表示该段在文件中存在,bss段没有CONTENTS,就说明bss其实是不存在的。

注:上面没说.eh_frame段,《程序员的自我修养》一书中,给出的simpleSection.o段信息并没有eh_frame段,但我在自己的主机上查看,多了这个段。在网上查了下这个段的作用,是辅助调试时,保存一些编译的帧栈信息的。具体为什么要保存在这里,一篇博客作了解释:https://book.2cto.com/201309/33387.html

3.3.1代码段

使用objdump的-s参数可将所有段的内容以十六进制的方式打印出来,-d参数可以将所有包含指令的段反汇编。

text段的内容如上所示,上一节中,我们看到text的的长度为0x51,换算为10进制,则是81个字节。现在我们看到text的内容,2个16进制数为一个字节,刚好是81字节。

对照下面的反汇编结果,可以看到.text段中包含的就是main()和func1()的指令。

3.3.2 数据段和只读数据段

.data段中保存的是全局变量和静态变量(含全局和局部)。【在原书中,写的是保存已经初始化的全局静态变量和局部静态变量。我觉得这句话有误,初始化的全局普通变量也在.data段中存放,如global_init_data】

在simpleSection.c程序中,我们调用printf的时候,使用了一个字符串常量“%d\n”,这是一种只读数据,因此存放在.rodata中。

程序里的只读变量,如const修饰的变量和字符串常量,都会存放在.rodata中,单独设立只读数据段有很多好处,不仅在语义上支持了C++的const关键字,而且操作系统在加载的时候可以将.rodata段的属性映射成只读,保证了程序的安全性。

3.3.3 BSS段

.bss段存放的是未初始化的全局变量和局部静态变量

我们看到,针对simpleSection.c程序,里面有两个未初始化的变量,global_uninit_var和static_var2,但是打印出来的段信息中.bss段的长度为4,显然只有一个变量存放在.bss段中了。这是因为不同的编译器实现的方式不同,有的编译器会把全局未初始化变量存进.bss,有些则不存放,只预留一个未定义的全局变量和“COMMON块”。

如上两行代码,x1和x2会被存放在什么段中呢?照常理,这两个变量都初始化了,应该存放在data段。然而事实是,x1存放在.bss段,x2存放在.data段。原因是x1的值为0,可以认为是未初始化的,所以被优化掉可以放在.bss,这样可以节省磁盘空间。

3.3.4 其它段

除了上述说的几个常用段之外,ELF文件也会包含其他的段,用来保存与程序有关的其它信息。段的名字以“.”作为前缀,表示这些段的名字是系统保留的。应用程序可以使用一些非系统保留的名字作为段名,不能以“.”作为前缀。

原文地址:https://www.cnblogs.com/songshuguiyu/p/8447035.html

时间: 2024-10-10 22:38:48

《程序员的自我修养》读书笔记 -- 第三章的相关文章

C++程序员的自我修养–读书笔记

 1:注意不要反回指向栈内存的指针或引用,因为在函数返回时改内存已经被销毁了 2:C/C++没有办法知道指针所指的内存容量大小 当数组作为参数传递时,数组将退化成相同类型的指针 不要指望要指针参数去申请动态内存,因为函数会为产生一个临时变量指向参数的内存,当函数内分配内存时,将内存的地址赋给了临时参数,而没有给实参赋值,所有实参没有发生任何变化,应该修改的是指针所指的内容,而不是修改指针的指向,所有可以用指向指针的指针 3:重载和内联机制既可用于全局函数也可用于类的成员函数,const和vi

程序员的自我修养-读书笔记(2)

3 目标文件里有什么 3.1 目标文件格式 1)可执行文件:PE(Windows)  和 ELF(linux), 都是COFF格式的变种.目标文件(.obj, .o)采用一样的格式. 2)动态链接库:(windows的.dll,linux的.so) 3) 静态链接库:(.lib, .a) 3.2 目标文件是什么样的 1)分成数据段和代码段的好处:1.代码段设为只读,防止误写.2.提高缓存命中率(数据缓存和指令缓存).3.多个进程时,只有一份代码段节约内存. 3.3 挖掘 .o 1) objdum

程序员的自我修养-读书笔记(1)

1. 温故而知新 1.2万变不离其中 三个核心部件:CPU,内存,I/O控制芯片 北桥芯片(PCI 桥):协调CPU,内存,高速图形设备.PCI总线之后又有AGP, PCI express 南桥芯片(ISA 桥):协调USB,键盘,鼠标 1.4 不要让CPU打盹 分时系统:W95, mac os x之前的系统 多任务:unix,linux,win NT 2) Windows中,图形硬件抽象成了GDI, 多媒体设备抽象为DirectX, 磁盘为文件 3)硬盘的基本存储单位为扇区,比如,一个硬盘2个

程序员的自我修养 学习笔记(1)

本文源自在学习<程序员的自我修养>中的心得体会. 对于底层系统程序开发者来说,硬件平台可以抽象为三个主要部件,CPU.内存.I/O控制器. 早期的计算机没有复杂的图形功能,CPU和内存之间的频率差异不大,它们都是连接在同一个bus上面的.其他I/O设备,诸如显示设备.键盘.磁盘等速度比内存.CPU慢很多.为了IO设备与CPU.内存之间的协调通讯,一般每个IO设备商都有相应的IO控制器,早期的硬件结构图如下: 随着技术的进步,CPU的频率越来越高,内存跟不上CPU的速度,他们之间就需要一个转换机

程序员的自我修养 学习笔记(4)

可执行文件只有装载到内存以后才能被CPU执行.程序就将是菜谱,CPU就像是厨师,计算机的其他硬件就像是厨具,整个炒菜的过程就是一个进程.同样的一份菜谱,不同人可以做出来不同的味道.这个类比真是巧妙. Linux下面,进程最大使用3G的虚拟空间 Windows下面,进程最大使用2G的虚拟空间 现在计算机,配置超过4G的内存的电脑已经不是不可能了,在这种情况下,32位CPU能够访问到大于4G的空间吗?如果此空间指的是虚拟地址空间,由于32位CPU的指针只能是32位,最大寻址范围是0~4GB.如果此空

一、《程序员的自我修养》笔记-前言

引子:在linux上写了三年多的c了,平时遇到一些编译和链接的问题仍然很是头痛,感觉很无力,好基友推荐<程序员的自我修养>,趁着周末,速速围观. 先记录下作者在书中抛出来的问题 1.为啥程序是从main函数开始执行? 2.PE/ELF文件存的是啥? 3.如何写一个直接跑在未安装os裸机上的程序? 4.目标文件是啥?链接是啥? 5.链接为啥报错? 6.句柄到底是啥? 7.普通c/c++代码如何被编译成牧宝文件及程序在目标文件中如何存储? 8.目标文件如何被链接器链接到一起,并形成可执行文件? 9

程序员的自我修养 读书体会

就如标题一样,真本书可以说很棒,基本上覆盖了一个程序从编译-链接-装载-到消亡的所有过程.表达清晰完整,也非常的浅显易懂. 作者本身的理念和我的兴趣爱好和所追求的技术路线不谋而合:软件技术本身可以说是日新月异,但是都脱离不开操作系统的运筹帷幄,我们不能把程序本身生命周期当作是黑盒,亦或者是操作系统原理,编译原理书籍上的概念文字,而是要真正转化为程序员可以理解的代码.可以说这本书从这一点上做到了,也可以说增强了我们的DEBUG能力. 本书对我最大的帮助就是解答了操作系统内存管理的原因.链接部分的本

程序员的自我修养学习笔记

------------------------------------------------------------------------ 1.查看各种目标文件的结构和内容 objdump -h SimpleSection.o ------------------------------------------------------------------------ 2.查看ELF文件代码段.数据段.BSS(Block Stated by Symbol)段的长度 size Simple

读书笔记:程序员的自我修养-----第一章(综述)

题前:30--45天读完,一周至少3篇读书笔记.不能坚持,不再联系,不再找你. 一. hello world 程序引出的问题,看40天后,再回来看看自己的答案,提升多少. Q1:程序为什么要被编译器编译之后才可以运行?   A1 : 系统执行的机器语言,即二进制文件,程序是文本文件需要编译之后,由链接器链接需要的基本库生成二进制文件. Q2: 编译器在把C语言程序转换成可以执行的机器码的过程中作了什么,怎么做的?   A2: 预处理,汇编器生成汇编文件,编译器生成目标文件,链接器链接生成可执行文

【读书笔记】程序员的自我修养总结(六)

[读书笔记]程序员的自我修养总结(六) 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 说明:这是程序员的自我修养一书的读书总结,随着阅读的推进,逐步增加内容. 本文主要介绍可执行文件的装载与进程 程序与进程的区别 程序是静态的,指的是一些预先编译好的指令和数据集合的一个文件:而进程实际上就是运行着的程序,是动态的. 虚拟地址空间 程序运行起来后将拥有独立的虚拟地址空间 virtual address space,其大小由计算机的硬件平台决定,具体地说是