小师妹问沃什么是可执行程序?(全网最简洁,必看,错过后悔终生!!!)

news/2024/12/28 12:41:09/

        相信很多同学知道Windows下的可执行程序是*.exe,但是除了*.exe之外,Windows下动态库*.dll和*.lib也是可执行程序。不过,小师妹问沃的是什么是Linux下的可执行程序?

        为了不让小师妹伤心,师兄沃再次拿起《程序员的自我修养》第三章肝了一晚上,呕心沥血才完美回答出小师妹的问题,回答让小师妹十分满意,对师兄好感爆棚。好了,吹牛到此为止,正经脸(咳咳咳  ^_^),接下来让我们来看看师兄回答了什么,让小师妹万分满意?

一、目标文件格式

        现在PC平台流行的可执行文件格式(executable)主要是Windows下的PE(Portable Executable)和Linux下的ELF(Executable Linkable Format),它们都是COFF(Common file format)格式的变种。其中,Windows下可执行文件还包括动态链接库(DLL,Dynamic Linking Library)和静态链接库(lib,Static Linking Library),Linux下动态链接库(*.so)和静态链接库(*.a)以及目标文件object(*.o)。在Windows下可执行文件按PE-COFF格式存储,Linux下按照ELF格式存储。ELF可以理解为是一系列的目标文件或者加上链接库组合而成的文件包。下面是ELF文件类型和说明。

表格1-1 ELF文件类型和说明
ELF文件类型说明实例

可重定位文件

(Relocatable File)

这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也可以归类为这一类

Linux的*.o文件

Windows的*.obj文件

可执行文件

(Executable File)

这类文件包含了可以直接执行的程序,它的代表就是ELF可执行文件,它们一般都没有扩展名

比如/bin/bash文件

Windows的*.exe文件

共享目标文件

(Shared Object File)

这类文件包含了代码和数据,可以在一下两种情况使用,

一种是连接器可以使用这种文件跟其它的可重定位文件和共享目标文件链接,产生新的目标文件;

另一种是动态链接器可以将几个这种共享目标文件与可执行文件结合,作为进程映像的一部分来运行。

Linux的*.so,如/lib/glibc-2.5.so

Windows的DLL

核心转存储文件

(Core Dump File)

当进程意外中职生时,系统可以将该进程的地址空间的内容及终止时的一些其他信息转存储到核心转存储文件Linux下的Core dump
  • 小知识:Unix的可执行程序是*.out;COFF主要贡献在目标文件引入“段”机制。

二、目标文件结构

2.1样例代码

我们先给小师妹上一段经典代码,惊呆小师妹一脸。

(注:文本分析的是ubuntu20.04 x86 64位系统下的ELF文件)

#include <iostream>int globalVar1 = 100;
int globalVar2;void fun1(int var)
{std::cout<<var<<std::endl;
}struct St
{St() : var(200) {}~St() {}int getVar() const {return var;}private:int var;
};int main(int arc, char* argv[]) {static int staticVar1 = 666;static int staticVar2;int var1 = 555;int var2;fun1(staticVar1 + staticVar1 + var1 + var2);return 0;
}

2.2生成可执行文件

编译成main.o文件命令如下:(-c表示只编译不链接)

g++ -c main.cpp -o main.o

编译成main文件命令如下:

g++ main.cpp -o main

2.3分析main.o文件

Linux下查看和操作目标文件许多工具,比如hexdump、objdum、objcopy、readelf,本文主要介绍ELF的文件结构,故主要用objdump工具足够,具体文件结构里面每个部分将在下一篇文章结合源码详细分析。

查看main.o内部结构命令

objdump -h main.o
图1-1  main.o文件结构

         通过图1-1可知,可执行文件结构主要分为几大部分“.text段”、“.data段”、“.bss段”、“.rodata段”、“其它信息段”,即如下结构图:

结构图1-1  main.o内存分布图

        由结构图1-1可知,可知执行文件main.o主要结构分为文件头header、.text段、.data段、.bss段、其它信息段(.rodata段、.init_arrary段、.comment段、other段等)。其中左侧红色数据位对应“段”的大小,右边蓝色数字表示文件偏移地址。

ELF header:ELF文件头,定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、ELF重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口地址和长度、段表的位置和长度及段的数量等;(具体代码定义参考Linux系统库“/usr/inlcude//elf.h”)

  • .text:代码段,存放程序运行时相关需要的代码
  • .data:数据段,保存已经初始化了的全局变量、全局静态变量、局部静态变量,比如globalVar1和staticVar1,一共8字节
  • .bss:存放的是未初始化的全局变量、全局静态变量、局部静态变量,比如globalVar2和staticVar2,var2一共12(0x0c)字节
  • .rodata:只读数据段,由于cout::endl本质是一个"\n"常量字符,大小为1,故带只读数据段大小只有1字节
  • .init_arrary:初始化数组段,有类似ctor效果
  • .comment:注释段,一般存放注释代码文本段
  • .other:其它段包含符号表、gnu版本号等等

2.4段.text、.data和.rodata、.bss段分析

2.4.1代码段.text分析

objdum中的-s可以显示所有段十六进制数,-d可以将所有段包含的指令反汇编,下面敲入命令查看代码如下:

objdump -s -d main.o

显示结果:

tjq@ubuntu:~/eclipse-workspace/Test1/src$ objdump -s -d main.omain.o:     文件格式 elf64-x86-64Contents of section .text:0000 f30f1efa 554889e5 4883ec10 897dfc8b  ....UH..H....}..0010 45fc89c6 488d3d00 000000e8 00000000  E...H.=.........0020 4889c248 8b050000 00004889 c64889d7  H..H......H..H..0030 e8000000 0090c9c3 f30f1efa 554889e5  ............UH..0040 4883ec20 897dec48 8975e0c7 45f82b02  H.. .}.H.u..E.+.0050 00008b05 00000000 8d14008b 45f801c2  ............E...0060 8b45fc01 d089c7e8 00000000 b8000000  .E..............0070 00c9c3f3 0f1efa55 4889e548 83ec1089  .......UH..H....0080 7dfc8975 f8837dfc 01753281 7df8ffff  }..u..}..u2.}...0090 00007529 488d3d00 000000e8 00000000  ..u)H.=.........00a0 488d1500 00000048 8d350000 0000488b  H......H.5....H.00b0 05000000 004889c7 e8000000 0090c9c3  .....H..........00c0 f30f1efa 554889e5 beffff00 00bf0100  ....UH..........00d0 0000e89c ffffff5d c3                 .......].       
Contents of section .data:0000 64000000 9a020000                    d.......        
Contents of section .rodata:0000 00                                   .               
Contents of section .init_array:0000 00000000 00000000                    ........        
Contents of section .comment:0000 00474343 3a202855 62756e74 7520392e  .GCC: (Ubuntu 9.0010 332e302d 31377562 756e7475 317e3230  3.0-17ubuntu1~200020 2e303429 20392e33 2e3000             .04) 9.3.0.     
Contents of section .note.gnu.property:0000 04000000 10000000 05000000 474e5500  ............GNU.0010 020000c0 04000000 03000000 00000000  ................
Contents of section .eh_frame:0000 14000000 00000000 017a5200 01781001  .........zR..x..0010 1b0c0708 90010000 1c000000 1c000000  ................0020 00000000 38000000 00450e10 8602430d  ....8....E....C.0030 066f0c07 08000000 1c000000 3c000000  .o..........<...0040 00000000 3b000000 00450e10 8602430d  ....;....E....C.0050 06720c07 08000000 1c000000 5c000000  .r..........\...0060 00000000 4d000000 00450e10 8602430d  ....M....E....C.0070 0602440c 07080000 1c000000 7c000000  ..D.........|...0080 00000000 19000000 00450e10 8602430d  .........E....C.0090 06500c07 08000000                    .P......        Disassembly of section .text:0000000000000000 <_Z4fun1i>:0:	f3 0f 1e fa          	endbr64 4:	55                   	push   %rbp5:	48 89 e5             	mov    %rsp,%rbp8:	48 83 ec 10          	sub    $0x10,%rspc:	89 7d fc             	mov    %edi,-0x4(%rbp)f:	8b 45 fc             	mov    -0x4(%rbp),%eax12:	89 c6                	mov    %eax,%esi14:	48 8d 3d 00 00 00 00 	lea    0x0(%rip),%rdi        # 1b <_Z4fun1i+0x1b>1b:	e8 00 00 00 00       	callq  20 <_Z4fun1i+0x20>20:	48 89 c2             	mov    %rax,%rdx23:	48 8b 05 00 00 00 00 	mov    0x0(%rip),%rax        # 2a <_Z4fun1i+0x2a>2a:	48 89 c6             	mov    %rax,%rsi2d:	48 89 d7             	mov    %rdx,%rdi30:	e8 00 00 00 00       	callq  35 <_Z4fun1i+0x35>35:	90                   	nop36:	c9                   	leaveq 37:	c3                   	retq   0000000000000038 <main>:38:	f3 0f 1e fa          	endbr64 3c:	55                   	push   %rbp3d:	48 89 e5             	mov    %rsp,%rbp40:	48 83 ec 20          	sub    $0x20,%rsp44:	89 7d ec             	mov    %edi,-0x14(%rbp)47:	48 89 75 e0          	mov    %rsi,-0x20(%rbp)4b:	c7 45 f8 2b 02 00 00 	movl   $0x22b,-0x8(%rbp)52:	8b 05 00 00 00 00    	mov    0x0(%rip),%eax        # 58 <main+0x20>58:	8d 14 00             	lea    (%rax,%rax,1),%edx5b:	8b 45 f8             	mov    -0x8(%rbp),%eax5e:	01 c2                	add    %eax,%edx60:	8b 45 fc             	mov    -0x4(%rbp),%eax63:	01 d0                	add    %edx,%eax65:	89 c7                	mov    %eax,%edi67:	e8 00 00 00 00       	callq  6c <main+0x34>6c:	b8 00 00 00 00       	mov    $0x0,%eax71:	c9                   	leaveq 72:	c3                   	retq   0000000000000073 <_Z41__static_initialization_and_destruction_0ii>:73:	f3 0f 1e fa          	endbr64 77:	55                   	push   %rbp78:	48 89 e5             	mov    %rsp,%rbp7b:	48 83 ec 10          	sub    $0x10,%rsp7f:	89 7d fc             	mov    %edi,-0x4(%rbp)82:	89 75 f8             	mov    %esi,-0x8(%rbp)85:	83 7d fc 01          	cmpl   $0x1,-0x4(%rbp)89:	75 32                	jne    bd <_Z41__static_initialization_and_destruction_0ii+0x4a>8b:	81 7d f8 ff ff 00 00 	cmpl   $0xffff,-0x8(%rbp)92:	75 29                	jne    bd <_Z41__static_initialization_and_destruction_0ii+0x4a>94:	48 8d 3d 00 00 00 00 	lea    0x0(%rip),%rdi        # 9b <_Z41__static_initialization_and_destruction_0ii+0x28>9b:	e8 00 00 00 00       	callq  a0 <_Z41__static_initialization_and_destruction_0ii+0x2d>a0:	48 8d 15 00 00 00 00 	lea    0x0(%rip),%rdx        # a7 <_Z41__static_initialization_and_destruction_0ii+0x34>a7:	48 8d 35 00 00 00 00 	lea    0x0(%rip),%rsi        # ae <_Z41__static_initialization_and_destruction_0ii+0x3b>ae:	48 8b 05 00 00 00 00 	mov    0x0(%rip),%rax        # b5 <_Z41__static_initialization_and_destruction_0ii+0x42>b5:	48 89 c7             	mov    %rax,%rdib8:	e8 00 00 00 00       	callq  bd <_Z41__static_initialization_and_destruction_0ii+0x4a>bd:	90                   	nopbe:	c9                   	leaveq bf:	c3                   	retq   00000000000000c0 <_GLOBAL__sub_I_globalVar1>:c0:	f3 0f 1e fa          	endbr64 c4:	55                   	push   %rbpc5:	48 89 e5             	mov    %rsp,%rbpc8:	be ff ff 00 00       	mov    $0xffff,%esicd:	bf 01 00 00 00       	mov    $0x1,%edid2:	e8 9c ff ff ff       	callq  73 <_Z41__static_initialization_and_destruction_0ii>d7:	5d                   	pop    %rbpd8:	c3                   	retq 

由上面反汇编代码可知,代码段包含了全局函数void fun1(),主函数main,类代码指令、静态变量globalVar1;.text代码段的前四个字节是f3 0f 1e fa;函数fun1和main的第一条指令为push %rbp,该两函数最后返回指令为retq。

2.4.2.数据段data和只读数据段.rodata分析

执行命令查看数据段指令和反汇编代码

objdump -x -s -d main.o

输出结果:

......
Idx Name          Size      VMA               LMA               File off  Algn0 .text         000000d9  0000000000000000  0000000000000000  00000040  2**0CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data         00000008  0000000000000000  0000000000000000  0000011c  2**2CONTENTS, ALLOC, LOAD, DATA2 .bss          0000000c  0000000000000000  0000000000000000  00000124  2**2ALLOC3 .rodata       00000001  0000000000000000  0000000000000000  00000124  2**0CONTENTS, ALLOC, LOAD, READONLY, DATA4 .init_array   00000008  0000000000000000  0000000000000000  00000128  2**3CONTENTS, ALLOC, LOAD, RELOC, DATA
......     
Contents of section .data:0000 64000000 9a020000                    d.......        
Contents of section .rodata:0000 00                                   .               
Contents of section .init_array:0000 00000000 00000000                    ........        
Contents of section .comment:0000 00474343 3a202855 62756e74 7520392e  .GCC: (Ubuntu 9.0010 332e302d 31377562 756e7475 317e3230  3.0-17ubuntu1~200020 2e303429 20392e33 2e3000             .04) 9.3.0.  
......

从上面代码可以看出.data数据段有两个已经初始化了全局变量值0x00000064、0x0000029a,这两个十六进制数转化为十进制数分别是100和666,刚好和全局变量globalVar1=100和staticVar1=666对的上。

2.4.3未初始化数据段.bss分析

还是通过敲下面命令查看.bss段的十六进制数和反汇编代码

objdump -x -s -d main.o

输出结果显示:

......
Idx Name          Size      VMA               LMA               File off  Algn0 .text         000000d9  0000000000000000  0000000000000000  00000040  2**0CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data         00000008  0000000000000000  0000000000000000  0000011c  2**2CONTENTS, ALLOC, LOAD, DATA2 .bss          0000000c  0000000000000000  0000000000000000  00000124  2**2ALLOC
......
SYMBOL TABLE:
0000000000000000 l    df *ABS*	0000000000000000 main.cpp
0000000000000000 l    d  .text	0000000000000000 .text
0000000000000000 l    d  .data	0000000000000000 .data
0000000000000000 l    d  .bss	0000000000000000 .bss  
0000000000000000 l    d  .rodata	0000000000000000 .rodata
0000000000000000 l     O .rodata	0000000000000001 _ZStL19piecewise_construct
0000000000000004 l     O .bss	0000000000000001 _ZStL8__ioinit
0000000000000004 l     O .data	0000000000000004 _ZZ4mainE10staticVar1
0000000000000008 l     O .bss	0000000000000004 _ZZ4mainE10staticVar2
0000000000000073 l     F .text	000000000000004d _Z41__static_initialization_and_destruction_0ii
00000000000000c0 l     F .text	0000000000000019 _GLOBAL__sub_I_globalVar1
0000000000000000 l    d  .init_array	0000000000000000 .init_array
0000000000000000 l    d  .note.GNU-stack	0000000000000000 .note.GNU-stack
0000000000000000 l    d  .note.gnu.property	0000000000000000 .note.gnu.property
0000000000000000 l    d  .eh_frame	0000000000000000 .eh_frame
0000000000000000 l    d  .comment	0000000000000000 .comment
0000000000000000 g     O .data	0000000000000004 globalVar1
0000000000000000 g     O .bss	0000000000000004 globalVar2
0000000000000000 g     F .text	0000000000000038 _Z4fun1i
0000000000000000         *UND*	0000000000000000 _ZSt4cout
0000000000000000         *UND*	0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000         *UND*	0000000000000000 _ZNSolsEi
0000000000000000         *UND*	0000000000000000 _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
0000000000000000         *UND*	0000000000000000 _ZNSolsEPFRSoS_E
0000000000000038 g     F .text	000000000000003b main
0000000000000000         *UND*	0000000000000000 _ZNSt8ios_base4InitC1Ev
0000000000000000         *UND*	0000000000000000 .hidden __dso_handle
0000000000000000         *UND*	0000000000000000 _ZNSt8ios_base4InitD1Ev
0000000000000000         *UND*	0000000000000000 __cxa_atexit
......

由上面的符号表可知,未初始化的全局变量globalVar2、局部静态变量staticVar2、类St都在.bss段,而非静态局部变量var2不在.bss段里面,var2非静态局部变量应该在栈内存中,此处没有体现。

2.5总结

ELF文件的结构主要为ELF文件头、代码段、数据段(.data和rodata)、BSS段与程序运行密切相关的其它段,同时还可以自定义段,比如.mp3,通过objcopy工具将该段插入ELF数据段中。

看到这里,小师妹对师兄的回答非常满意,一双勾魂迷妹眼让师兄心肝砰砰乱跳,血流加速,此时师妹似乎下定了什么决心,羞红着脸颊,决定最后问师兄一个问题:“请问ELF头的内容是什么?段又是怎么定义的呢?”

师兄听完后,决定今夜不眠不休,爆了几个肝,也要为小师妹答疑解惑!好了,具体结果,请听下回分解。哈哈哈~~(^_^)

三、参考文献

《程序员的自我修养——编译、装载与库》俞甲子 石凡 潘爱民 著, 电子工业出版社,  page:55-95


http://www.ppmy.cn/news/317368.html

相关文章

C++全局对象的构造与析构

C全局对象构造与析构 先上代码&#xff1a; //simpleClass.cppclass A{int val; public:A(int n){val n;}~A(){val 0;} };A a1(1);int main(){A a2(2);return 0; }抛出问题&#xff1a;局部对象a2和全局对象a1分别在何处调用构造函数和析构函数&#xff1f; 将上述cpp文件…

PyTorch实战(1):矩阵运算

常见的矩阵运算&#xff1a;基于pytorch和numpy实现 import torch import numpy as np import torch.nn as nnX torch.rand(4, 2, 3) linear nn.Linear(3, 2)"""矩阵加法""" # ZWX W torch.randn(4, 2, 3)Z11 W X print(Z11) Z12 W.add(X…

gcc汇编释义(1)

原始c代码 #include <iostream>int func(int m) {return m5; }int main() {int s func(10);return 0; }使用g生成的汇编代码 .file "1.cpp".text.local _ZStL8__ioinit.comm _ZStL8__ioinit,1,1.globl _Z4funci.type _Z4funci, function _Z4funci: .…

Matplotlib复习(1)——绘制三角函数曲线、正态分布曲线、圆锥曲线、极坐标方程(心形线、玫瑰线、阿基米德螺线)、3D图(球、马鞍面)

文章目录 0 前置1 基础API——绘制三角函数曲线2 图例、注释、文本——绘制正态分布曲线3 轮廓——绘制圆锥曲线4 绘制极坐标方程&#xff08;心形线、玫瑰线、阿基米德螺线&#xff09;5 3D图&#xff08;球、马鞍面&#xff09; 0 前置 import numpy as np import matplotli…

多弹协同攻击时的无源定位(一)

题目 采用被动接收方式的无源探测定位技术具有作用距离远、隐蔽接 收、不易被敌方发觉等优点&#xff0c;能有效提高探测系统在电子战环境下的 生存能力和作战能力。 在无源定位的研究中&#xff0c;测向定位技术&#xff08;Direction of Arrival&#xff0c;DOA&#xff09; …

Linux 命令(59)—— c++filt 命令

文章目录 1.命令简介2.命令格式3.选项说明4.常用示例参考文献 1.命令简介 cfilt 命令可用于解析 C 和 Java 中被修饰的符号&#xff0c;比如变量与函数名称。 我们知道&#xff0c; 在 C 和 Java 中&#xff0c; 允许函数重载&#xff0c;也就是说我们可以写出多个同名但参数…

Linux 命令(57)—— objdump 命令

文章目录 1.功能简介2.命令格式3.选项说明4.常用示例参考文献 1.功能简介 objdump 命令是 GNU Binutils 二进制工具集的一员&#xff0c;用于查看目标文件或可执行文件的组成信息&#xff0c;以可读的形式打印二进制文件的内容。 2.命令格式 objdump [OPTIONS] OBJFILES3.选…

代码的编译原理,以Linux系统为例

程序编译分为预编译、编译、汇编和链接四个阶段。在Windows操作系统中&#xff0c;编译工具用的是集成的开发环境&#xff0c;在Linux系统中没有很好的继承开发环境&#xff0c;用的是gcc编译器或者g&#xff0c;gcc用于C语言代码的编译&#xff0c;g用在C的编译过程中。在Linu…