本文信息来源:
又是一期硬核内容:ELF文件格式
What's the difference of section and segment in ELF file format
ELF Sections & Segments and Linux VMA Mappings
ELF简介
ELF全称 executable and linkable format 精灵
是一种linux下常用的可执行文件 对象 共享库的标准文件格式
还有许多其他可执行文件格式 PE Mach-O COFF COM内核中处理elf相关代码参考: binfmt_elf.c
elf中的数据按照Segment(段)和Section(节)两个概念进行划分
ELF文件格式
文章图片
ELF Header
- 架构 ABI版本等基础信息
- program header table的位置和数量
- section header table的位置和数量
- 每个表项定义了一个segment
- 每个segment可包含多个section
- 每个表项定义了一个section
可用readelf命令来展示elf文件的相关信息
用法如下:
用法:readelf <选项> elf-文件
显示关于 ELF 格式文件内容的信息
Options are:
-a --allEquivalent to: -h -l -S -s -r -d -V -A -I
-h --file-headerDisplay the ELF file header
-l --program-headersDisplay the program headers
--segmentsAn alias for --program-headers
-S --section-headersDisplay the sections' header
--sectionsAn alias for --section-headers
-g --section-groupsDisplay the section groups
-t --section-detailsDisplay the section details
-e --headersEquivalent to: -h -l -S
比如,使用readelf来查看date的信息
readelf -l /bin/date
输出
Elf 文件类型为 DYN (Position-Independent Executable file)
Entry point 0x38c0
There are 13 program headers, starting at offset 64程序头:
TypeOffsetVirtAddrPhysAddr
FileSizMemSizFlagsAlign
PHDR0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8R0x8
INTERP0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001cR0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000028a8 0x00000000000028a8R0x1000
LOAD0x0000000000003000 0x0000000000003000 0x0000000000003000
0x0000000000010001 0x0000000000010001R E0x1000
LOAD0x0000000000014000 0x0000000000014000 0x0000000000014000
0x0000000000005cf0 0x0000000000005cf0R0x1000
LOAD0x0000000000019ff0 0x000000000001aff0 0x000000000001aff0
0x00000000000010b0 0x0000000000001268RW0x1000
DYNAMIC0x000000000001ab98 0x000000000001bb98 0x000000000001bb98
0x00000000000001f0 0x00000000000001f0RW0x8
NOTE0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000040 0x0000000000000040R0x8
NOTE0x0000000000000378 0x0000000000000378 0x0000000000000378
0x0000000000000044 0x0000000000000044R0x4
GNU_PROPERTY0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000040 0x0000000000000040R0x8
GNU_EH_FRAME0x0000000000018000 0x0000000000018000 0x0000000000018000
0x0000000000000454 0x0000000000000454R0x4
GNU_STACK0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000RW0x10
GNU_RELRO0x0000000000019ff0 0x000000000001aff0 0x000000000001aff0
0x0000000000001010 0x0000000000001010R0x1 Section to Segment mapping:
段节...
00
01.interp
02.interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03.init .plt .text .fini
04.rodata .eh_frame_hdr .eh_frame
05.init_array .fini_array .data.rel.ro .dynamic .got .data .bss
06.dynamic
07.note.gnu.property
08.note.gnu.build-id .note.ABI-tag
09.note.gnu.property
10.eh_frame_hdr
11
12.init_array .fini_array .data.rel.ro .dynamic .got
可知:
- 在加载到内存中时,程序被分成了13个Segment(从PHDR到GNU_RELRO)
- 每个Segment都包含了1个或者更多的Section
- 包含着运行时需要的信息
- 用于告诉操作系统,段应该被加载到虚拟内存中的什么位置?每个段都有那些权限?(read, write, execute)
- 每个Segment主要包含加载地址 文件中的范围 内存权限 对齐方式等信息
- 包含着链接时需要的信息
- 用于告诉链接器,elf中每个部分是什么,哪里是代码,哪里是只读数据,哪里是重定位信息
- 每个Section主要包含Section类型 文件中的位置 大小等信息
- 链接器会把Section放入Segment中
- 相同权限的Section会放入同一个Segment,例如.text和.rodata section
- 一个Segment包含许多Section,一个Section可以属于多个Segment
运行
ld --verbose
可以看到本系统中所用的脚本
我的Archlinux 5.16.13-arch1-1的链接脚本一部分是这样:
.gnu.version_r: { *(.gnu.version_r) }
.rela.dyn:
{
*(.rela.init)
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
*(.rela.fini)
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
*(.rela.ctors)
*(.rela.dtors)
*(.rela.got)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
*(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
*(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
*(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
*(.rela.ifunc)
}
表示:
.gnu.version_r
Section 会被放入 .gnu.version_r
Segment.rela.init
等一大堆的Section,会被放入 .rela.dyn
Segment汇编中的伪指令全部都是Section,要等链接之后才会有SegmentELF文件分类 可执行文件(ET_EXEC)
NASM中.section
和.segment
这两个是等效的,都表示Section
可直接运行的程序,必须包含segment
对象文件(ET_REL,*.o)
需要与其他对象文件链接,必须包含section
动态库(ET_DYN,*.so)
与其他对象文件/可执行文件链接
必须同时包含segment和section
ELF的内存映射
文章图片
查看内存映射情况
cat /proc/[pid]/maps
比如运行
cat /proc/self/maps
查看cat本身的内存映射
563b04d75000-563b04d77000 r--p 00000000 fe:00 6294104/usr/bin/cat
563b04d77000-563b04d7c000 r-xp 00002000 fe:00 6294104/usr/bin/cat
563b04d7c000-563b04d7f000 r--p 00007000 fe:00 6294104/usr/bin/cat
563b04d7f000-563b04d80000 r--p 00009000 fe:00 6294104/usr/bin/cat
563b04d80000-563b04d81000 rw-p 0000a000 fe:00 6294104/usr/bin/cat
563b058b6000-563b058d7000 rw-p 00000000 00:00 0[heap]
7f5f7324b000-7f5f73837000 r--p 00000000 fe:00 6364075/usr/lib/locale/locale-archive
7f5f73837000-7f5f7383a000 rw-p 00000000 00:00 0
7f5f7383a000-7f5f73866000 r--p 00000000 fe:00 6294921/usr/lib/libc.so.6
7f5f73866000-7f5f739dc000 r-xp 0002c000 fe:00 6294921/usr/lib/libc.so.6
7f5f739dc000-7f5f73a30000 r--p 001a2000 fe:00 6294921/usr/lib/libc.so.6
7f5f73a30000-7f5f73a31000 ---p 001f6000 fe:00 6294921/usr/lib/libc.so.6
7f5f73a31000-7f5f73a34000 r--p 001f6000 fe:00 6294921/usr/lib/libc.so.6
7f5f73a34000-7f5f73a37000 rw-p 001f9000 fe:00 6294921/usr/lib/libc.so.6
7f5f73a37000-7f5f73a46000 rw-p 00000000 00:00 0
7f5f73a70000-7f5f73a92000 rw-p 00000000 00:00 0
7f5f73a92000-7f5f73a94000 r--p 00000000 fe:00 6294911/usr/lib/ld-linux-x86-64.so.2
7f5f73a94000-7f5f73abb000 r-xp 00002000 fe:00 6294911/usr/lib/ld-linux-x86-64.so.2
7f5f73abb000-7f5f73ac6000 r--p 00029000 fe:00 6294911/usr/lib/ld-linux-x86-64.so.2
7f5f73ac7000-7f5f73ac9000 r--p 00034000 fe:00 6294911/usr/lib/ld-linux-x86-64.so.2
7f5f73ac9000-7f5f73acb000 rw-p 00036000 fe:00 6294911/usr/lib/ld-linux-x86-64.so.2
7ffec90a6000-7ffec90c8000 rw-p 00000000 00:00 0[stack]
7ffec918d000-7ffec9191000 r--p 00000000 00:00 0[vvar]
7ffec9191000-7ffec9193000 r-xp 00000000 00:00 0[vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0[vsyscall]
【从ELF文件谈起】从左到右为:
- 虚拟地址的起始和结束
- 该内存映射的类型flag r(read) w(write) x(execute) p(private) s(shared)
- 实际对象在该内存映射上相对于起始的偏移量
major:minor
: the major and minor number pairs of the device holding the file that has been mapped.- 映射文件的索引节点号码
- 该内存映射文件的名称