Hong's Blog
Be Agile. Be Smart. Be Visionary. Be Dedicated.
在Unix/Linux中,可执行文件、目标文件(.o)、共享库(.so)等一般都是ELF(Executable and Linkable Format)格式的。一个ELF文件的大致结构如下所示,
首先是ELF header,它描述了这个ELF文件的概况,比如数据的编码方式、ELF文件的类型、program header和section header的起始位置(起始字节)等。ELF header同时还指定了自身的长度,一般而言是64字节(byte);之后,就是program header。 readelf命令可以用来检查ELF文件的内容。查看ELF header的命令如下,
[hong@localhost elf]$ readelf -h exe ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 #ELF文件的mgaic number Class: ELF64 Data: 2's complement, little endian #补码,小端编码 Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) #可执行文件 Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x400390 #第一条指令的地址 Start of program headers: 64 (bytes into file) Start of section headers: 2416 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) #ELF header长度为64字节 Size of program headers: 56 (bytes) Number of program headers: 8 Size of section headers: 64 (bytes) Number of section headers: 30 Section header string table index: 27
ELF header之后是两张表和多个section,依次排列为——首先是program header table,然后紧接着多个section,最后是section header table。 program header table中的信息可以告诉操作系统在运行该ELF文件时应该如何加载这个ELF文件。通过如下命令可以查看program header table的信息,
[hong@localhost elf]$ readelf -l exe Elf file type is EXEC (Executable file) Entry point 0x400390 There are 8 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040 0x00000000000001c0 0x00000000000001c0 R E 8 INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200 0x000000000000001c 0x000000000000001c R 1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x000000000000062c 0x000000000000062c R E 200000 LOAD 0x0000000000000630 0x0000000000600630 0x0000000000600630 0x00000000000001e4 0x0000000000000200 RW 200000 DYNAMIC 0x0000000000000658 0x0000000000600658 0x0000000000600658 0x0000000000000190 0x0000000000000190 RW 8 NOTE 0x000000000000021c 0x000000000040021c 0x000000000040021c 0x0000000000000044 0x0000000000000044 R 4 GNU_EH_FRAME 0x0000000000000588 0x0000000000400588 0x0000000000400588 0x0000000000000024 0x0000000000000024 R 4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 8 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag .note.gnu.build-id 06 .eh_frame_hdr 07
注意类型为LOAD的两个segment,在ELF文件被执行时它们会被加载到内存中。以第一个类型为LOAD的segment为例,它会被加载到内存中以地址(虚拟地址)0x0000000000400000起始,长度为0x000000000000062c的区域。可以看到这个segment中包含了.text(代码段),所以代码段在运行时会被加载到内存中。另外,地址0x0000000000400000是程序运行时的最低虚拟地址,如果是32位系统,则是0x08048000。
ELF文件的大部分信息都在它的各个section里,典型的section有,
ELF文件的最后一块信息是section header table。通过这张表可以知道各个section在ELF文件中的起始位置、长度等信息。可以通过readelf -S elf-file-name命令查看section header table,
###ELF与链接 ELF文件格式被设计成可以满足分离编译和链接的要求。在链接多个.o类型的ELF文件为单个可执行的ELF文件时,需要注意链接器所使用的规则。规则如下, 链接器认为函数和已初始化的全局变量为强符号,未初始化的全局变量是弱符号;则, 1 不允许出现强符号的重复定义 2 如果定义了一个强符号和多个弱符号,那么引用时会选择引用强符号 3 如果有多个同名弱符号的定义,那么引用时会任意选择 当链接器默默地引用规则2或者3时,可以会导致意想不到的行为。提高链接时的警告级别,可以发现潜在的问题。强弱符号可以通过nm命令查看。
另外在链接多个静态库时,如果相同名字的符号在多个静态库中被定义,那么这些库在链接时的排列顺序会影响符号的重定位。
###常用的检查ELF文件的工具 ar、ldd、nm、objdump、od、readelf、strings、strip
注意:以上讨论的ELF文件是基于C语言编译而来。
参考: 《深入理解计算机系统/Computer Systems A Programmer’s Perspective 2nd Edition》 《Binary Hacks》 http://en.wikipedia.org/wiki/Executable_and_Linkable_Format http://stackoverflow.com/questions/7187981/whats-the-memory-before-0x08048000-used-for-in-32-bit-machine
如果你觉得这篇文章对你有用,可以微信扫一扫表示🙏 / If you find this post is useful to you, buy me 🍶 via Wechat
ELF简介
在Unix/Linux中,可执行文件、目标文件(.o)、共享库(.so)等一般都是ELF(Executable and Linkable Format)格式的。一个ELF文件的大致结构如下所示,
首先是ELF header,它描述了这个ELF文件的概况,比如数据的编码方式、ELF文件的类型、program header和section header的起始位置(起始字节)等。ELF header同时还指定了自身的长度,一般而言是64字节(byte);之后,就是program header。
readelf命令可以用来检查ELF文件的内容。查看ELF header的命令如下,
ELF header之后是两张表和多个section,依次排列为——首先是program header table,然后紧接着多个section,最后是section header table。
program header table中的信息可以告诉操作系统在运行该ELF文件时应该如何加载这个ELF文件。通过如下命令可以查看program header table的信息,
注意类型为LOAD的两个segment,在ELF文件被执行时它们会被加载到内存中。以第一个类型为LOAD的segment为例,它会被加载到内存中以地址(虚拟地址)0x0000000000400000起始,长度为0x000000000000062c的区域。可以看到这个segment中包含了.text(代码段),所以代码段在运行时会被加载到内存中。另外,地址0x0000000000400000是程序运行时的最低虚拟地址,如果是32位系统,则是0x08048000。
ELF文件的大部分信息都在它的各个section里,典型的section有,
ELF文件的最后一块信息是section header table。通过这张表可以知道各个section在ELF文件中的起始位置、长度等信息。可以通过readelf -S elf-file-name命令查看section header table,
###ELF与链接 ELF文件格式被设计成可以满足分离编译和链接的要求。在链接多个.o类型的ELF文件为单个可执行的ELF文件时,需要注意链接器所使用的规则。规则如下,
链接器认为函数和已初始化的全局变量为强符号,未初始化的全局变量是弱符号;则,
1 不允许出现强符号的重复定义
2 如果定义了一个强符号和多个弱符号,那么引用时会选择引用强符号
3 如果有多个同名弱符号的定义,那么引用时会任意选择
当链接器默默地引用规则2或者3时,可以会导致意想不到的行为。提高链接时的警告级别,可以发现潜在的问题。强弱符号可以通过nm命令查看。
另外在链接多个静态库时,如果相同名字的符号在多个静态库中被定义,那么这些库在链接时的排列顺序会影响符号的重定位。
###常用的检查ELF文件的工具 ar、ldd、nm、objdump、od、readelf、strings、strip
注意:以上讨论的ELF文件是基于C语言编译而来。
参考:
《深入理解计算机系统/Computer Systems A Programmer’s Perspective 2nd Edition》
《Binary Hacks》
http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
http://stackoverflow.com/questions/7187981/whats-the-memory-before-0x08048000-used-for-in-32-bit-machine
如果你觉得这篇文章对你有用,可以微信扫一扫表示🙏 / If you find this post is useful to you, buy me 🍶 via Wechat