這是ELF的layout,所謂的Linking View是指以檔案呈現之ELF(左圖),而Execution View則是指被載入到RAM上執行的ELF(右圖)。ELF header會包含整個檔案的"road map",我們可以利用readelf -h檢視elf header到底包含哪些東西?
由readelf -h可看出可看出ELF header包含了許多資訊,這些資訊都可以由定義在elf.h中的Elf32_Ehdr解讀出來。介紹ELF header之前,先介紹ELF的Data Types,這些資訊也都放置在elf.h檔中。
/* Standard ELF types. */ #include <stdint.h> /* Type for a 16-bit quantity. */ typedef uint16_t Elf32_Half; typedef uint16_t Elf64_Half; /* Types for signed and unsigned 32-bit quantities. */ typedef uint32_t Elf32_Word; typedef int32_t Elf32_Sword; typedef uint32_t Elf64_Word; typedef int32_t Elf64_Sword; /* Types for signed and unsigned 64-bit quantities. */ typedef uint64_t Elf32_Xword; typedef int64_t Elf32_Sxword; typedef uint64_t Elf64_Xword; typedef int64_t Elf64_Sxword; /* Type of addresses. */ typedef uint32_t Elf32_Addr; typedef uint64_t Elf64_Addr; /* Type of file offsets. */ typedef uint32_t Elf32_Off; typedef uint64_t Elf64_Off; /* Type for section indices, which are 16-bit quantities. */ typedef uint16_t Elf32_Section; typedef uint16_t Elf64_Section; /* Type for version symbol information. */ typedef Elf32_Half Elf32_Versym; typedef Elf64_Half Elf64_Versym;Elf32和Elf64的data type只有在off和addr這兩種type的資料長度有不同,其餘都相同。您可以在下表中發現Elf32_Ehdr和Elf64_Ehdr的member資料長度只有差e_entry、e_phoff和e_shoff會不相同,其餘都相同,所以,Elf64_Ehdr比Elf32_Ehdr多了12byte。
ELF header如下
/* 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; /* Fields in the e_ident array. The EI_* macros are indices into the * array. The macros under each EI_* macro are the values the byte * may have. */ #define EI_MAG0 0 /* File identification byte 0 index */ #define ELFMAG0 0x7f /* Magic number byte 0 */ #define EI_MAG1 1 /* File identification byte 1 index */ #define ELFMAG1 'E' /* Magic number byte 1 */ #define EI_MAG2 2 /* File identification byte 2 index */ #define ELFMAG2 'L' /* Magic number byte 2 */ #define EI_MAG3 3 /* File identification byte 3 index */ #define ELFMAG3 'F' /* Magic number byte 3 */ /* Conglomeration of the identification bytes, for easy testing as a word. */ #define ELFMAG "\177ELF" #define SELFMAG 4 #define EI_CLASS 4 /* File class byte index */ #define ELFCLASSNONE 0 /* Invalid class */ #define ELFCLASS32 1 /* 32-bit objects */ #define ELFCLASS64 2 /* 64-bit objects */ #define ELFCLASSNUM 3 #define EI_DATA 5 /* Data encoding byte index */ #define ELFDATANONE 0 /* Invalid data encoding */ #define ELFDATA2LSB 1 /* 2's complement, little endian */ #define ELFDATA2MSB 2 /* 2's complement, big endian */ #define ELFDATANUM 3 #define EI_VERSION 6 /* File version byte index */ /* Value must be EV_CURRENT */ #define EI_OSABI 7 /* OS ABI identification */ #define ELFOSABI_NONE 0 /* UNIX System V ABI */ #define ELFOSABI_SYSV 0 /* Alias. */ #define ELFOSABI_HPUX 1 /* HP-UX */ #define ELFOSABI_NETBSD 2 /* NetBSD. */ #define ELFOSABI_LINUX 3 /* Linux. */ #define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ #define ELFOSABI_AIX 7 /* IBM AIX. */ #define ELFOSABI_IRIX 8 /* SGI Irix. */ #define ELFOSABI_FREEBSD 9 /* FreeBSD. */ #define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ #define ELFOSABI_MODESTO 11 /* Novell Modesto. */ #define ELFOSABI_OPENBSD 12 /* OpenBSD. */ #define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ #define EI_ABIVERSION 8 /* ABI version */ #define EI_PAD 9 /* Byte index of padding bytes */
資料結構大致介紹完畢,接著就可以透過實做readelf -h的程式來了解ELF header了,首先,所有的ELF開頭都會有16 bytes的ELF Identification,也是本章節所要介紹的部份。
EI_MAG0 ~ EI_MAG3
所有的ELF前面4byte為magic number,其內容為{0x7f, 'E', 'L', 'F'},用以判斷是否為ELF檔。static int elf_header(int fd) { int sz; unsigned char e_ident[EI_NIDENT]; sz = read(fd, e_ident, sizeof(e_ident)); if (sz < sizeof(e_ident)) { fprintf(stderr, "invalid elf file\n"); return -1; } // 判斷是否為ELF檔 if (memcmp(ELFMAG, e_ident, SELFMAG)) { fprintf(stderr, "invalid elf file\n"); return -1; } elf_header_magic(e_ident); } /** * 印出ident(前面16byte) */ static int elf_header_magic(unsigned char *c) { int i; printf("%-10s", "Magic: "); for (i = 0; i < EI_NIDENT; i++) { printf("%02X ", c[i]); } printf("\n"); return 0; }
EI_CLASS
EI_CLASS這個byte是用來判斷是32-bit或是64-bit的ELF檔,根據不同的Class就要選擇使用Elf32_Ehdr或是Elf64_Ehdr判讀後面的資料。/** * 判斷是32-bit/64-bit architectures. */ static int elf_header_class(unsigned char c) { printf("%-36s", "Class: "); switch(c) { case ELFCLASSNONE: fprintf(stderr, "Invalid class\n"); return -1; case ELFCLASS32: printf("32-bit object\n"); break; case ELFCLASS64: printf("64-bit object\n"); break; default: fprintf(stderr, "unknow class\n"); return -1; } return 0; }
EI_DATA
EI_DATA這個byte是用來判斷ELF檔是LSB(Little-endian)還是MSB(Big-endian)。/** * 判斷ELF檔是LSB(Little-endian)還是MSB(Big-endian) */ static int elf_header_data(unsigned char c) { printf("%-36s", "Data: "); switch(c) { case ELFDATANONE: fprintf(stderr, "Invalid data encoding\n"); return -1; case ELFDATA2LSB: printf("2's complement, little endian\n"); break; case ELFDATA2MSB: printf("2's complement, big endian\n"); break; default: fprintf(stderr, "unknow data\n"); return -1; } return 0; }
EI_VERSION
EI_VERSION這個byte是指出這個ELF檔的ELF header的版本是多少?目前這個值必須是EV_CURRENT。static int elf_header_version(unsigned char c) { printf("%-36s", "Version: "); switch(c) { case EV_CURRENT: printf("Current version"); break; default: case EV_NONE: printf("Invalid ELF version"); break; } printf("(%d)\n", c); return 0; }
EI_OSABI
EI_OSABI這個byte是指出這個ELF檔會在那個OS上運行。static int elf_header_osabi(unsigned char c) { printf("%-36s", "OS/ABI: "); switch(c) { case ELFOSABI_SYSV: printf("UNIX System V ABI"); break; case ELFOSABI_HPUX: printf("HP-UX"); break; case ELFOSABI_NETBSD: printf("NetBSD."); break; case ELFOSABI_LINUX: printf("Linux."); break; case ELFOSABI_SOLARIS: printf("Sun Solaris."); break; case ELFOSABI_AIX: printf("IBM AIX."); break; case ELFOSABI_IRIX: printf("SGI Irix."); break; case ELFOSABI_FREEBSD: printf("FreeBSD."); break; case ELFOSABI_TRU64: printf("Compaq TRU64 UNIX."); break; case ELFOSABI_MODESTO: printf("Novell Modesto."); break; case ELFOSABI_OPENBSD: printf("OpenBSD."); break; case ELFOSABI_ARM_AEABI: printf("ARM EABI"); break; case ELFOSABI_ARM: printf("ARM"); break; case ELFOSABI_STANDALONE: printf("Standalone (embedded) application"); break; default: fprintf(stderr, "unknow osabi\n"); return -1; } printf("(%d)\n", c); return 0; }
EI_ABIVERSION
EI_ABIVERSION這個byte是指出這個ELF檔會在那個API版本上運行。一個OS上可能有多個ABI的版本在運行的版本在運行,如SYSV至少就有SVR、Solaris、SCO等ABI。0代表不指定(unspecified)。static int elf_header_abi_version(unsigned char c) { printf("%-36s%d\n", "ABI Version: ", c); return 0; }
EI_PAD
EI_PAD這個byte之後的都是padding。到目前為止,僅有解釋ELF header中的e_ident,剩下的部份會在後面繼續探討與研究。
非常好,
回覆刪除有个问题,
如果我有一个BOOT program, 烧录到Flash中, 是如何进行地址转换的? 比如, CPU Reset的入口是0x00000000, ELF有一个HEADER, 把那部分的代码烧到了0000的位置?
請參考 http://www.emdebian.org/~zumbi/panda/u-boot/doc/README.arm-relocation
刪除a) cpu starts
b) it copies the first page in nand to internal ram
(nand_spl_code)
c) end executes this code
d) this initialize CPU, RAM, ... and copy itself to RAM
(this bin must fit in one page, so board_init_f()
don;t fit in it ... )
e) there it copy u-boot to CONFIG_SYS_NAND_U_BOOT_DST and
starts this image @ CONFIG_SYS_NAND_U_BOOT_START
f) u-boot code steps through board_init_f() and calculates
the relocation address and copy itself to it
If CONFIG_SYS_TEXT_BASE == relocation address, the copying of u-boot
in f) could be saved.