本文信息来源:

又是一期硬核内容: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的位置和数量

Program header table

  • 每个表项定义了一个segment
  • 每个segment可包含多个section

Section header table

  • 每个表项定义了一个section

readelf命令

可用readelf命令来展示elf文件的相关信息

用法如下:

用法:readelf <选项> elf-文件
显示关于 ELF 格式文件内容的信息
Options are:
-a --all Equivalent to: -h -l -S -s -r -d -V -A -I
-h --file-header Display the ELF file header
-l --program-headers Display the program headers
--segments An alias for --program-headers
-S --section-headers Display the sections' header
--sections An alias for --section-headers
-g --section-groups Display the section groups
-t --section-details Display the section details
-e --headers Equivalent 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 程序头:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000028a8 0x00000000000028a8 R 0x1000
LOAD 0x0000000000003000 0x0000000000003000 0x0000000000003000
0x0000000000010001 0x0000000000010001 R E 0x1000
LOAD 0x0000000000014000 0x0000000000014000 0x0000000000014000
0x0000000000005cf0 0x0000000000005cf0 R 0x1000
LOAD 0x0000000000019ff0 0x000000000001aff0 0x000000000001aff0
0x00000000000010b0 0x0000000000001268 RW 0x1000
DYNAMIC 0x000000000001ab98 0x000000000001bb98 0x000000000001bb98
0x00000000000001f0 0x00000000000001f0 RW 0x8
NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000040 0x0000000000000040 R 0x8
NOTE 0x0000000000000378 0x0000000000000378 0x0000000000000378
0x0000000000000044 0x0000000000000044 R 0x4
GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000040 0x0000000000000040 R 0x8
GNU_EH_FRAME 0x0000000000018000 0x0000000000018000 0x0000000000018000
0x0000000000000454 0x0000000000000454 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000019ff0 0x000000000001aff0 0x000000000001aff0
0x0000000000001010 0x0000000000001010 R 0x1 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

Segment vs Section

Segment

  • 包含着运行时需要的信息

  • 用于告诉操作系统,段应该被加载到虚拟内存中的什么位置?每个段都有那些权限?(read, write, execute)

  • 每个Segment主要包含加载地址 文件中的范围 内存权限 对齐方式等信息

Section

  • 包含着链接时需要的信息

  • 用于告诉链接器,elf中每个部分是什么,哪里是代码,哪里是只读数据,哪里是重定位信息

  • 每个Section主要包含Section类型 文件中的位置 大小等信息

  • 链接器会把Section放入Segment中

Segment和Section的关系

  • 相同权限的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,要等链接之后才会有Segment

NASM中 .section.segment 这两个是等效的,都表示 Section

ELF文件分类

可执行文件(ET_EXEC)

可直接运行的程序,必须包含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]

从左到右为:

  • 虚拟地址的起始和结束
  • 该内存映射的类型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.
  • 映射文件的索引节点号码
  • 该内存映射文件的名称

从ELF文件谈起的更多相关文章

  1. linux实践之ELF文件分析

    linux实践之ELF文件分析 下面开始elf文件的分析. 我们首先编写一个简单的C代码. 编译链接生成可执行文件. 首先,查看scn15elf.o文件的详细信息. 以16进制形式查看scn15elf ...

  2. ELF文件

    ELF文件格式是一个开发标准,各种UNIX系统的可执行文件都采用ELF格式,它有三种不同的类型: 可重定位的目标文件 可执行文件 共享库 现在分析一下上一篇文章中经过汇编之后生成的目标文件max.o和 ...

  3. elf文件中的.plt .rel.dyn .rel.plt .got .got.plt的关系

    .plt的作用是一个跳板,保存了某个符号在重定位表中的偏移量(用来第一次查找某个符号)和对应的.got.plt的对应的地址 .rel.dyn重定向表,在程序启动时就需要重定位完成. .rel.plt保 ...

  4. 实例分析ELF文件动态链接

    参考文献: <ELF V1.2> <程序员的自我修养---链接.装载与库>第6章 可执行文件的装载与进程 第7章 动态链接 <Linux GOT与PLT> 开发平台 ...

  5. 实例分析ELF文件静态链接

    参考文献: <ELF V1.2> <程序员的自我修养---链接.装载与库>第4章 静态链接 开发平台: [thm@tanghuimin static_link]$ uname ...

  6. ldconfig报错 :libstdc++.so.6.0.18-gdb.py不是一个elf文件

    今天安装wxWidgets,输入ldconfig竟然提示 /usr/lib64/libstdc++.so.6.0.18-gdb.py 不是一个elf文件,开头魔数错误 摸不着头脑,上网搜了一下,有说是 ...

  7. axf、elf文件转换成bin、hex脚本工具

    在嵌入式开发过程中常常遇到将axf或elf文件转换成bin的情况,大家都知道通过gnu toolchain中的objcopy和keil中的fromelf能做到.可是为了这么一个小事而记住复杂的选项以及 ...

  8. 为什么ELF文件的加载地址是0x8048000

    在一个进程的虚拟地址空间中,ELF文件是从0x8048000这个地址开始加载的,为什么会是这个地址? 回答:用命令ld --verbose可以看到0x08048000,ld的默认脚本用这个地址作为EL ...

  9. ELF文件数据布局探索(1)

    作为一名Linux小白,第一次看到a.out这个名字,感觉实在是奇怪,搜了一下才知道这是编译器输出的默认可执行文件名 然后vi一下,哇,各种乱码,仔细看看,发现了三个清晰的字符ELF.继续搜索, 第一 ...

随机推荐

  1. 鸿蒙轻内核源码分析:文件系统LittleFS

    摘要:本文先介绍下LFS文件系统结构体的结构体和全局变量,然后分析下LFS文件操作接口. 本文分享自华为云社区<# 鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS>,作者: ...

  2. eclipse中快捷键中逗号是怎么用的,如ctrl+2,L

    Ctrl +2 同时按下,松开后,再按下L

  3. uniap tab list 滑动

    效果如下 <uni-popup ref="bankListAll" type="dialog"> <ty-mutiple-select :mu ...

  4. Linux防火墙firewalld安全设置

    背景描述 防火墙是具有很好的保护作用.入侵者必须首先穿越防火墙的安全防线,才能接触目标计算机.在公司里数据安全是最重要的,要求安全部门进行全公司进行服务器防火墙安全搭建,在原有的基础上进行安全的防火墙 ...

  5. iOS中JavaScript和OC交互 --by 胡 xu

    在iOS开发中很多时候我们会和UIWebView打交道,目前国内的很多应用都采用了UIWebView的混合编程技术,最常见的是微信公众号的内容页面.前段时间在做微信公众平台相关的开发,发现很多应用场景 ...

  6. Java访问修饰符和三大特征(封装,继承和多态)

    一.访问修饰符基本介绍: java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围): 1.公开级别:用public修饰,对外公开2.受保护级别:用protected修饰,对子 ...

  7. 1Appium Desktop 的简单应用

    由于Appium Desktop出来了,所以使用appium要比以前简单许多,现在根据以前的文章针对Appium Desktop做下修改更新 之前文章链接:https://testerhome.com ...

  8. Java面向对象之各种变量详解

    在Java中一定有很多变量让大家头疼,成员变量.类变量.局部变量等等,今天就来分别认识认识他们吧! Java面向对象之各种变量详解 前言 在 Java语言中, 根据定义变量位置的不同,可以将变量分成两 ...

  9. Spring系列14:IoC容器的扩展点

    Spring系列14:IoC容器的扩展点 回顾 知识需要成体系地学习,本系列文章前后有关联,建议按照顺序阅读.上一篇我们详细介绍了Spring Bean的生命周期和丰富的扩展点,没有阅读的强烈建议先阅 ...

  10. verification 提取差异点

    提取出差异点 传统用例 项目A锁定的寄存器是regA,项目B的锁定功能的寄存器是regB,如果项目A用例中直接用 reg_model.regA.write(); reg_model.regA.read ...