天天看點

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

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,那麼問題來了,一旦超過這個數值怎麼辦?别急,繼續看下文,總有解決辦法的!

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式
/* 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檔案類型。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

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用于指出對應的處理器架構。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

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檔案格式标準 的版本号。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

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成員。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

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檔案格式的檔案。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式
  • EI_CLASS

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

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

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

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]指定資料的大小端格式。同時用于指定檔案中的資料結構以及其餘檔案内容的大小端格式。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

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

其它值用作保留值。

NOTE:Primarily 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的值來解釋擴充值。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式
  • 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表明小端格式,即低位元組存儲在低位元組,高位元組存儲在高位元組。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

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

ELFDATA2MSB表明大端格式,位元組排列方式與ELFDATA2LSB相反。

ELF檔案頭分析ELF HeaderELF Identification   ELF平台無關的辨別資訊Data Encoding 資料編碼格式

以下程式用于輸出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:[email protected]:/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:[email protected]:/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》