链接¶
C 语言程序内存结构
-
gcc –E hello.c –o hello.i
-
gcc –S hello.i –o hello.s
-
as hello.s -o hello.o
预处理(Prepressing)¶
- 处理
#define
,#include
- 处理
#if
,#ifdef
,#elif
,#else
,endif
. - 删除注释
- 添加行号,便于调试产生相关信息
- 保留
#pragma
目标文件¶
ELF Header
描述整个文件的基本属性.text
代码段.data
数据段- .bss段 为未初始化的全局变量和局部静态变量预留位置,它并没有内容,所以它在文件中不占据空间。
Section Header Table
binutils使用方法¶
gcc -c
只编译不链接
objdump -h
查看ELF的基本信息
-x
查看ELF的全部信息
-s
将段中的内容以十六进制打印出来
-d
将包含指令的段反汇编
readelf -a
查看ELF的全部信息
-h
查看ELF的 header
-S
查看所有的段表信息
-s
查看文件的符号
size
查看ELF各个段长度
nm
查看ELF文件的符号
代码
/*
* SimpleSection.c
*
* Linux:
* gcc -c SimpleSection.c
*
* Windows:
* cl SimpleSection.c /c /Za
*/
int printf(const char *format, ...);
int global_init_var = 84;
int global_uninit_var;
void func1(int i)
{
printf("%d\n", i);
}
int main(void)
{
static int static_var = 85;
static int static_var2;
int a = 1;
int b;
func1(static_var + static_var2 + a + b);
return a;
}
-
.text
: 已编译程序的机器代码 -
.rodata
: 只读数据,比如 printf 语句中的格 图 7-3 典型的 ELF 可重定位目标文件式串和开关语句的跳转表。 -
.data
: 已初始化的全局和静态 C 变量。局部 C 变量在运行时被保存在栈中,既不出现在 .data 节中,也不出现在 .bss 节中。 -
.bss
: 未初始化的全局和静态 C 变量,以及所有被初始化为 0 的全局或静态变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分已初始化和未初始化变量是为了空间效率:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。运行时,在内存中分配这些变量,初始值为 0 。 -
.symtab
: 一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。一些程序员错误地认为必须通过 -g 选项来编译一个程序,才能得到符号表信息。实际上,每个可重定位目标文件在 .symtab 中都有一张符号表(除非程序员特意用 STRIP 命令去掉它)。然而,和编译器中的符号表不同,.symtab 符号表不包含局部变量的条目。 -
.rel.text
: 一个 .text 节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面,调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非用户显式地指示链接器包含这些信息。 -
.rel.data
: 被模块引用或定义的所有全局变量的重定位信息。一般而言,任何已初始化的全局变最,如果它的初始值是一个全局变量地址或者外部定义函数的地址,都需要被修改。 -
.debug
: 一个调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的 C 源文件。只有以 -g 选项调用编译器驱动程序时,才会得到这张表。 -
.line
: 原始 C 源程序中的行号和 .text 节中机器指令之间的映射。只有以 -g 选项调用编译器驱动程序时,才会得到这张表。 -
.strtab
: 一个字符串表,其内容包括 .symtab 和 .debug 节中的符号表,以及节头部中的节名字。字符串表就是以 null 结尾的字符串的序列。