详述 从代码如何到可执行文件 的过程和解耦
Posted Fu_Lin_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详述 从代码如何到可执行文件 的过程和解耦相关的知识,希望对你有一定的参考价值。
文章目录
前言
本想写一下程序和进程之间,以及线程和进程之间的关联和使用操作,然后看了网上一些文章,觉得如果从0到1的记录,可能效果会更好,所以此篇博客将详细讲述一个可执行文件是如何形成的,它和代码之间的故事又是怎么样的,下面将详细揭露。
一段程序gemfield本文 可能对你的编码能力不会产生影响,也可能对提高你的社会地位起不到任何帮助,但世界上总有一些事务是为改变你的世界观而诞生的——就像gemfield本文《从代码到可执行文件》 。
gemfield.c 代码
正如题目所说的那样,我们得先从一段程序代码开始——gemfield.c。
又正如程序的名字所说的,这是个c语言文件,极其简单。
int main(int argc,char *argv[])
{
int a = 0;
char gemfield[32];
printf(“input gemfield’s blog: “);
scanf(“%s”,gemfield);
printf(“gemfield’s blog is %s\\n”,gemfield);
}
正如代码量所揭示的,这真的是个简单的c程序,但 gemfield 还是要强调一点——main函数书写的规范性。 从 gemfield.c 的代码可以看出:
- main函数是有返回值的,为int型,操作系统可以感知这个值。
- main函数的第一个参数 argc,是argument count的缩写,意思是参数数量;
- main函数的第二个参数 argv,是argument vector的缩写,意思是存放参数的vector。
下面的研究步骤展示了从程序(gemfield.c)代码到可执行文件(gemfield)的一步步变化。
第一步:编译。
gcc gemfield.c -o gemfield
可执行文件gemfield在GCC的帮助下诞生了。那么这个gemfield文件究竟是什么?
第二步:有关可执行文件。
objdump -f gemfield 输出:
gemfield: file format elf32-i386 architecture: i386,
flags 0×00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0x080483d0
上面的信息明确的告诉我们,gemfield文件是 elf 可执行文件格式(什么是elf可执行格式?看第四步!),起始地址是 0x080483d0(这个地址是什么含意?看第六步中的第三部分!)。先了解这么多吧,下文或许继续会有新发现。
第三步:什么是ELF格式?
ELF是 “Executable and Linking Format” 首字母的缩写,也就是上文中gemfield这个可执行文件的格式。这种格式主要用于UNIX家族中。对于本文的讨论,将关注ELF的三大部分:
-
ELF头
-
节(section)头表
-
程序头表
一个ELF文件(比如gemfield)是由若干个 Segment 构成的。有些 Segment 需要被装入、即被映射到用户空间,有些则不需要被装入(只有类型为 PT_LOAD 的 Segment 才需要被装入,参考第七部分)。所以,ELF文件被操作系统装入的过程只 “管” 到Segment为止。
而从ELF文件的动态连接、重定位(即浮动)、和启动运行的角度看,ELF文件是由若干个“节(Section)”构成的。我们通常所说映像中的“代码段”、“数据段”等等都是Section。所以,动态连接和启动运行的过程所涉及的则是Section。
一个Segment可以包含多个Section。其实,Segment和Section都是从操作/处理 的角度对ELF的划分;对于不同的操作/处理,划分的方式也就可以不同。
第四步:ELF头
我们首先关注ELF文件头格式,因为 ELF文件头(header)位于文件的最开始处,包含整个文件(如gemfield 可执行文件)的结构信息(使用二进制编辑器察看可执行文件的数据,gemfield 直接使用 Qt creator 就可以查看 gemfield 文件了):
typedef struct {
unsigned char e_ident[16]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address*/
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /*Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx;/* Section header string table index */
} Elf32_Ehdr;//52个字节
-
e_ident 数组,16个字节。 1、前4个字节被称为魔数,值是固定的,并且是:0x7f,’E’,’L’,’F’。表明这是个ELF文件; 2、第5个字节有三种值:0,非法目标文件;1,32位目标文件;2,64位目标文件。 3、第6个字节指定编码格式,有3种值:0,非法编码;1,LSB编码(小端格式);2,MSB编码(大端格式)。 4、第7个字节指定ELF头版本,值为1; 5、第8~16为保留字节,默认填充零; 可执行文件gemfield的此处值是:7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00。
-
e_type,2个字节,指明文件类型:
- 未知文件类型;
- 可重定位文件;
- 可执行文件;
- 动态链接库文件;
- Core 文件;0xff00,特定处理器文件扩展下边界 ;0xffff 特定处 理器文件扩展上边界 。
可执行文件gemfield的此处值是:02 00。
-
e_machine,2个字节,指明cpu架构:如intel的是3,其他的去CivilNet BBS上自行查找;可执行文件gemfield的此处值是:03 00。
-
e_version,4个字节,指明版本:0,非法版本号;1,当前版本号;可执行文件gemfield的此处值是:01 00 00 00。
-
e_entry,4个字节,程序入口的虚拟地址;可执行文件gemfield的此处值是:d0 83 04 08。
-
e_phoff,4个字节,程序头表在文件中的偏移量;可执行文件gemfield的此处值是:34 00 00 00。
-
e_shoff,4个字节,节头表的起始地址;可执行文件gemfield的此处值是:44 11 00 00。
-
e_flags,4个字节,处理器特定标志位,intel为0;可执行文件gemfield的此处值是:00 00 00 00。
-
e_ehsize,2个字节,ELF文件头大小(52字节);可执行文件gemfield的此处值是:34 00。
-
e_phentsize,2个字节,程序头表中每一个表项的大小;可执行文件gemfield的此处值是:20 00。
-
e_phnum,2个字节,程序头表中表项的数目;可执行文件gemfield的此处值是:09 00。
-
e_shentsize,2个字节,节头表中每一个表项的大小(40个字节);可执行文件gemfield的此处值是:28 00。
-
e_shnum,2个字节,节头表中的表项的数目;可执行文件gemfield的此处值是:1e 00。
-
e_shstrndx,2个字节,Section header string table index(节头字符串表的索引);可执行文件gemfield的此处值是:1b 00。
第五步、ELF文件中的节头表 节头表的作用是什么呢?
像gemfield 这样的可执行文件里,包含了多个节,其中每个节的信息都登记在这个节头表中(30个),用一个 Elf32_Shdr 结构体来描述:
typedef struct {
Elf32_Word sh_name; //本节的名字。
Elf32_Word sh_type; //类型。
Elf32_Word sh_flags ;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr; //刚好4×10=40个字节。
-
sh_name 存放的是个索引号,指向“字符串表”中的相对位置。在字符串中才有真正的以“\\0”结束的字符串。
-
sh_type 类型值:
SHT_NULL 0 //无效的节头 SHT_PROGBITS 1 //程序定义 SHT_SYMTAB 2 //完整的符号表,往往会包含很多在运行期间( 动态连接) 用不到的符号 SHT_STRTAB 3 //表明本节是字符串表 SHT_RELA 4 //重定位节 SHT_HASH 5 //符号哈希表 SHT_DYNAMIC 6 //本节包含的是动态连接信息 SHT_NOTE 7 //本节包含的信息用于以某种方式来标记本文件 SHT_NOBITS 8 //这一节的内容是空的 SHT_REL 9 //一个重定位节,含有带明确加数的重定位项 SHT_SHLIB 0xa //一个保留值,暂未指定语义 SHT_DYNSYM 0xb //本节是符号表,含有一个较小的符号表,专门用于动态连接 SHT_LOPROC 0×70000000 //为特殊处理器保留的节类型索引值的下边界 SHT_HIPROC 0x7fffffff //为特殊处理器保留的节类型索引值的上边界 SHT_LOUSER 0×80000000 //为应用程序保留节类型索引值的下边界 SHT_HIUSER 0xffffffff //为应用程序保留节类型索引值的下边界
-
sh_flags , 本节的一些属性,各个位定义了节的不同属性,当某种属性被设置时,相应的标志位被设为1,反之则设为0。未定义的标志位被全部置0。 类型值:
SHF_WRITE 0×1 //表示本节所包含的内容在进程运行过程中是可写的 SHF_ALLOC 0×2 //表示本节内容在进程运行过程中要占用内存单元 SHF_EXECINSTR 0×4 //表示此节内容是指令代码 SHF_MASHPROC 0xf0000000 //所有被此值所覆盖的位都是保留做特殊处理器扩展用的
-
sh_addr, 如果本节的内容需要映射到进程空间中去,此成员指定映射的起始地址;如果不需要映射,此值为0。
-
sh_offset, 指明了本节所在的位置,该值是节的第一个字节在文件中的位置,即相对于文件开头的偏移量。单位是字节。如果该节的类型为 SHT_NOBITS 的话,表明这一节的内容是空的,节并不占用实际 的空间,这时 sh_offset 只代表一个逻辑上的位置概念,并不代表实际的内容。
-
sh_size, 指明节的大小,单位是字节。如果该节的类型为 SHT_NOBITS,此值仍然可能为非零,但没有实际的意义。
-
sh_link, 此成员是一个索引值,指向节头表中本节所对应的位置。根据节的类型不同,本成员的意义也有所不同。
-
sh_info, 此成员含有此节的附加信息,根据节的类型不同,本成员的意义也有所不同。 对于某些节类型来说,sh_link 和sh_info 含有特殊的信息。
sh_type sh_link sh_info SHT_DYNAMIC //用于本节中项目的字符串表在节头表中相应的索引值 0 SHT_HASH //用于本节中哈希表的符号表在节头表中相应的索引值 0 SHT_REL/SHT_RELA //相应符号表在节头表中的索引值 本重定位节所应用到目标节在节头表中的索引值 SHT_SYMTAB/SHT_DYNSYM //相关字符串表的节头索引 符号表中最后一个本地符号的索引值加1 其它 SHN_UNDEF 0
-
sh_addralign, 此成员指明本节内容如何对齐字节,即该节的地址应该向多少个字节对齐。比如,在这个节中如果含有一个双字(doubleword) ,系统必须保证整个节的内容向双字对齐。也就是说,本节内容在进程空间中的映射地址sh_addr必须是一个向sh_addralign对齐,即能被 sh_addralign 整除的值。目前,sh_addralign 只能取0、1或者2 的正整数倍。如果该值为0 或1,表明本节没有字节对齐约束。
-
sh_entsize, 有一些节的内容是一张表,其中每一个表项的大小是固定的,比如符号表。对于这种表来说,本成员指定其每一个表项的大小。如果此值为0 ,则表明本节内容不是这种表格。
从e_shoff处我们知道这个节头表起始地址是:0×1144,表中有0x1e项(e_shnum),每一项大小40个字节(e_shentsize)。那么这个节头表的大小为30×40=1200个字节(0x4b0),因此,节头表的地址范围是:0×1144~0x15f3。数据摘录如下:
//空节 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 //.interp 1b 00 00 00 01 00 00 00 02 00 00 00 54 81 04 08 54 01 00 00 13 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 //.note.ABI-tag 23 00 00 00 07 00 00 00 02 00 00 00 68 81 04 08 68 01 00 00 20 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.note.gnu.build-id 31 00 00 00 07 00 00 00 02 00 00 00 88 81 04 08 88 01 00 00 24 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.gnu.hash 44 00 00 00 f6 ff ff 6f 02 00 00 00 ac 81 04 08 ac 01 00 00 20 00 00 00 05 00 00 00 00 00 00 00 04 00 00 00 04 00 00 00 //.dynsym 4e 00 00 00 0b 00 00 00 02 00 00 00 cc 81 04 08 cc 01 00 00 70 00 00 00 06 00 00 00 01 00 00 00 04 00 00 00 10 00 00 00 //.dynstr 56 00 00 00 03 00 00 00 02 00 00 00 3c 82 04 08 3c 02 00 00 80 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 //.gnu.version 5e 00 00 00 ff ff ff 6f 02 00 00 00 bc 82 04 08 bc 02 00 00 0e 00 00 00 05 00 00 00 00 00 00 00 02 00 00 00 02 00 00 00 //.gnu.version_r 6b 00 00 00 fe ff ff 6f 02 00 00 00 cc 82 04 08 cc 02 00 00 40 00 00 00 06 00 00 00 01 00 00 00 04 00 00 00 00 00 00 00 //.rel.dyn 7a 00 00 00 09 00 00 00 02 00 00 00 0c 83 04 08 0c 03 00 00 08 00 00 00 05 00 00 00 00 00 00 00 04 00 00 00 08 00 00 00 //.rel.plt 83 00 00 00 09 00 00 00 02 00 00 00 14 83 04 08 14 03 00 00 28 00 00 00 05 00 00 00 0c 00 00 00 04 00 00 00 08 00 00 00 //.init 8c 00 00 00 01 00 00 00 06 00 00 00 3c 83 04 08 3c 03 00 00 2e 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 .plt 87 00 00 00 01 00 00 00 06 00 00 00 70 83 04 08 70 03 00 00 60 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 04 00 00 00 //.text 92 00 00 00 01 00 00 00 06 00 00 00 d0 83 04 08 d0 03 00 00 dc 01 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 //.fini 98 00 00 00 01 00 00 00 06 00 00 00 ac 85 04 08 ac 05 00 00 1a 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.rodata 9e 00 00 00 01 00 00 00 02 00 00 00 c8 85 04 08 c8 05 00 00 3b 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.eh_frame_hdr a6 00 00 00 01 00 00 00 02 00 00 00 04 86 04 08 04 06 00 00 34 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.eh_frame b4 00 00 00 01 00 00 00 02 00 00 00 38 86 04 08 38 06 00 00 c4 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.ctors be 00 00 00 01 00 00 00 03 00 00 00 14 9f 04 08 14 0f 00 00 08 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.dtors c5 00 00 00 01 00 00 00 03 00 00 00 1c 9f 04 08 1c 0f 00 00 08 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.jcr cc 00 00 00 01 00 00 00 03 00 00 00 24 9f 04 08 24 0f 00 00 04 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.dynamic d1 00 00 00 06 00 00 00 03 00 00 00 28 9f 04 08 28 0f 00 00 c8 00 00 00 06 00 00 00 00 00 00 00 04 00 00 00 08 00 00 00 //.got da 00 00 00 01 00 00 00 03 00 00 00 f0 9f 04 08 f0 0f 00 00 04 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 04 00 00 00 //.got.plt df 00 00 00 01 00 00 00 03 00 00 00 f4 9f 04 08 f4 0f 00 00 20 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 04 00 00 00 //.data e8 00 00 00 01 00 00 00 03 00 00 00 14 a0 04 08 14 10 00 00 08 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.bss ee 00 00 00 08 00 00 00 03 00 00 00 1c a0 04 08 1c 10 00 00 08 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 //.comment f3 00 00 00 01 00 00 00 30 00 00 00 00 00 00 00 1c 10 00 00 2a 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 //.shstrtab 11 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 46 10 00 00 fc 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 //.symtab 01 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 f4 15 00 00 30 04 00 00 1d 00 00 00 2d 00 00 00 04 00 00 00 10 00 00 00 //.strtab 09 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 24 1a 00 00 32 02 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
第六步、摘选几个节来研究下
那我们就摘选出4个关键的节来探讨一下:节头字符串表(节)、字符串表(节)、代码节、符号表(节)。
第一部分、节头字符串表(节,.shstrtab)
一个明显的疑问就是:一个可执行文件(gemfield)中可能会有多个字符串节区,怎么确定哪个就是和sh_name 这个索引相关的字符串表呢(也就是节名字表(.shstrtab))?谜底就在本文开始介绍 的第一个结构体中。e_shstrndx代表了该字符串表的索引,该值为:0x1b。上面不是说gemfield中一共有0x1e个节区吗,那么这个特殊的字符串表就是第0x1b个,起始地址就应该是:0×1144+ (0x1b)*0×28 = 0×1554。从gemfield中把这它复制出来,如下:
11 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 46 10 00 00 fc 00 00 00 00
00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
从中我们可以看出,起始地址(偏移)是0×1046,大小是0xfc,也就是这个特定的字符串表的地址范围是:0×1046 ~ 0×1141
我们再从gemfield文件中把0×1046 ~ 0×1141处的数据拿出来,如下:
00 2e 73 79 6d 74 61 62 00 2e 73 74 72 74 61 62 00 2e 73 68 73 74 72 74 61 62 00 2e 69 6e
74 65 72 70 00 2e 6e 6f 74 65 2e 41 42 49 2d 74 61 67 00 2e 6e 6f 74 65 2e 67 6e 75 2e
62 75 69 6c 64 2d 69 64 00 2e 67 6e 75 2e 68 61 73 68 00 2e 64 79 6e 73 79 6d 00 2e 64 79
6e 73 74 72 00 2e 67 6e 75 2e 76 65 72 73 69 6f 6e 00 2e 67 6e 75 2e 76 65 72 73 69 6f
6e 5f 72 00 2e 72 65 6c 2e 64 79 6e 00 2e 72 65 6c 2e 70 6c 74 00 2e 69 6e 69 74 00 2e 74
65 78 74 00 2e 66 69 6e 69 00 2e 72 6f 64 61 74 61 00 2e 65 68 5f 66 72 61 6d 65 5f 68
64 72 00 2e 65 68 5f 66 72 61 6d 65 00 2e 63 74 6f 72 73 00 2e 64 74 6f 72 73 00 2e 6a 63
72 00 2e 64 79 6e 61 6d 69 63 00 2e 67 6f 74 00 2e 67 6f 74 2e 70 6c 74 00 2e 64 61 74
61 00 2e 62 73 73 00 2e 63 6f 6d 6d 65 6e 74 00
既然上面的是字符串表,那么我再把它翻译成ASCII字符的形式:
symtab .strtab .shstrtab .interp .note.ABI-tag .note.gnu.build-id .gnu.hash
.dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .text .fini
.rodata .eh_frame_hdr .eh_frame .ctors .dtors .jcr .dynamic .got .got.plt .data
.bss .comment
从这些熟悉的名字中,我们已经知道了 gemfield 中包含的各种各样的节区的名字了。这些节区的属性如下:
名字 | 类型 | 属性 |
---|---|---|
.bss | SHT_NOBITS | SHF_ALLOC+SHF_WRITE |
.comment | SHT_PROGBITS | 无 |
.data | SHT_PROGBITS | SHF_ALLOC+SHF_WRITE |
.data1 | SHT_PROGBITS | SHF_ALLOC+SHF_WRITE |
.debug | SHT_PROGBITS | 无 |
.dynamic | SHT_DYNAMIC | 见下文 |
.dynstr | SHT_STRTAB | SHF_ALLOC |
.dynsym | SHT_DYNSYM | SHF_ALLOC |
.fini | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR |
.got | SHT_PROGBITS | SHF_ALLOC + SHF_WRITE |
.hash | SHT_HASH | SHF_ALLOC |
.init | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR |
.interp | SHT_PROGBITS | 见下文 |
.line | SHT_PROGBITS | 无 |
.note | SHT_NOTE | 无 |
.plt | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR |
.relname | SHT_REL | 见下文 |
.relaname | SHT_RELA | 见下文 |
.rodata | SHT_PROGBITS | SHF_ALLOC |
.rodata1 | SHT_PROGBITS | SHF_ALLOC |
.shstrtab | SHT_STRTAB | 无 |
.strtab | SHT_STRTAB | 见下文 |
.symtab | SHT_SYMTAB | 见下文 |
.text | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR |
- .bss 本节中包含目标文件中未初始化的全局变量。一般情况下,可执行程序在开始运行的时候,系统会把这一段内容清零。但是,在运行期间的bss段是由系统初始化而成的,在目标文件中.bss 节并不包含任何内容,其长度为0,所以它的节类型为SHT_NOBITS
- .comment 本节包含版本控制信息。
- .data/.data1 这两个节用于存放程序中被初始化过的全局变量。在目标文件中,它们是占用实际的存储空间的,与.bss 节不同。
- .debug 本节中含有调试信息,内容格式没有统一规定。所有以 ”.debug” 为前缀的节名字都是保留的。
- .dynamic 本节包含动态连接信息,并且可能有SHF_ALLOC 和SHF_WRITE 等属性。是否具有SHF_WRITE 属性取决于操作系统和处理器。
- .dynstr 此节含有用于动态连接的字符串,一般是那些与符号表相关的名字。
- .dynsym 此节含有动态连接符号表。
- .fini 此节包含进程终止时要执行的程序指令。当程序正常退出时,系统会执行这一节中的代码。
- .got 此节包含全局偏移量表。
- .hash 本节包含一张符号哈希表。
- .init 此节包含进程初始化时要执行的程序指令。当程序开始运行时,系统会在进入主函数之前执行这一节中的代码。
- .interp 此节含有ELF 程序解析器的路径名。如果此节被包含在某个可装载的段中,那么本节的属性中应置SHF_ALLOC 标志位,否则不置此标志。
- .line 本节也是一个用于调试的节,它包含那些调试符号的行号,为程序指令码与源文件的行号建立起联系。其内容格式没有统一规定。
- .note 注释节(note section)。
- .plt 此节包含函数连接表。
- .relname 和.relaname 这两个节含有重定位信息。如果此节被包含在某个可装载的段中,那么本节的属性中应置 SHF_ALLOC 标志位,否则不置此标志。注意,这两个节的名字中”name”是可替换的部分,执照惯例 ,对哪一节做重定位就把”name”换成哪一节的名字。比如,.text 节的重定位节的名字将是.rel.text 或.rela.text 。
- .rodata/.rodata1 本节包含程序中的只读数据,在程序装载时,它们一般会被装入进程空间中那些只读的段中去。
- .shstrtab 本节是“节名字表”,含有所有其它节的名字。
- .strtab 本节用于存放字符串,主要是那些符号表项的名字。如果一个目标文件有一个可装载的段,并且其中含有符号表,那么本节的属性中应该有SHF_ALLOC 。
- .symtab 本节用于存放符号表。如果一个目标文件有一个可载入的段,并且其中含有符号表,那么本节的属性中应该有SHF_ALLOC 。
- .text 本节包含程序指令代码。
以点号 ”.” 为前缀的节名字是为系统保留的。应用程序也可以构造自己的段,但最好不要取与上述系统已定义的节相同的名字,也不要取以点号开头的名字,以避免潜在的冲突。注意,目标文件中节的名字并不具有唯一性,可以存在多个相同名字的节。
第二部分、字符串表(节,.strtab)
09 00 00 00 //.strtab
03 00 00 00 //3 ,表明本节是字符串表
00 00 00 00 //标志
00 00 00 00 //还没重定位,所以为0
24 1a 00 00 //起始地址:0x1a24
32 02 00 00 //大小 0×232
00 00 00 00
00 00 00 00
01 00 00 00 //对齐
00 00 00 00 //指向的不是表
根据上面的解析,把字符串表拿出来,地址为0x1a24~0x1c55:
00 63 72 74 73 74 75 66 66 2e 63 00 5f 5f 43 54 4f 52 5f 4c 49 53 54 5f 5f 00 5f 5f 44
54 4f 52 5f 4c 49 53 54 5f 5f 00 5f 5f 4a 43 52 5f 4c 49 53 54 5f 5f 00 5f 5f 64 6f 5f
67 6c 6f 62 61 6c 5f 64 74 6f 72 73 5f 61 75 78 00 63 6f 6d 70 6c 65 74 65 64 2e 36 30
38 36 00 64 74 6f 72 5f 69 64 78 2e 36 30 38 38 00 66 72 61 6d 65 5f 64 75 6d 6d 79 00
5f 5f 43 54 4f 52 5f 45 4e 44 5f 5f 00 5f 5f 46 52 41 4d 45 5f 45 4e 44 5f 5f 00 5f 5f
4a 43 52 5f 45 4e 44 5f 5f 00 5f 5f 64 6f 5f 67 6c 6f 62 61 6c 5f 63 74 6f 72 73 5f 61
75 78 00 6c 65 61 66 2e 63 00 5f 5f 69 6e 69 74 5f 61 72 72 61 79 5f 65 6e 64 00 5f 44
59 4e 41 4d 49 43 00 5f 5f 69 6e 69 74 5f 61 72 72 61 79 5f 73 74 61 72 74 00 5f 47 4c
4f 42 41 4c 5f 4f 46 46 53 45 54 5f 54 41 42 4c 45 5f 00 5f 5f 6c 69 62 63 5f 63 73 75
5f 66 69 6e 69 00 5f 5f 69 36 38 36 2e 67 65 74 5f 70 63 5f 74 68 75 6e 6b 2e 62 78 00
64 61 74 61 5f 73 74 61 72 74 00 70 72 69 6e 74 66 40 40 47 4c 49 42 43 5f 32 2e 30 00
5f 65 64 61 74 61 00 5f 66 69 6e 69 00 5f 5f 73 74 61 63 6b 5f 63 68 6b 5f 66 61 69 6c
40 40 47 4c 49 42 43 5f 32 2e 34 00 5f 5f 44 54 4f 52 5f 45 4e 44 5f 5f 00 5f 5f 64 61
74 61 5f 73 74 61 72 74 00 5f 5f 67 6d 6f 6e 5f 73 74 61 72 以上是关于详述 从代码如何到可执行文件 的过程和解耦的主要内容,如果未能解决你的问题,请参考以下文章