本文拟通过分析一个普通的32位elf文件去剖析32位elf文件结构。(System V ABI基于2013版)
一、32位数据格式
Name | Size | Alignment | Purpose |
---|---|---|---|
Elf32_Addr | 4 | 4 | Unsigned program address |
Elf32_Off | 4 | 4 | Unsigned file offset |
Elf32_Half | 2 | 2 | Unsigned medium integer |
Elf32_Word | 4 | 4 | Unsigned integer |
Elf32_Sword | 4 | 4 | Signed integer |
unsigned char | 1 | 1 | Unsigned small integer |
上图表示:
比如Elf32_Addr表示无符号程序地址,其大小为4bytes,字节对齐为4。对于STM32来说,其地址是32位,这个毋庸置疑。在Linux下/usr/include/elf.h中也有定义:
typedef uint16_t Elf32_Half;
typedef uint16_t Elf64_Half;
typedef uint32_t Elf32_Word;
typedef int32_t Elf32_Sword;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
typedef uint64_t Elf32_Xword;
typedef int64_t Elf32_Sxword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint32_t Elf32_Addr;
typedef uint64_t Elf64_Addr;
typedef uint32_t Elf32_Off;
typedef uint64_t Elf64_Off;
typedef uint16_t Elf32_Section;
typedef uint16_t Elf64_Section;
typedef Elf32_Half Elf32_Versym;
typedef Elf64_Half Elf64_Versym;
可以看到就是一些重定义,这个主要是为了不同平台的兼容性。在平台确定的情况下把它们带入的看做uint32_t或uint16_t等是完全没问题的。 如果是在Windows下使用gcc工具链开发,也可在工具链的 include 文件夹下找到elf.h ,本文所有宏与结构体定义均可在elf.h里找到。
二、ELF Header
这个结构体即为所有elf文件的文件头,是一个固定格式。文件起始对应结构体首地址。
2.1 e_ident[EI_NIDENT]
首先可以看到这是一个unsigned char类型的数组,大小为16。
这个数组内容标志着这是一个object文件,而且里面包含了和架构等无关的信息(也就是每个字节顺序和含义都是被定义死的,不会受比如大小端等的影响)。数组所存信息如下:
Name | Value | Purpose |
---|---|---|
EI_MAG0 | 0 | File identification |
EI_MAG1 | 1 | File identification |
EI_MAG2 | 2 | File identification |
EI_MAG3 | 3 | File identification |
EI_CLASS | 4 | File class |
EI_DATA | 5 | Data encoding |
EI_VERSION | 6 | File version |
EI_OSABI | 7 | Operating system/ABI identification |
EI_ABIVERSION | 8 | ABI version |
EI_PAD | 9 | Start of padding bytes |
EI_NIDENT | 16 | Size of e_ident[] |
其中每个部分代表:
2.1.1 EI_MAG0~EI_MAG3
很明显,前四个字节是固定的,分别是0x7F和E、L、F的ASCII值。
2.1.2 EI_CLASS
它表示该文件是一个什么类型的文件,可选:无效、32位、64位
2.1.3 EI_DATA
它表示该文件字节序是大端还是小端,可选:无效、小端、大端
LSB:Least Significant Bit
MSB:Most Significant Bit
2.1.4 EI_VERSION
当前版本,此字节必须为EV_CURRENT,在elf.h里可以看到:
2.1.5 EI_OSABI
此字节用于标识当前文件使用特定的操作系统或者ABI。相关内容如下:
Name | Value | Meaning |
---|---|---|
ELFOSABI_NONE | 0 | No extensions or unspecified |
ELFOSABI_HPUX | 1 | Hewlett-Packard HP-UX |
ELFOSABI_NETBSD | 2 | NetBSD |
ELFOSABI_GNU | 3 | GNU |
ELFOSABI_LINUX | 3 | Linux historical - alias for ELFOSABI_GNU |
ELFOSABI_SOLARIS | 6 | Sun Solaris |
ELFOSABI_AIX | 7 | AIX |
ELFOSABI_IRIX | 8 | IRIX |
ELFOSABI_FREEBSD | 9 | FreeBSD |
ELFOSABI_TRU64 | 10 | Compaq TRU64 UNIX |
ELFOSABI_MODESTO | 11 | Novell Modesto |
ELFOSABI_OPENBSD | 12 | Open BSD |
ELFOSABI_OPENVMS | 13 | Open VMS |
ELFOSABI_NSK | 14 | Hewlett-Packard Non-Stop Kernel |
ELFOSABI_AROS | 15 | Amiga Research OS |
ELFOSABI_FENIXOS | 16 | The FenixOS highly scalable multi-core OS |
ELFOSABI_CLOUDABI | 17 | Nuxi CloudABI |
ELFOSABI_OPENVOS | 18 | Stratus Technologies OpenVOS |
64-255 | Architecture-specific value range |
2.1.6 EI_ABIVERSION
此字节用于标识目标文件的ABI版本,从而用来区别那些不相互兼容的ABI版本。
2.1.7 EI_PAD
这些字节未使用,可以看到上面一共用了从0到8字节,所以剩下的从9到15字节都为0。
2.2 e_type
Name | Value | Meaning |
---|---|---|
ET_NONE | 0 | No file type |
ET_REL | 1 | Relocatable file |
ET_EXEC | 2 | Executable file |
ET_DYN | 3 | Shared object file |
ET_CORE | 4 | Core file |
ET_LOOS | 0xfe00 | Operating system-specific |
ET_HIOS | 0xfeff | Operating system-specific |
ET_LOPROC | 0xff00 | Processor-specific |
ET_HIPROC | 0xffff | Processor-specific |
这两个字节表示文件类型,可以看到上图中Systerm V ABI中就有:
无类型、可重定位文件、可执行文件、共享目标文件、核心文件、用以特定操作系统、用以特定处理器等
2.3 e_machine
这两个字节表示目标文件属于哪种的芯片架构类型:
Name | Value | Meaning |
---|---|---|
EM_NONE | 0 | No machine |
EM_M32 | 1 | AT&T WE 32100 |
EM_SPARC | 2 | SPARC |
EM_386 | 3 | Intel 80386 |
EM_68K | 4 | Motorola 68000 |
EM_88K | 5 | Motorola 88000 |
EM_IAMCU | 6 | Intel MCU |
EM_860 | 7 | Intel 80860 |
EM_MIPS | 8 | MIPS I Architecture |
EM_S370 | 9 | IBM System/370 Processor |
EM_MIPS_RS3_LE | 10 | MIPS RS3000 Little-endian |
reserved | 11-14 | Reserved for future use |
EM_PARISC | 15 | Hewlett-Packard PA-RISC |
reserved | 16 | Reserved for future use |
EM_VPP500 | 17 | Fujitsu VPP500 |
EM_SPARC32PLUS | 18 | Enhanced instruction set SPARC |
EM_960 | 19 | Intel 80960 |
EM_PPC | 20 | PowerPC |
EM_PPC64 | 21 | 64-bit PowerPC |
EM_S390 | 22 | IBM System/390 Processor |
EM_SPU | 23 | IBM SPU/SPC |
reserved | 24-35 | Reserved for future use |
EM_V800 | 36 | NEC V800 |
EM_FR20 | 37 | Fujitsu FR20 |
EM_RH32 | 38 | TRW RH-32 |
EM_RCE | 39 | Motorola RCE |
EM_ARM | 40 | ARM 32-bit architecture (AARCH32) |
EM_ALPHA | 41 | Digital Alpha |
EM_SH | 42 | Hitachi SH |
EM_SPARCV9 | 43 | SPARC Version 9 |
EM_TRICORE | 44 | Siemens TriCore embedded processor |
EM_ARC | 45 | Argonaut RISC Core, Argonaut Technologies Inc. |
EM_H8_300 | 46 | Hitachi H8/300 |
EM_H8_300H | 47 | Hitachi H8/300H |
EM_H8S | 48 | Hitachi H8S |
EM_H8_500 | 49 | Hitachi H8/500 |
EM_IA_64 | 50 | Intel IA-64 processor architecture |
EM_MIPS_X | 51 | Stanford MIPS-X |
EM_COLDFIRE | 52 | Motorola ColdFire |
EM_68HC12 | 53 | Motorola M68HC12 |
EM_MMA | 54 | Fujitsu MMA Multimedia Accelerator |
EM_PCP | 55 | Siemens PCP |
EM_NCPU | 56 | Sony nCPU embedded RISC processor |
EM_NDR1 | 57 | Denso NDR1 microprocessor |
EM_STARCORE | 58 | Motorola Star*Core processor |
EM_ME16 | 59 | Toyota ME16 processor |
EM_ST100 | 60 | STMicroelectronics ST100 processor |
EM_TINYJ | 61 | Advanced Logic Corp. TinyJ embedded processor family |
EM_X86_64 | 62 | AMD x86-64 architecture |
EM_PDSP | 63 | Sony DSP Processor |
EM_PDP10 | 64 | Digital Equipment Corp. PDP-10 |
EM_PDP11 | 65 | Digital Equipment Corp. PDP-11 |
EM_FX66 | 66 | Siemens FX66 microcontroller |
EM_ST9PLUS | 67 | STMicroelectronics ST9+ 8/16 bit microcontroller |
EM_ST7 | 68 | STMicroelectronics ST7 8-bit microcontroller |
EM_68HC16 | 69 | Motorola MC68HC16 Microcontroller |
EM_68HC11 | 70 | Motorola MC68HC11 Microcontroller |
EM_68HC08 | 71 | Motorola MC68HC08 Microcontroller |
EM_68HC05 | 72 | Motorola MC68HC05 Microcontroller |
EM_SVX | 73 | Silicon Graphics SVx |
EM_ST19 | 74 | STMicroelectronics ST19 8-bit microcontroller |
EM_VAX | 75 | Digital VAX |
EM_CRIS | 76 | Axis Communications 32-bit embedded processor |
EM_JAVELIN | 77 | Infineon Technologies 32-bit embedded processor |
EM_FIREPATH | 78 | Element 14 64-bit DSP Processor |
EM_ZSP | 79 | LSI Logic 16-bit DSP Processor |
EM_MMIX | 80 | Donald Knuth's educational 64-bit processor |
EM_HUANY | 81 | Harvard University machine-independent object files |
EM_PRISM | 82 | SiTera Prism |
EM_AVR | 83 | Atmel AVR 8-bit microcontroller |
EM_FR30 | 84 | Fujitsu FR30 |
EM_D10V | 85 | Mitsubishi D10V |
EM_D30V | 86 | Mitsubishi D30V |
EM_V850 | 87 | NEC v850 |
EM_M32R | 88 | Mitsubishi M32R |
EM_MN10300 | 89 | Matsushita MN10300 |
EM_MN10200 | 90 | Matsushita MN10200 |
EM_PJ | 91 | picoJava |
EM_OPENRISC | 92 | OpenRISC 32-bit embedded processor |
EM_ARC_COMPACT | 93 | ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) |
EM_XTENSA | 94 | Tensilica Xtensa Architecture |
EM_VIDEOCORE | 95 | Alphamosaic VideoCore processor |
EM_TMM_GPP | 96 | Thompson Multimedia General Purpose Processor |
EM_NS32K | 97 | National Semiconductor 32000 series |
EM_TPC | 98 | Tenor Network TPC processor |
EM_SNP1K | 99 | Trebia SNP 1000 processor |
EM_ST200 | 100 | STMicroelectronics (www.st.com) ST200 microcontroller |
EM_IP2K | 101 | Ubicom IP2xxx microcontroller family |
EM_MAX | 102 | MAX Processor |
EM_CR | 103 | National Semiconductor CompactRISC microprocessor |
EM_F2MC16 | 104 | Fujitsu F2MC16 |
EM_MSP430 | 105 | Texas Instruments embedded microcontroller msp430 |
EM_BLACKFIN | 106 | Analog Devices Blackfin (DSP) processor |
EM_SE_C33 | 107 | S1C33 Family of Seiko Epson processors |
EM_SEP | 108 | Sharp embedded microprocessor |
EM_ARCA | 109 | Arca RISC Microprocessor |
EM_UNICORE | 110 | Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University |
EM_EXCESS | 111 | eXcess: 16/32/64-bit configurable embedded CPU |
EM_DXP | 112 | Icera Semiconductor Inc. Deep Execution Processor |
EM_ALTERA_NIOS2 | 113 | Altera Nios II soft-core processor |
EM_CRX | 114 | National Semiconductor CompactRISC CRX microprocessor |
EM_XGATE | 115 | Motorola XGATE embedded processor |
EM_C166 | 116 | Infineon C16x/XC16x processor |
EM_M16C | 117 | Renesas M16C series microprocessors |
EM_DSPIC30F | 118 | Microchip Technology dsPIC30F Digital Signal Controller |
EM_CE | 119 | Freescale Communication Engine RISC core |
EM_M32C | 120 | Renesas M32C series microprocessors |
reserved | 121-130 | Reserved for future use |
EM_TSK3000 | 131 | Altium TSK3000 core |
EM_RS08 | 132 | Freescale RS08 embedded processor |
EM_SHARC | 133 | Analog Devices SHARC family of 32-bit DSP processors |
EM_ECOG2 | 134 | Cyan Technology eCOG2 microprocessor |
EM_SCORE7 | 135 | Sunplus S+core7 RISC processor |
EM_DSP24 | 136 | New Japan Radio (NJR) 24-bit DSP Processor |
EM_VIDEOCORE3 | 137 | Broadcom VideoCore III processor |
EM_LATTICEMICO32 | 138 | RISC processor for Lattice FPGA architecture |
EM_SE_C17 | 139 | Seiko Epson C17 family |
EM_TI_C6000 | 140 | The Texas Instruments TMS320C6000 DSP family |
EM_TI_C2000 | 141 | The Texas Instruments TMS320C2000 DSP family |
EM_TI_C5500 | 142 | The Texas Instruments TMS320C55x DSP family |
EM_TI_ARP32 | 143 | Texas Instruments Application Specific RISC Processor, 32bit fetch |
EM_TI_PRU | 144 | Texas Instruments Programmable Realtime Unit |
reserved | 145-159 | Reserved for future use |
EM_MMDSP_PLUS | 160 | |
reserved | 145-159 | Reserved for future use |
EM_MMDSP_PLUS | 160 | STMicroelectronics 64bit VLIW Data Signal Processor |
EM_CYPRESS_M8C | 161 | Cypress M8C microprocessor |
EM_R32C | 162 | Renesas R32C series microprocessors |
EM_TRIMEDIA | 163 | NXP Semiconductors TriMedia architecture family |
EM_QDSP6 | 164 | QUALCOMM DSP6 Processor |
EM_8051 | 165 | Intel 8051 and variants |
EM_STXP7X | 166 | STMicroelectronics STxP7x family of configurable and extensible RISC processors |
EM_NDS32 | 167 | Andes Technology compact code size embedded RISC processor family |
EM_ECOG1 | 168 | Cyan Technology eCOG1X family |
EM_ECOG1X | 168 | Cyan Technology eCOG1X family |
EM_MAXQ30 | 169 | Dallas Semiconductor MAXQ30 Core Micro-controllers |
EM_XIMO16 | 170 | New Japan Radio (NJR) 16-bit DSP Processor |
EM_MANIK | 171 | M2000 Reconfigurable RISC Microprocessor |
EM_CRAYNV2 | 172 | Cray Inc. NV2 vector architecture |
EM_RX | 173 | Renesas RX family |
EM_METAG | 174 | Imagination Technologies META processor architecture |
EM_MCST_ELBRUS | 175 | MCST Elbrus general purpose hardware architecture |
EM_ECOG16 | 176 | Cyan Technology eCOG16 family |
EM_CR16 | 177 | National Semiconductor CompactRISC CR16 16-bit microprocessor |
EM_ETPU | 178 | Freescale Extended Time Processing Unit |
EM_SLE9X | 179 | Infineon Technologies SLE9X core |
EM_L10M | 180 | Intel L10M |
EM_K10M | 181 | Intel K10M |
reserved | 182 | Reserved for future Intel use |
EM_AARCH64 | 183 | ARM 64-bit architecture (AARCH64) |
reserved | 184 | Reserved for future ARM use |
EM_AVR32 | 185 | Atmel Corporation 32-bit microprocessor family |
EM_STM8 | 186 | STMicroeletronics STM8 8-bit microcontroller |
EM_TILE64 | 187 | Tilera TILE64 multicore architecture family |
EM_TILEPRO | 188 | Tilera TILEPro multicore architecture family |
EM_MICROBLAZE | 189 | Xilinx MicroBlaze 32-bit RISC soft processor core |
EM_CUDA | 190 | NVIDIA CUDA architecture |
EM_TILEGX | 191 | Tilera TILE-Gx multicore architecture family |
EM_CLOUDSHIELD | 192 | CloudShield architecture family |
EM_COREA_1ST | 193 | KIPO-KAIST Core-A 1st generation processor family |
EM_COREA_2ND | 194 | KIPO-KAIST Core-A 2nd generation processor family |
EM_ARC_COMPACT2 | 195 | Synopsys ARCompact V2 |
EM_OPEN8 | 196 | Open8 8-bit RISC soft processor core |
EM_RL78 | 197 | Renesas RL78 family |
EM_VIDEOCORE5 | 198 | Broadcom VideoCore V processor |
EM_78KOR | 199 | Renesas 78KOR family |
EM_56800EX | 200 | Freescale 56800EX Digital Signal Controller (DSC) |
EM_BA1 | 201 | Beyond BA1 CPU architecture |
EM_BA2 | 202 | Beyond BA2 CPU architecture |
EM_XCORE | 203 | XMOS xCORE processor family |
EM_MCHP_PIC | 204 | Microchip 8-bit PIC(r) family |
EM_INTEL205 | 205 | Reserved by Intel |
EM_INTEL206 | 206 | Reserved by Intel |
EM_INTEL207 | 207 | Reserved by Intel |
EM_INTEL208 | 208 | Reserved by Intel |
EM_INTEL209 | 209 | Reserved by Intel |
EM_KM32 | 210 | KM211 KM32 32-bit processor |
EM_KMX32 | 211 | KM211 KMX32 32-bit processor |
EM_KMX16 | 212 | KM211 KMX16 16-bit processor |
EM_KMX8 | 213 | KM211 KMX8 8-bit processor |
EM_KVARC | 214 | KM211 KVARC processor |
EM_CDP | 215 | Paneve CDP architecture family |
EM_COGE | 216 | Cognitive Smart Memory Processor |
EM_COOL | 217 | Bluechip Systems CoolEngine |
EM_NORC | 218 | Nanoradio Optimized RISC |
EM_CSR_KALIMBA | 219 | CSR Kalimba architecture family |
EM_Z80 | 220 | Zilog Z80 |
EM_VISIUM | 221 | Controls and Data Services VISIUMcore processor |
EM_FT32 | 222 | FTDI Chip FT32 high performance 32-bit RISC architecture |
EM_MOXIE | 223 | Moxie processor family |
EM_AMDGPU | 224 | AMD GPU architecture |
225 - 242 | ||
EM_RISCV | 243 | RISC-V |
我们也可以到elf.h里查看:
也就是ARM架构是40来表示。
2.4 e_version
这四个字节表示目标文件版本,只有0和1分别表示无效版本、当前版本:
2.5 e_entry
这四个字节表示程序入口的虚拟地址,如果是可执行文件,则程序指令从这个地址开始执行。但是像可重定位文件这些没有入口地址的,一般都为0。
2.6 e_phoff
这四个字节表示文件程序头表的相对偏移,单位字节。如果没有程序头表,则为0。
2.7 e_shoff
这四个字节表示文件段头表的相对偏移,单位字节。如果没有段头表,则为0。
关于程序头表和段头表在下面会有更加详细的解释。
2.8 e_flags
这四个字节表示ELF文件平台相关属性,一般格式为EF_machine_flag。其中ARM架构相关flag有:
2.9 e_ehsize
这两个字节表示ELF文件头的大小,单位字节。
2.10 e_phentsize
这两个字节表示程序头表中一个条目大小,单位字节,每个条目大小相同。
2.11 e_phnum
这两个字节表示程序头表中的条目数。没有程序头表,则为0。
2.12 e_shentsize
这两个字节表示段表描述符大小,单位字节。
2.13 e_shnum
这两个字节表示段表描述符数量。
2.14 e_shstrndx
这两个字节表示段表字符串表所在的段在段表中的下标。
2.15 实际文件分析
上图是笔者某个工程的elf文件,把它放在excel里分析如下:
然后使用readelf -a读取elf文件信息:
* section有段和节的意思,但是通常站在链接的角度,我们把section译作 “节” ,而站在执行角度我们把它的近义词segment译作 “段” 。此处分析借鉴了《程序员的自我修养-链接、装载与库》,所以叫做段表。
三、section header
以上是一个section header的标准结构,而段表就是一个包含一个或多个这样相同结构体的列表。
3.1 sh_name
这四个字节表示一个段的名字的偏移。由上文知道在elf的header里面专门有描述字符串段的索引,就是因为所有段的名字都存在这个段内(.shstrtab),而这个偏移就是在这个段里面的偏移。
3.2 sh_type
这四个字节表示段的类型。如下图:
Name | Value |
---|---|
SHT_NULL | 0 |
SHT_PROGBITS | 1 |
SHT_SYMTAB | 2 |
SHT_STRTAB | 3 |
SHT_RELA | 4 |
SHT_HASH | 5 |
SHT_DYNAMIC | 6 |
SHT_NOTE | 7 |
SHT_NOBITS | 8 |
SHT_REL | 9 |
SHT_SHLIB | 10 |
SHT_DYNSYM | 11 |
SHT_INIT_ARRAY | 14 |
SHT_FINI_ARRAY | 15 |
SHT_PREINIT_ARRAY | 16 |
SHT_GROUP | 17 |
SHT_SYMTAB_SHNDX | 18 |
SHT_LOOS | 0x60000000 |
SHT_HIOS | 0x6fffffff |
SHT_LOPROC | 0x70000000 |
SHT_HIPROC | 0x7fffffff |
SHT_LOUSER | 0x80000000 |
SHT_HIUSER | 0xffffffff |
3.2.1 SHT_NULL
此类型表示该段为非活跃的,它不关联任何有用的信息,也可以理解为无意义。
3.2.2 SHT_PROGBITS
该段所持有的所有信息都是由程序定义,比如代码段、数据段、程序段等都是这种,它们的意义是由程序决定的。
3.2.3 SHT_SYMTAB
该段的内容为完整的符号表。
3.2.4 SHT_STRTAB
该段的内容为字符串表。
3.2.5 SHT_RELA
该段内容为重定位表。
3.2.6 SHT_HASH
该段内容为符号表的哈希表。
3.2.7 SHT_DYNAMIC
该段包含动态链接的相关信息。
3.2.8 SHT_NOTE
该段内容为提示性信息。
3.2.9 SHT_NOBITS
该段在文件中不占用空间,其他同 SHT_PROGBITS 。
3.2.10 SHT_REL
该段内容为重定位信息。
3.2.11 SHT_SHLIB
保留。
3.2.12 SHT_DYNSYM
该段内容为动态链接的符号表。相当于把完整符号表 SHT_SYMTAB 里面关于动态链接的符号全部放在一个段内。
3.2.13 SHT_INIT_ARRAY
该段包含一个指向初始化函数的指针数组。
3.2.14 SHT_FINI_ARRAY
该段包含一个指向终止函数的指针数组。
3.2.15 SHT_PREINIT_ARRAY
本段包含一个指向函数的指针数组,这些函数在所有其他初始化函数之前被调用。
3.2.16 SHT_GROUP
该段定义了一个段组,即一组相关的段。
3.2.17 SHT_SYMTAB_SHNDX
该段与一个符号表部分相关联,如果该符号表引用的任何部分头索引包含转义值SHN_XINDEX,则需要这个段。
3.2.18 SHT_LOOS~SHT_HIOS
这个范围内的值留给特定的操作系统使用。
3.2.19 SHT_LOPROC~SHT_HIPROC
这个范围内的值留给特定的处理器使用。
3.2.20 SHT_LOUSER~SHT_HIUSER
用户可以使用该范围表示自定义段类型,而不用担心和标准段类型冲突。
3.3 sh_flags
这四个字节表示段的标志:
Name | Value |
---|---|
SHF_WRITE | 0x1 |
SHF_ALLOC | 0x2 |
SHF_EXECINSTR | 0x4 |
SHF_MERGE | 0x10 |
SHF_STRINGS | 0x20 |
SHF_INFO_LINK | 0x40 |
SHF_LINK_ORDER | 0x80 |
SHF_OS_NONCONFORMING | 0x100 |
SHF_GROUP | 0x200 |
SHF_TLS | 0x400 |
SHF_COMPRESSED | 0x800 |
SHF_MASKOS | 0x0ff00000 |
SHF_MASKPROC | 0xf0000000 |
它们分别表示:
3.3.1 SHF_WRITE
该段包含在程序执行时可写的数据。
3.3.2 SHF_ALLOC
该段在进程空间中必须要被分配空间。
3.3.3 SHF_EXECINSTR
该段包含可以被执行的机器码。
3.3.4 SHF_MERGE
该段的数据可以被合并以消除重复。除非SHF_STRINGS标志也被设置,否则段中的数据元素是统一大小的。每个元素的大小在段头表的sh_entsize字段中指定。如果SHF_STRINGS标志也被设置,数据元素由空尾的字符串组成。每个字符的大小在段头表的sh_entsize字段中指定。
3.3.5 SHF_STRINGS
该段中的数据元素由以空字符结尾的字符串组成。每个字符的大小在段头表的 sh_entsize
字段中指定。
3.3.6 SHF_INFO_LINK
该段的 sh_info
字段保存一个段头表索引。
3.3.7 SHF_LINK_ORDER
这个标志为链接编辑增加了特殊的排序要求。这些要求适用于本段头的sh_link字段引用另一段(被链接的段)的情况。如果这个部分与输出文件中的其他部分结合在一起,它必须以与这些部分相同的相对顺序出现,因为被链接的部分与被链接的部分结合在一起。
3.3.8 SHF_OS_NONCONFORMING
这一部分需要特殊的操作系统处理(超出标准的链接规则)以避免不正确的行为。如果这部分有一个sh_type值或者包含在操作系统特定范围内的sh_flags位,而处理这部分的链接编辑器不能识别这些值,那么链接编辑器应该以一个错误拒绝包含这部分的对象文件。
3.3.9 SHF_GROUP
这个部分是一个部分组的成员(可能是唯一的一个)。
3.3.10 SHF_TLS
该部分持有线程本地存储,这意味着每个独立的执行流程都有自己的该数据的不同实例。
3.3.11 SHF_COMPRESSED
这个标志标识了一个包含压缩数据的部分。SHF_COMPRESSED只适用于不可分配的部分,并且不能与SHF_ALLOC一起使用。此外,SHF_COMPRESSED不能应用于SHT_NOBITS类型的部分。
所有对压缩区的重定位都指定了对未压缩区数据的偏移量。因此,在应用重定位之前,有必要对部分数据进行解压。每个压缩部分都独立地指定了算法。允许在一个给定的ELF对象中的不同部分采用不同的压缩算法。
被压缩的部分以一个描述压缩相关信息的结构体开始,该结构体标识了压缩算法。压缩信息结构体如下:
typedef struct {
Elf32_Word ch_type;//压缩算法类型
Elf32_Word ch_size;//未压缩数据大小
Elf32_Word ch_addralign;//未压缩数据所需对齐方式
} Elf32_Chdr;
而压缩算法类型包含:
Name | Value |
---|---|
ELFCOMPRESS_ZLIB | 1 |
ELFCOMPRESS_LOOS | 0x60000000 |
ELFCOMPRESS_HIOS | 0x6fffffff |
ELFCOMPRESS_LOPROC | 0x70000000 |
ELFCOMPRESS_HIPROC | 0x7fffffff |
可以看到只有 1 是被指定为 ZLIB,其他 0x60000000 ~ 0x6fffffff 都保留给了操作系统去指定,而 0x7000000 ~ 0x7fffffff都保留给了处理器去指定。
3.3.12 SHF_MASKOS
保留给操作系统去指定。
3.3.13 SHF_MASKPROC
保留给处理器去指定。
更多信息请查看资料引用。
3.3.14 一些标准段的类型及其属性
3.4 sh_addr
段的虚拟地址,如果该段可以被加载,则次字段为该段被加载后在进程空间中的虚拟地址,否则为0 。
3.5 sh_offset
如果该段在文件中,表示该段在文件中的相对偏移。比如 SHT_NOBITS 类型的段该字段就没有意义。
3.6 sh_size
该段的长度。单位:字节。
3.7 sh_link
该字段保存一个段头表的索引链接,具体解释取决于段的类型。
3.8 sh_info
该字段包含额外的信息,具体解释取决于段的类型。
sh_link 和 sh_info 具体信息在下表:
sh_type | sh_link | sh_info |
---|---|---|
SHT_DYNAMIC | The section header index of the string table used by entries in the section. | 0 |
SHT_HASH | The section header index of the symbol table to which the hash table applies. | 0 |
SHT_REL SHT_RELA | The section header index of the associated symbol table. | The section header index of the section to which the relocation applies. |
SHT_SYMTAB SHT_DYNSYM | The section header index of the associated string table. | One greater than the symbol table index of the last local symbol (binding STB_LOCAL ). |
SHT_GROUP | The section header index of the associated symbol table. | The symbol table index of an entry in the associated symbol table. The name of the specified symbol table entry provides a signature for the section group. |
SHT_SYMTAB_SHNDX | The section header index of the associated symbol table section. | 0 |
3.9 sh_addralign
段地址对齐,此值是2的指数。比如8字节对齐,则sh_addralign = 3 。
3.10 sh_entsize
该字段在某些包含固定长度条目的段中,表示单个条目的大小,比如符号表。如果为0表示不包含固定长度条目的表。
3.11 实际文件分析
还使用上一节的elf文件,从ELF文件头可以看到Section headers的偏移是 0x00021824 ,单个项目大小是 40 字节,共有 22 个项目。我们找到段头表起始地址:
可以看到22个项目中第一个也就是索引0的项目40个字节全是0,但与标准规定相符:
段头表之【索引0】:
Name | Value | Note |
---|---|---|
sh_name | 0 | No name |
sh_type | SHT_NULL | Inactive |
sh_flags | 0 | No flags |
sh_addr | 0 | No address |
sh_offset | 0 | No offset |
sh_size | Unspecified | If non-zero, the actual number of section header entries |
sh_link | Unspecified | If non-zero, the index of the section header string table section |
sh_info | 0 | No auxiliary information |
sh_addralign | 0 | No alignment |
sh_entsize | 0 | No entries |
使用readelf -S也可以看到:
我们继续看第二个项目:
我们实际分析一下:
首先我们如果想知道名称,必须要先找到包含段名的字符串表,这也是为什么ELF文件头里面要把包含段名的字符串表的索引单独列出来的原因,由上一节我们知道段名字符串表索引是 21 ,注意索引是从 0 开始的。所以字符串表在段表里面的起始地址应该是 0x00021824 + 21 * 40 = 0x21B6C ,查看该地址处数据:
同样对它进行分析:
由该段在文件中的偏移我们可以找到该段内容的首地址:
注意,字符串表的查表方式为从索引开始,到0x00结束的字符串内容。可以看到从第9个字节开始到0x00用字符串表示刚好是 .shstrtab ,而从第27个字节开始到0x00为止字符串内容刚好是 .isr_vector ,与预期相符。
回到刚才段表中索引1的项,通过段名我们知道索引1的段表项保存的是向量表。它的类型是 SHT_PROGBITS ,所以它的内容被程序所定义,而且它需要被分配空间,它被加载时的虚拟地址是0x0800 0000,这个地址在STM32里就是0x0000 0000,这与向量表在运行时存放地址不谋而合。其次该段在文件中的相对偏移是0x010000,我们找到它的内容:
这与bin文件中地址相同:
四、program header
以上是一个program header的标准结构,程序头表就是包含一个或多个program header的列表。
4.1 p_type
段类型。仔细看会发现和段表很多类型有点类似。
Name | Value |
PT_NULL | 0 |
PT_LOAD | 1 |
PT_DYNAMIC | 2 |
PT_INTERP | 3 |
PT_NOTE | 4 |
PT_SHLIB | 5 |
PT_PHDR | 6 |
PT_TLS | 7 |
PT_LOOS | 0x60000000 |
PT_HIOS | 0x6fffffff |
PT_LOPROC | 0x70000000 |
PT_HIPROC | 0x7fffffff |
4.1.1 PT_NULL
数组元素未使用。
4.1.2 PT_LOAD
数组元素指定一个可加载段,由p_filesz和p_memsz描述。
4.1.3 PT_DYNAMIC
数组元素指定动态链接信息。
4.1.4 PT_INTERP
数组元素指定一个以null结尾的路径名的位置与大小,被解释器调用。只对可执行文件有意义。
4.1.5 PT_NOTE
数组元素指定辅助信息的位置和大小。
4.1.6 PT_SHLIB
保留。
4.1.7 PT_PHDR
数组元素指定程序头表本身在文件和程序程序运行空间中的位置和大小。
4.1.8 PT_TLS
数组元素指定TLS模版。(Thread-Local Storage)
4.1.9 PT_LOOS~PT_HIOS
此范围内类型由操作系统指定。
4.1.10 PT_LOPROC~PT_HIPROC
此范围内类型由处理器指定。
4.2 p_offset
段在文件中的相对偏移。
4.3 p_vaddr
段在内存中的虚拟地址。
4.4 p_paddr
段的物理地址。
4.5 p_filesz
段在镜像文件中的大小,单位:字节。
4.6 p_memsz
段在运行运行空间中的大小,单位:字节。
4.7 p_flags
段的相关标志。如下图:
Flags | Value | Exact | Allowable |
---|---|---|---|
none | 0 | All access denied | All access denied |
PF_X | 1 | Execute only | Read, execute |
PF_W | 2 | Write only | Read, write, execute |
PF_W+PF_X | 3 | Write, execute | Read, write, execute |
PF_R | 4 | Read only | Read, execute |
PF_R+PF_X | 5 | Read, execute | Read, execute |
PF_R+PF_W | 6 | Read, write | Read, write, execute |
PF_R+PF_W+PF_X | 7 | Read, write, execute | Read, write, execute |
4.8 p_align
该字段指定段的对齐字节。
4.9 实际文件分析
还是由之前的elf文件分析,由ELF文件头可知,程序头表单个项大小为32字节,一共有3个项,程序头表起始位置为 0x34 。我们看索引0的 program header:
实际分析如下:
使用readelf -l 查看程序头表可以看到:
查看3个段的划分:
资料引用:
SYSTEM V ABI 4.1
System V Application Binary Interface - DRAFT - 10 June 2013
《程序员的自我修养——链接、装载与库》