以下内容源于网络资源的学习与整理,如有侵权请告知删除。
参考内容
(1)ELF文件格式解析_elf文件解析_mergerly的博客-CSDN博客
(2)Linux C/C++目标文件、可执行文件分为几段? - 知乎(推荐其博客)
(3)程序的编译、装载与链接
Linux系统下,C/C++源码进行汇编之后生成的.o目标文件,或者链接之后生成的可执行程序文件,它们一般是ELF文件格式(Executable and Linking Format,可执行与可连接格式)。
Linux上的目标文件(Relocatable File)、可执行文件(Executable File)、动态链接库文件(Shared Object File)、coredump文件(Core Dump File)都是ELF格式。
这类文件是分段进行组织的,段的个数可通过代码控制,但通常一个程序文件中至少包含以下段:
本文介绍的是ELF文件的格式,不是介绍程序进程的地址空间分布。不过两者有一定的关系,将来ELF文件被执行时,它所对应的进程地址空间分布中每个区域的内容,就对应着该文件的段内容?
本文以一个简单的C程序为例,说明C/C++代码跟ELF文件中的段是如何对应的。
//main.c
#include long global_n1; // 全局变量默认初始化为0,指针的话就是null
long global_n2 = 10; long sum_func(long a, long b)
{static long local_static_n1; // 局部静态变量默认初始化为0static long local_static_n2 = 123;static long local_static_n3 = 456;return a + b;
}int main(void)
{long sum = sum_func(global_n1, global_n2);printf("sum=%ld\n", sum);return 0;
}
为了简化分析,我们将上述代码编译为目标文件而不是可执行文件,因为编译为可执行文件时,会引入很多另外的符号和段。
gcc -c -o main.o main.c
然后使用objdump工具查看main.o这个目标文件的反汇编代码,-t选项表示显示符号列表。
xjh@ubuntu:~/iot/tmp$ objdump -t main.omain.o: 文件格式 elf32-i386SYMBOL TABLE:
00000000 l df *ABS* 00000000 main.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .rodata 00000000 .rodata
00000004 l O .data 00000004 local_static_n3.1831
00000008 l O .data 00000004 local_static_n2.1830
00000000 l O .bss 00000004 local_static_n1.1829
00000000 l d .note.GNU-stack 00000000 .note.GNU-stack
00000000 l d .eh_frame 00000000 .eh_frame
00000000 l d .comment 00000000 .comment
00000004 O *COM* 00000004 global_n1
00000000 g O .data 00000004 global_n2
00000000 g F .text 0000000d sum_func
0000000d g F .text 0000003f main
00000000 *UND* 00000000 printfxjh@ubuntu:~/iot/tmp$
从中可以知道每个符号(函数名、变量名)位于哪个段:
段 | 符号 | 备注 |
---|---|---|
.data | local_static_n3 local_static_n2 global_n2 | 存放初始化的全局变量、初始化的局部静态变量。 |
.bss | local_static_n1 global_n1 | 存放未初始化的全局变量、未初始化的局部静态变量。 |
.text | sum_func main | 存放代码 |
通过size命令可以查看每个段的大小。如下所示,text段的大小173字节(main函数和sum_func函数的二进制代码长度),data段的大小是12字节(上表中的.data段的3个变量的大小),bss段的大小是4字节(上表中.bss段的两个变量的大小)。
xjh@ubuntu:~/iot/tmp$ size main.otext data bss dec hex filename173 12 4 189 bd main.o
xjh@ubuntu:~/iot/tmp$
上面说到将程序编译为可执行文件时,会引入很多另外的符号和段。 现在我们来验证一下。
xjh@ubuntu:~/iot/tmp$ gcc main.o -o main.elf
xjh@ubuntu:~/iot/tmp$ objdump -t main.elf main.elf: 文件格式 elf32-i386SYMBOL TABLE:
08048154 l d .interp 00000000 .interp
08048168 l d .note.ABI-tag 00000000 .note.ABI-tag
08048188 l d .note.gnu.build-id 00000000 .note.gnu.build-id
080481ac l d .gnu.hash 00000000 .gnu.hash
080481cc l d .dynsym 00000000 .dynsym
0804821c l d .dynstr 00000000 .dynstr
08048268 l d .gnu.version 00000000 .gnu.version
08048274 l d .gnu.version_r 00000000 .gnu.version_r
08048294 l d .rel.dyn 00000000 .rel.dyn
0804829c l d .rel.plt 00000000 .rel.plt
080482b4 l d .init 00000000 .init
080482e0 l d .plt 00000000 .plt
08048320 l d .text 00000000 .text
080484e4 l d .fini 00000000 .fini
080484f8 l d .rodata 00000000 .rodata
0804850c l d .eh_frame_hdr 00000000 .eh_frame_hdr
08048540 l d .eh_frame 00000000 .eh_frame
08049f08 l d .init_array 00000000 .init_array
08049f0c l d .fini_array 00000000 .fini_array
08049f10 l d .jcr 00000000 .jcr
08049f14 l d .dynamic 00000000 .dynamic
08049ffc l d .got 00000000 .got
0804a000 l d .got.plt 00000000 .got.plt
0804a018 l d .data 00000000 .data
0804a02c l d .bss 00000000 .bss
00000000 l d .comment 00000000 .comment
00000000 l df *ABS* 00000000 crtstuff.c
08049f10 l O .jcr 00000000 __JCR_LIST__
08048360 l F .text 00000000 deregister_tm_clones
08048390 l F .text 00000000 register_tm_clones
080483d0 l F .text 00000000 __do_global_dtors_aux
0804a02c l O .bss 00000001 completed.6600
08049f0c l O .fini_array 00000000 __do_global_dtors_aux_fini_array_entry
080483f0 l F .text 00000000 frame_dummy
08049f08 l O .init_array 00000000 __frame_dummy_init_array_entry
00000000 l df *ABS* 00000000 main.c
0804a024 l O .data 00000004 local_static_n3.1831
0804a028 l O .data 00000004 local_static_n2.1830
0804a030 l O .bss 00000004 local_static_n1.1829
00000000 l df *ABS* 00000000 crtstuff.c
0804860c l O .eh_frame 00000000 __FRAME_END__
08049f10 l O .jcr 00000000 __JCR_END__
00000000 l df *ABS* 00000000
08049f0c l .init_array 00000000 __init_array_end
08049f14 l O .dynamic 00000000 _DYNAMIC
08049f08 l .init_array 00000000 __init_array_start
0804a000 l O .got.plt 00000000 _GLOBAL_OFFSET_TABLE_
080484e0 g F .text 00000002 __libc_csu_fini
0804a020 g O .data 00000004 global_n2
00000000 w *UND* 00000000 _ITM_deregisterTMCloneTable
08048350 g F .text 00000004 .hidden __x86.get_pc_thunk.bx
0804a018 w .data 00000000 data_start
00000000 F *UND* 00000000 printf@@GLIBC_2.0
0804a02c g .data 00000000 _edata
080484e4 g F .fini 00000000 _fini
0804a018 g .data 00000000 __data_start
00000000 w *UND* 00000000 __gmon_start__
0804a01c g O .data 00000000 .hidden __dso_handle
080484fc g O .rodata 00000004 _IO_stdin_used
00000000 F *UND* 00000000 __libc_start_main@@GLIBC_2.0
0804a034 g O .bss 00000004 global_n1
08048470 g F .text 00000061 __libc_csu_init
0804a038 g .bss 00000000 _end
08048320 g F .text 00000000 _start
080484f8 g O .rodata 00000004 _fp_hw
0804a02c g .bss 00000000 __bss_start
0804842a g F .text 0000003f main
00000000 w *UND* 00000000 _Jv_RegisterClasses
0804a02c g O .data 00000000 .hidden __TMC_END__
00000000 w *UND* 00000000 _ITM_registerTMCloneTable
0804841d g F .text 0000000d sum_func
080482b4 g F .init 00000000 _initxjh@ubuntu:~/iot/tmp$