ELF文件头分析

ELF Header

Some object file control structures can grow, because the ELF header contains their actual sizes. If the object file format changes, a program may encounter control structures that are larger or smaller than expected. Programs might therefore ignore ``extra'' information. The treatment of ``missing'' information depends on context and will be specified when and if extensions are defined.

ELF文件头结构就像是一个总览图,描述了整个文件的布局情况。

因此在ELF文件头结构允许的数值范围内,整个文件的大小是可以动态增减的。但是由于历史原因以及像C++这样的语言的发展,ELF文件头结构中有些数据成员的大小已经显得捉襟见肘,不足以表示现实世界中的ELF文件了。例如,表示程序头表表项个数的e_phnum和表示节头表表项个数的e_shnum的类型都是Elf32_Half,在32-bit和64-bit平台上都是16位的无符号数,也就是说最大数值为65536,那么问题来了,一旦超过这个数值怎么办?别急,继续看下文,总有解决办法的!

/* The ELF file header.  This appears at the start of every ELF file.  */

#define EI_NIDENT (16)

typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf32_Half    e_type;                 /* Object file type */
  Elf32_Half    e_machine;              /* Architecture */
  Elf32_Word    e_version;              /* Object file version */
  Elf32_Addr    e_entry;                /* Entry point virtual address */
  Elf32_Off     e_phoff;                /* Program header table file offset */
  Elf32_Off     e_shoff;                /* Section header table file offset */
  Elf32_Word    e_flags;                /* Processor-specific flags */
  Elf32_Half    e_ehsize;               /* ELF header size in bytes */
  Elf32_Half    e_phentsize;            /* Program header table entry size */
  Elf32_Half    e_phnum;                /* Program header table entry count */
  Elf32_Half    e_shentsize;            /* Section header table entry size */
  Elf32_Half    e_shnum;                /* Section header table entry count */
  Elf32_Half    e_shstrndx;             /* Section header string table index */
} Elf32_Ehdr;

typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf64_Half    e_type;                 /* Object file type */
  Elf64_Half    e_machine;              /* Architecture */
  Elf64_Word    e_version;              /* Object file version */
  Elf64_Addr    e_entry;                /* Entry point virtual address */
  Elf64_Off     e_phoff;                /* Program header table file offset */
  Elf64_Off     e_shoff;                /* Section header table file offset */
  Elf64_Word    e_flags;                /* Processor-specific flags */
  Elf64_Half    e_ehsize;               /* ELF header size in bytes */
  Elf64_Half    e_phentsize;            /* Program header table entry size */
  Elf64_Half    e_phnum;                /* Program header table entry count */
  Elf64_Half    e_shentsize;            /* Section header table entry size */
  Elf64_Half    e_shnum;                /* Section header table entry count */
  Elf64_Half    e_shstrndx;             /* Section header string table index */
} Elf64_Ehdr; 
  • e_ident

The initial bytes mark the file as an object file and provide machine-independent data with which to decode and interpret the file's contents. Complete descriptions appear below in ``ELF Identification''.

开始的这16字节标识这是个ELF格式的文件,并且提供机器无关的信息数据(这样不管在什么平台上,对这些信息的解析方式都是一样的,因为单字节不牵扯大小端问题),根据这些信息数据就可以很方便的解析文件的剩余内容。对这些信息的详细描述,请见后文的“ELF Identification.”。

  • e_type

This member identifies the object file type.

e_type表示具体的ELF文件类型。

Although the core file contents are unspecified, type ET_CORE is reserved to mark the file. Values from ET_LOOS through ET_HIOS (inclusive) are reserved for operating system-specific semantics. Values from ET_LOPROC through ET_HIPROC (inclusive) are reserved for processor-specific semantics. If meanings are specified, the processor supplement explains them. Other values are reserved and will be assigned to new object file types as necessary.

目前尽管内存转储文件(core file)的格式还没有标准定义,但是我们依然保留ET_CORE来标识这个类型的文件。从ET_LOOS ~ ET_HIOS的值用于操作系统实现特定功能,从ET_LOPROC ~ ET_HIPROC的值用于处理器实现特定的功能。其它的值同样保留为了将来做扩展时使用。

  • e_machine

This member's value specifies the required architecture for an individual file.

e_machine用于指出对应的处理器架构。

Other values are reserved and will be assigned to new machines as necessary. Processor-specific ELF names use the machine name to distinguish them. For example, the flags mentioned below use the prefix EF_; a flag named WIDGET for the EM_XYZ machine would be called EF_XYZ_WIDGET.

除了上面表格之外的数值目前都保留用以将来扩展新的处理器架构。

具有处理器独有特性的ELF文件中的e_flags命名时用到了这里的e_machine,因此当e_machine是EM_XYZ时,处理器独有特性名为WIDGET,那么最终的e_flags的名称为EF_XYZ_WIDGET。

  • e_version

This member identifies the object file version.

e_version指出ELF文件格式标准 的版本号。

The value 1 signifies the original file format; extensions will create new versions with higher numbers. Although the value of EV_CURRENT is shown as 1 in the previous table, it will change as necessary to reflect the current version number.

数值1代表最初的版本号,因为在将来有可能对ELF文件格式做新的扩充,因此就有更高的版本号出现。EV_CURRENT永远代表最新的版本号。

  • e_entry

This member gives the virtual address to which the system first transfers control, thus starting the process. If the file has no associated entry point, this member holds zero.

成员e_entry的值给出的是系统将控制权交给程序时的虚拟地址,进程才得以启动。

  • e_phoff

This member holds the program header table's file offset in bytes. If the file has no program header table, this member holds zero.

e_phoff的数值代表的意思是程序头表在文件中的偏移。

  • e_shoff

This member holds the section header table's file offset in bytes. If the file has no section header table, this member holds zero.

e_shoff的数值代表的意思是节头表在文件中的偏移。

  • e_flags

This member holds processor-specific flags associated with the file. Flag names take the form EF_machine_flag.

e_flags指出处理器独有的特性。它的格式为EF_<machine>_<flag>。

  • e_ehsize

This member holds the ELF header's size in bytes.

e_ehsize代表的就是ELF文件头的大小。

  • e_phentsize

This member holds the size in bytes of one entry in the file's program header table; all entries are the same size.

e_phentsize代表的意思是程序头表表项的大小。

  • e_phnum

This member holds the number of entries in the program header table. Thus the product of e_phentsize and e_phnum gives the table's size in bytes. If a file has no program header table, e_phnum holds the value zero.

e_phnum代表的意思是程序头表表项总个数。

 

If the number of entries in the program header table is larger than or equal to PN_XNUM(0xffff), this member holds PN_XNUM(0xffff) and the real number of entries in the program header table is held in the sh_info member of the initial entry in section header table. Otherwise, the sh_info member of the initial entry contains the value zero.

如果程序头表中的表项总数大于或者等于PN_XNUM(0xffff)个,那么e_phnum数值被设置为PN_XNUM(0xffff),而真实的表项总数存储在第一个节头表表项中的sh_info数据成员中。在其它情况下,这个sh_info的值为0。


PN_XNUM  This is defined as 0xffff, the largest number e_phnum can have, specifying where the actual number of  program  headers is assigned.

PN_XNUM是e_phnum最大的值,代表的意思是真实的程序头表表项个数已经记录在别处。

  • e_shentsize

This member holds a section header's size in bytes. A section header is one entry in the section header table; all entries are the same size.

e_shentsize代表的意思是节头表表项的大小。

  • e_shnum

This member holds the number of entries in the section header table. Thus the product of e_shentsize and e_shnum gives the section header table's size in bytes. If a file has no section header table, e_shnum holds the value zero.

e_shnum代表的意思是节头表表项总个数。

 

If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), this member has the value zero and the actual number of section header table entries is contained in the sh_size field of the section header at index 0. (Otherwise, the sh_size member of the initial entry contains 0.)

如果节头表中的表项总数大于或者等于SHN_LORESERVE(0xff00),那么e_shnum值被设置为0,而真实的表项总数存储在第一个节头表表项中的sh_size数据成员中。在其它情况下,这个sh_size的值为0。

  • e_shstrndx

This member holds the section header table index of the entry associated with the section name string table. If the file has no section name string table, this member holds the value SHN_UNDEF. See ``Sections'' and ``String Table'' below for more information.

e_shstrndx代表的是节头表的一个下标值,对应的节头表表项包含的是一个特殊的节的信息。这个特殊的节之所以特殊是因为它里面连续存储有所有节的名字。

 

If the section name string table section index is greater than or equal to SHN_LORESERVE (0xff00), this member has the value SHN_XINDEX (0xffff) and the actual index of the section name string table section is contained in the sh_link field of the section header at index 0. (Otherwise, the sh_link member of the initial entry contains 0.)

如果e_shstrndx的数值大于或者等于SHN_LORESERVE(0xff00),那么e_shstrndx的值被设置为SHN_XINDEX(0xffff),而真实的下标值存储在第一个节头表表项中的sh_link数据成员中。在其它情况下,这个sh_link的值为0。

 

ELF Identification   ELF平台无关的标识信息

As mentioned above, ELF provides an object file framework to support multiple processors, multiple data encodings, and multiple classes of machines. To support this object file family, the initial bytes of the file specify how to interpret the file, independent of the processor on which the inquiry is made and independent of the file's remaining contents.

如前所述,ELF文件格式理论上应该支持多种处理器,多种数据格式,多平台。

支持多处理器意思是说可以支持intel处理器,也可以支持ARM处理器等。

支持多种数据格式指的是大小端的问题。

多平台指的是32位平台还是64位平台等等。

如何做到这些呢?答案是ELF文件头有一些控制信息,而这些控制信息是与机器无关的,同样与文件的其余内容也是无关的。


The initial bytes of an ELF header (and an object file) correspond to the e_ident member.

这些与机器无关的控制信息其实就是ELF文件头中的e_ident成员。

 

These indexes access bytes that hold the following values.

通过这些下标值来获得数组e_ident的元素。各元素含义如下:

 

  • EI_MAG0 to EI_MAG3

A file's first 4 bytes hold a ``magic number,'' identifying the file as an ELF object file.

文件一开始的4个字节内容我们称之为”魔数“,用来标识这是一个ELF文件格式的文件。

  • EI_CLASS

The next byte, e_ident[EI_CLASS], identifies the file's class, or capacity.

e_ident[EI_CLASS]标识目标平台是多少位的。

The file format is designed to be portable among machines of various sizes, without imposing the sizes of the largest machine on the smallest. The class of the file defines the basic types used by the data structures of the object file container itself. The data contained in object file sections may follow a different programming model. If so, the processor supplement describes the model used.

ELF文件是可以适应多种不同的平台的。 为什么呢?

那是因为在每个平台上我们都根据每个平台的位数自定义数据类型的大小。例如ElfXX_Addr和ElfXX_Off这两个类型会根据是32-bit平台还是64-bit平台分别定义成uint32_t和uint64_t。

然而,不同的平台,节的内容一般是彼此不同的,因为它们是平台相关的 -- 例如每个平台的重定位类型是彼此不同的。不过不用担心,每个平台的手册中都会给出详细的描述。

Class ELFCLASS32 supports machines with 32-bit architectures. It uses the basic types defined in the table labeled ``32-Bit Data Types.''

ELFCLASS32表明是32-bit平台。

Class ELFCLASS64 supports machines with 64-bit architectures. It uses the basic types defined in the table labeled ``64-Bit Data Types.''

ELFCLASS64表明是64-bit平台。

Other classes will be defined as necessary, with different basic types and sizes for object file data.

将来出现其它位数的平台也是有可能的,到时就要根据新平台的特性来定义数据类型的大小。

  • EI_DATA

Byte e_ident[EI_DATA] specifies the encoding of both the data structures used by object file container and data contained in object file sections. The following encodings are currently defined.

e_ident[EI_DATA]指定数据的大小端格式。同时用于指定文件中的数据结构以及其余文件内容的大小端格式。

Other values are reserved and will be assigned to new encodings as necessary.

其它值用作保留值。

NOTEPrimarily for the convenience of code that looks at the ELF file at runtime, the ELF data structures are intended to have the same byte order as that of the running program.

注意一般为了方便,读取分析ELF文件格式的程序(例如readelf)会将ELF文件内容以所在平台的数据大小端方式读取进内存(因此必要时会进行大小端之间的转换),例如在x86平台(小端格式)上读取分析一个大端数据格式的ELF文件。

  • EI_VERSION

Byte e_ident[EI_VERSION] specifies the ELF header version number. Currently, this value must be EV_CURRENT, as explained above for e_version.

e_ident[EI_VERSION]指明ELF文件格式的版本号。它的值必须是EV_CURRENT,因为EV_CURRENT总是代表最新的版本号。

  • EI_OSABI

Byte e_ident[EI_OSABI] identifies the OS- or ABI-specific ELF extensions used by this file. Some fields in other ELF structures have flags and values that have operating system and/or ABI specific meanings; the interpretation of those fields is determined by the value of this byte. If the object file does not use any extensions, it is recommended that this byte be set to 0. If the value for this byte is 64 through 255, its meaning depends on the value of the e_machine header member. The ABI processor supplement for an architecture can define its own associated set of values for this byte in this range. If the processor supplement does not specify a set of values, one of the following values shall be used, where 0 can also be taken to mean unspecified.

e_ident[EI_OSABI]表明ELF文件中是否用到了特定操作系统或者特定应用程序二进制接口(ABI -- Application Binary Interface)的扩展。后续我们会看到,ELF其它结构体中有的数据成员的值会用到特定操作系统或者ABI的扩展值,那么如何解释它们就是依据e_ident[EI_OSABI]来决定的。

当然,如果ELF文件中没有用到什么扩展,那大可设置e_ident[EI_OSABI]为0即可。

如果e_ident[EI_OSABI]值在64 ~ 255之间,那么会根据e_machine的值来解释扩展值。

  • EI_ABIVERSION

Byte e_ident[EI_ABIVERSION] identifies the version of the ABI to which the object is targeted. This field is used to distinguish among incompatible versions of an ABI. The interpretation of this version number is dependent on the ABI identified by the EI_OSABI field. If no values are specified for the EI_OSABI field by the processor supplement or no version values are specified for the ABI determined by a particular value of the EI_OSABI byte, the value 0 shall be used for the EI_ABIVERSION byte; it indicates unspecified.

如果e_ident[EI_OSABI]指定了ABI,那么e_ident[EI_ABIVERSION]用以指定ABI特定的版本号。因为有时同一平台的不同版本ABI之间是彼此不兼容的。如果e_ident[EI_ABIVERSION]的值为0,表明未指定版本号。

  • EI_PAD

This value marks the beginning of the unused bytes in e_ident. These bytes are reserved and set to zero; programs that read object files should ignore them. The value of EI_PAD will change in the future if currently unused bytes are given meanings.
EI_PAD下标值表明从EI_PAD开始,后续的字节都是目前没有使用的。它们的值设置为0,用作将来扩充使用。因此读取分析ELF文件的程序应该忽略这些字节。当然了,EI_PAD的值是可以改变的,因为将来可能会扩充使用这些未使用的字节。

 

Data Encoding 数据编码格式

A file's data encoding specifies how to interpret the basic objects in a file. Class ELFCLASS32 files use objects that occupy 1, 2, and 4 bytes. Class ELFCLASS64 files use objects that occupy 1, 2, 4, and 8 bytes. Under the defined encodings, objects are represented as shown below.
大小端格式决定了如何去解释数据。我们知道,在32-bit平台上,一个数据对象的大小可以为1,2和4字节,在64-bit平台上,一个数据对象的大小可以为1,2,4和8字节。那么分别在大小端格式下,它们的字节排列方式是不一样的。

Encoding ELFDATA2LSB specifies 2's complement values, with the least significant byte occupying the lowest address.

ELFDATA2LSB表明小端格式,即低字节存储在低字节,高字节存储在高字节。

Encoding ELFDATA2MSB specifies 2's complement values, with the most significant byte occupying the lowest address.

ELFDATA2MSB表明大端格式,字节排列方式与ELFDATA2LSB相反。

以下程序用于输出ELF文件头的信息,模仿readelf的--file-header选项:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <link.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <elf.h>
#include <error.h>
#include <errno.h>
#include <assert.h>
#include <inttypes.h>

static const char *elf_osabi(uint32_t osabi, char *buf) 
{
	assert(buf);

	switch (osabi) {
//		case ELFOSABI_NONE:
		case ELFOSABI_SYSV:
			(void)strcpy(buf, "UNIX - System V"); break;	
		case ELFOSABI_HPUX:
			(void)strcpy(buf, "HP/UX"); break;
		case ELFOSABI_NETBSD:
			(void)strcpy(buf, "NetBSD"); break;
//		case ELFOSABI_GNU:
//			(void)strcpy(buf, "GNU"); break;
		case ELFOSABI_LINUX:	
			(void)strcpy(buf, "Linux"); break;
		case ELFOSABI_SOLARIS:	
			(void)strcpy(buf, "Solaris"); break;
		case ELFOSABI_AIX:
			(void)strcpy(buf, "AIX"); break;
		case ELFOSABI_IRIX:		
			(void)strcpy(buf, "Irix"); break;
		case ELFOSABI_FREEBSD:	
			(void)strcpy(buf, "FreeBSD"); break;
		case ELFOSABI_TRU64:
			(void)strcpy(buf, "TRU64"); break;
		case ELFOSABI_MODESTO:	
			(void)strcpy(buf, "Modesto"); break;
		case ELFOSABI_OPENBSD:
			(void)strcpy(buf, "OpenBSD"); break;
		case ELFOSABI_ARM_AEABI:  
			(void)strcpy(buf, "ARM EABIARM"); break;
		case ELFOSABI_ARM:
			(void)strcpy(buf, "ARM"); break;
		case ELFOSABI_STANDALONE:
			(void)strcpy(buf, "Stand alone"); break;
		default:
			(void)sprintf(buf, "%s: %d", "<unknown>", osabi);
			break;
	}

	return (buf); 
}

static const char *elf_type(uint32_t type, char *buf)
{
	assert(buf);

	switch (type) {
		case ET_NONE:
			(void)strcpy(buf, "NONE (None)");	break;
		case ET_REL:
			(void)strcpy(buf, "REL (Relocatable file)");	break;
		case ET_EXEC:
			(void)strcpy(buf, "EXEC (Executable file)");	break;
		case ET_DYN:
			(void)strcpy(buf, "DYN (Shared object file)");	break;
		case ET_CORE:
			(void)strcpy(buf, "CORE (Core file)");	break;
		default:
			if (type >= ET_LOOS && type <= ET_HIOS) {
				(void)sprintf(buf, "<os: %#x>", type);
			} else if (type >= ET_LOPROC) {
				(void)sprintf(buf, "<proc: %#x>", type);	
			} else {
				(void)sprintf(buf, "<unknown: %#x>", type);
			}
			break;
	}

	return (buf);
}

static const char *elf_machine(uint32_t machine, char *buf)
{
	assert(buf);

	switch (machine) {
		case EM_NONE: return "Unknown machine";
		case EM_M32: return "AT&T WE32100";
		case EM_SPARC: return "Sun SPARC";
		case EM_386: return "Intel 80386";
		case EM_68K: return "Motorola 68000";
		case EM_88K: return "Motorola 88000";
		case EM_860: return "Intel i860";
		case EM_MIPS: return "MIPS R3000 Big-Endian only";
		case EM_S370: return "IBM System/370";
		case EM_MIPS_RS3_LE: return "MIPS R3000 Little-Endian";
		case EM_PARISC: return "HP PA-RISC";
		case EM_VPP500: return "Fujitsu VPP500";
		case EM_SPARC32PLUS: return "SPARC v8plus";
		case EM_960: return "Intel 80960";
		case EM_PPC: return "PowerPC 32-bit";
		case EM_PPC64: return "PowerPC 64-bit";
		case EM_S390: return "IBM System/390";
		case EM_V800: return "NEC V800";
		case EM_FR20: return "Fujitsu FR20";
		case EM_RH32: return "TRW RH-32";
		case EM_RCE: return "Motorola RCE";
		case EM_ARM: return "ARM";
		case EM_SH: return "Hitachi SH";
		case EM_SPARCV9: return "SPARC v9 64-bit";
		case EM_TRICORE: return "Siemens TriCore embedded processor";
		case EM_ARC: return "Argonaut RISC Core";
		case EM_H8_300: return "Hitachi H8/300";
		case EM_H8_300H: return "Hitachi H8/300H";
		case EM_H8S: return "Hitachi H8S";
		case EM_H8_500: return "Hitachi H8/500";
		case EM_IA_64: return "Intel IA-64 Processor";
		case EM_MIPS_X: return "Stanford MIPS-X";
		case EM_COLDFIRE: return "Motorola ColdFire";
		case EM_68HC12: return "Motorola M68HC12";
		case EM_MMA: return "Fujitsu MMA";
		case EM_PCP: return "Siemens PCP";
		case EM_NCPU: return "Sony nCPU";
		case EM_NDR1: return "Denso NDR1 microprocessor";
		case EM_STARCORE: return "Motorola Star*Core processor";
		case EM_ME16: return "Toyota ME16 processor";
		case EM_ST100: return "STMicroelectronics ST100 processor";
		case EM_TINYJ: return "Advanced Logic Corp. TinyJ processor";
		case EM_X86_64: return "Advanced Micro Devices x86-64";
		case EM_PDSP: return "Sony DSP Processor";
		case EM_FX66: return "Siemens FX66 microcontroller";
		case EM_ST9PLUS: return "STMicroelectronics ST9+ 8/16 microcontroller";
		case EM_ST7: return "STmicroelectronics ST7 8-bit microcontroller";
		case EM_68HC16: return "Motorola MC68HC16 microcontroller";
		case EM_68HC11: return "Motorola MC68HC11 microcontroller";
		case EM_68HC08: return "Motorola MC68HC08 microcontroller";
		case EM_68HC05: return "Motorola MC68HC05 microcontroller";
		case EM_SVX: return "Silicon Graphics SVx";
		case EM_ST19: return "STMicroelectronics ST19 8-bit mc";
		case EM_VAX: return "Digital VAX";
		case EM_CRIS: return "Axis Communications 32-bit embedded processor";
		case EM_JAVELIN: return "Infineon Tech. 32bit embedded processor";
		case EM_FIREPATH: return "Element 14 64-bit DSP Processor";
		case EM_ZSP: return "LSI Logic 16-bit DSP Processor";
		case EM_MMIX: return "Donald Knuth's educational 64-bit proc";
		case EM_HUANY: return "Harvard University MI object files";
		case EM_PRISM: return "SiTera Prism";
		case EM_AVR: return "Atmel AVR 8-bit microcontroller";
		case EM_FR30: return "Fujitsu FR30";
		case EM_D10V: return "Mitsubishi D10V";
		case EM_D30V: return "Mitsubishi D30V";
		case EM_V850: return "NEC v850";
		case EM_M32R: return "Mitsubishi M32R";
		case EM_MN10300: return "Matsushita MN10300";
		case EM_MN10200: return "Matsushita MN10200";
		case EM_PJ: return "picoJava";
		case EM_OPENRISC: return "OpenRISC 32-bit embedded processor";
		case EM_ARC_A5: return "ARC Cores Tangent-A5";
		case EM_XTENSA: return "Tensilica Xtensa Architecture";
		case EM_AARCH64: return "AArch64";
		default:
			(void)sprintf(buf, "<unknown: %#x>", machine);
			break;
		}

	return (buf);
}

static const char *elf_flags(uint32_t machine, uint32_t flags, char *buf)
{
	assert(buf);

	switch (machine) {
		case EM_386: 
		case EM_X86_64:
			(void)strcpy(buf, "");
		case EM_ARM:
			/* 平台相关的东西,这里略之 */
			(void)strcpy(buf, "");
			break;	
		default:
			/* ... */	 
			break;
	}

	return (buf);
}

static void print_ehdr(ElfW(Ehdr) *ehdr, ElfW(Shdr) *shdr) 
{
	ElfW(Half) phnum, shnum, shstrndx;
	char buf[512];

	phnum = ehdr->e_phnum;
	shnum = ehdr->e_shnum;
	shstrndx = ehdr->e_shstrndx;

	/* e_ident */
	printf("ELF Header:\n  Magic:   ");
	for (int i = 0; i < EI_NIDENT; i++) {
		printf("%02hhx ", ehdr->e_ident[i]);
	}
	printf("\n");

	printf("%-37s%s\n", "  Class:", 
			ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? "ELF32"
			: ehdr->e_ident[EI_CLASS] == ELFCLASS64 ? "ELF64" 
			: "\?\?\?");
	printf("%-37s%s\n", "  Data:",
			ehdr->e_ident[EI_DATA] == ELFDATA2LSB ? "2's complement, little endian"
			: ehdr->e_ident[EI_DATA] == ELFDATA2MSB ? "2's complement, big endian"
			: "\?\?\?");
	printf("%-37s%hhd (%s)\n", "  Version:", ehdr->e_ident[EI_VERSION],
				ehdr->e_ident[EI_VERSION] == EV_CURRENT ? "current": "\?\?\?"); 
	printf("%-37s%s\n", "  OS/ABI:", elf_osabi(ehdr->e_ident[EI_OSABI], buf));
	printf("%-37s%hhd\n", "  ABI Version:", ehdr->e_ident[EI_ABIVERSION]);
	
	/* e_type */
	printf("%-37s%s\n", "  Type:", elf_type(ehdr->e_type, buf));	
	/* e_machine */
	printf("%-37s%s\n", "  Machine:", elf_machine(ehdr->e_machine, buf));
	/* e_version */
	printf("%-37s%#x\n", "  Version:", ehdr->e_version);
	/* e_entry */
//	printf("%-37s%#" PRIxPTR "\n", "  Entry point address:", (uintptr_t)ehdr->e_entry);
	printf("%-37s%#jx\n", "  Entry point address:", (uintmax_t)ehdr->e_entry);

	/* e_phoff */
//	printf("%-37s%#" PRId64 " (bytes into file)\n", 
//			"  Start of program headers:", ehdr->e_phoff);
	printf("%-37s%ju (bytes into file)\n", 
			"  Start of program headers:", (uintmax_t)ehdr->e_phoff);
	/* e_shoff */
	printf("%-37s%ju (bytes into file)\n", 
			"  Start of section headers:", (uintmax_t)ehdr->e_shoff);

	/* e_flags */
	printf("%-37s%#x, %s\n", "  Flags:", 
			ehdr->e_flags, elf_flags(ehdr->e_machine, ehdr->e_flags, buf));
	/* e_ehsize */
	printf("%-37s%u (bytes)\n", "  Size of this header:", ehdr->e_ehsize);

	/* e_phentsize */
	printf("%-37s%u (bytes)\n", "  Size of program headers:", ehdr->e_phentsize);
	/* e_phnum */
	printf("%-37s%u", "  Number of program headers:", phnum); 
	if (phnum == PN_XNUM) {
		printf(" (%u)\n", shdr[0].sh_info);
	}

	/* e_shentsize */
	printf("%-37s%u (bytes)\n", "  Size of section headers:", ehdr->e_shentsize);
	/* e_shnum */
	printf("%-37s%u\n", "  Number of section headers:", shnum); 
	if (shnum == SHN_UNDEF) {
		printf(" (%ju)\n", (uintmax_t)shdr[0].sh_size);
	}	

	/* e_shstrndx */
	printf("%-37s%u\n", "  Section header string table index:", shstrndx);
	if (shstrndx == SHN_XINDEX) {
		printf(" (%u)\n", shdr[0].sh_link);	
	}
}

int main(int argc, const char *argv[])
{
	int fd;
	char *file_mmbase;
	struct stat file_status;
	size_t fsize;
	ElfW(Ehdr) *ehdr;
	ElfW(Shdr) *shdr;

	if (argc != 2) {
		error(EXIT_FAILURE, 0, "Usage: %s file-name", argv[0]);
	}

	if ((fd = open(argv[1], O_RDONLY)) < 0) {
		error(EXIT_FAILURE, errno, "open %s failed", argv[1]);	
	}

	if (fstat(fd, &file_status) < 0) {
		error(EXIT_FAILURE, errno, "get file %s info err", argv[1]);	
	}
	fsize = (size_t)file_status.st_size;

	if ((file_mmbase = mmap(NULL, fsize, PROT_READ, 
				MAP_PRIVATE, fd, (off_t)0)) == MAP_FAILED) {
		error(EXIT_FAILURE, errno, "mmap file %s err", argv[1]);
	}

	ehdr = (ElfW(Ehdr) *)file_mmbase;
	shdr = (ElfW(Shdr) *)(file_mmbase + ehdr->e_shoff);
	print_ehdr(ehdr, shdr);
	
	(void)munmap(file_mmbase, fsize);
	(void)close(fd);
	
	exit(EXIT_SUCCESS);
}

程序输出结果如下:

[10:18:02@astrol:/tmp]$ readelf --file-header print_ehdr
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048590
  Start of program headers:          52 (bytes into file)
  Start of section headers:          15148 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         30
  Section header string table index: 27
[10:18:10@astrol:/tmp]$ ./print_ehdr print_ehdr
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048590
  Start of program headers:          52 (bytes into file)
  Start of section headers:          15148 (bytes into file)
  Flags:                             0,
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9  Size of section headers:           40 (bytes)
  Number of section headers:         30
  Section header string table index: 27

 

以下是利用libelf库来输出ELF header的信息:

#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdint.h>
#include <unistd.h>

static void print_ehdr(Elf *pelf, GElf_Ehdr *ehdr)
{
#define PRINT_FMT	"%-37s%#jx\n"	
#define PRINT_HDR(field) do { (void)printf(PRINT_FMT, #field, (uintmax_t)ehdr->field); } while (0); 

	for (int i = 0; i < EI_NIDENT; i++) {
		printf("%02hhx ", ehdr->e_ident[i]);	
	}
	putchar('\n');

	PRINT_HDR(e_type);
	PRINT_HDR(e_machine);
	PRINT_HDR(e_version);
	PRINT_HDR(e_entry);
	PRINT_HDR(e_phoff);
	PRINT_HDR(e_shoff);
	PRINT_HDR(e_flags);
	PRINT_HDR(e_ehsize);
	PRINT_HDR(e_phentsize);
//	PRINT_HDR(e_phnum);
	PRINT_HDR(e_shentsize);
//	PRINT_HDR(e_shnum);
//	PRINT_HDR(e_shstrndx);

	size_t phnum, shnum, shstrndx;

	if (elf_getphdrnum(pelf, &phnum) == -1) {
		(void)fprintf(stderr, "elf_getphdrnum() failed: %s.", elf_errmsg(-1));
	} else {
		printf(PRINT_FMT, "(e_phnum)", (uintmax_t)phnum);
	}

	if (elf_getshdrnum(pelf, &shnum) == -1) {
		(void)fprintf(stderr, "elf_getshdrnum() failed: %s.", elf_errmsg(-1));
	} else {
		printf(PRINT_FMT, "(e_shnum)", (uintmax_t)shnum);
	}

	if (elf_getshdrstrndx(pelf, &shstrndx) == -1) {
		(void)fprintf(stderr, "elf_getshdrstrndx() failed: %s.", elf_errmsg(-1));
	} else {
		printf(PRINT_FMT, "(e_shstrndx)", (uintmax_t)shstrndx);
	}

#undef PRINT_FMT
#undef PRINT_HDR 
}

int main(int argc, const char *argv[])
{
	int fd, class;
	Elf *pelf = NULL;
	GElf_Ehdr ehdr;
	
	if (argc != 2)
		errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);

	if (elf_version(EV_CURRENT) == EV_NONE)
		errx(EXIT_FAILURE, "ELF library initializztion "
			"failed: %s", elf_errmsg(-1));

	if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
		errx(EXIT_FAILURE, "open \"%s\" failed", argv[1]);

	if (!(pelf = elf_begin(fd, ELF_C_READ, NULL)))
		errx(EXIT_FAILURE, "elf_begin() failed: %s", elf_errmsg(-1));

	if (elf_kind(pelf) != ELF_K_ELF)
		errx(EXIT_FAILURE, "\"%s\" is not an ELF object.", argv[1]);

	// get the elf header
	if (gelf_getehdr(pelf, &ehdr) == NULL)
		errx(EXIT_FAILURE, "getehdr() failed: %s.", elf_errmsg(-1));

	// get elf class ()
	if ((class = gelf_getclass(pelf)) == ELFCLASSNONE)
		errx(EXIT_FAILURE, "getclass() failed: %s.", elf_errmsg(-1));
	// print the elf class
	printf("%s: %d-bit ELF object\n", argv[1], 
								(class == ELFCLASS32) ? 32 : 64);

	// print header
	print_ehdr(pelf, &ehdr);	
	
	elf_end(pelf);	

	exit(EXIT_SUCCESS);
}

参考链接:

ELF Header

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值