Chinaunix首页 | 论坛 | 博客
  • 博客访问: 341571
  • 博文数量: 89
  • 博客积分: 5152
  • 博客等级: 大校
  • 技术积分: 1155
  • 用 户 组: 普通用户
  • 注册时间: 2006-02-25 15:12
文章分类

全部博文(89)

文章存档

2012年(1)

2011年(5)

2010年(14)

2009年(69)

我的朋友

分类:

2009-04-10 10:21:50

ELF(Executable and Linking Format)格式教學文件, #5: 讀 ELF Section(說明)

發表於 November 28, 2006 5:14 PM

今 天的內容「讀取 ELF section」是這系列 ELF 文章的重點戲。因為我們終於進入 ELF 的核心議題「節區的觀念」了。在 loader v0.5 以前的範例,都是屬於靜態的討論(linking view);在 loader v0.6 開始的討論中,我們將會開始提到動態的執行行為(execution view)。

如何讀取 ELF Section

我們分 2 個步驟來讀取 ELF 的 section 資訊:

1. 如圖一,一開始先讀取 section header table 的資訊。section header table 是所有 section 的紀錄表格,所有的 section 都要透過 section header table 才能得知其在檔案中的偏移位置(offset),如此一來才能讀取 section 的內容。

2. 如圖二,接著再根據 section header table 讀取檔案裡的每一個 section。

section header table 裡的 section 個數(section entries)紀錄於 ELF header 裡的 e_shnum 欄位,每個 section entry 的長度則是紀錄在 ELF header 的 e_shentsize 欄位,單位是bytes。

elf_sht.jpg
圖一:Section Header Table

ELF header 裡的 e_shoff 欄位,是 section header table 開始的檔案偏移位置 (file offset)。因此,我們只要由 e_shoff 偏移值開始讀取 e_shnum 個單位,每個單位為 e_shentsize(bytes),即可將整個 section header table 讀取出來。

elf_section_entry.jpg
圖二:Section Entries

SysV ABI 定義 section entry 的資料結構如下:

typedef struct {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;

Section header table 是 Elf32_Shdr data type 的陣列,其元素個數為 e_shnum,我們可以透過索引 Elf32_Shdr 陣列來讀取所有的 section,如圖二所示。

Section header(Elf32_Shdr)的欄位用途說明如下表。

Field Description
sh_name  section的名稱,需由string table查表取得。
sh_type  section的類型。
sh_flags  section的屬性。
sh_addr  section在記憶體裡的起始位址,但並非所有的section都會被載入至記憶體。
sh_offset  section在objct file裡的開始偏移值(offset),程式必須根據此欄位來讀取section的本文。
sh_size  section的長度(bytes)。
sh_link  可用來存放section header table的index link。
sh_info  可用來存放section的額外資訊。
sh_addralign  紀錄section的address alignment,例如有些section的 alignment為DWORD(dobuleword)。
sh_entsize  有些section的內容為entry長度固定的table,例如symbol table。此欄用來紀錄entry的長度,單位是bytes。

範例程式:loader v0.3

接著說明 loader v0.2 需要改寫的功能。

在主程式新增一個 parse_sections() 函數來讀取 ELF object file 的 section header table:

parse_sections(&f_header, fd);

parse_sections() 負責做2件工作:

1. 讀取 Section Header Table

2. 找出 Section Name String Table

同時,在主程式也要加入以下 2 個變數:

Elf32_Shdr header[40];
Elf32_Shdr *strtab; /* point to string table */

header[] 用來存放由 section header table 所讀取出來的所有 section entry,其型別為 Elf32_Shdrstrtab 指標用來指向 section name string table,其型別為 Elf32_Shdr。Section name string table 是一個特殊的 section,下一個範例再來處理這個 section。

將檔案讀寫指標移到 e_shoff 的地方,準備開始讀取 section header table:

lseek(fd, hdr->e_shoff, SEEK_SET);

然後再利用最簡單的方式,一次一個將所有的section entry讀取出來,並且判斷該section entry是否為string table:

   for (i = 0; i < hdr->e_shnum; i++) {
read(fd, &header[i], sizeof(Elf32_Shdr));

/* find out string table ! */
if (header[i].sh_type == SHT_STRTAB) strtab = &header[i];
}

最後,section 用途是根據他的類型來區分的,這個部份留待下次再做說明。

範例列表:loader v0.3

/*
* Copyright(c) 2003,2006
*
* ELF programming. ver 0.3
*
*/
#include
#include
#include
#include
#include
#include

int elf_ident(char *ident)
{
if (*(ident+EI_MAG0) != ELFMAG0) return 0;
if (*(ident+EI_MAG1) != ELFMAG1) return 0;
if (*(ident+EI_MAG2) != ELFMAG2) return 0;
if (*(ident+EI_MAG3) != ELFMAG3) return 0;

return -1;
}

void parse_ident(char *ident)
{
printf("ELF Identification\n");

printf(" Class: ");
switch (*(ident+EI_CLASS)) {
case ELFCLASSNONE: printf("Invalid class\n"); break;
case ELFCLASS32: printf("32-bit objects\n"); break;
case ELFCLASS64: printf("64-bit objects\n"); break;
}
}

void parse_machine(Elf32_Half machine)
{
printf("Machine: ");
switch (machine) {
case EM_NONE: printf("No machine\n"); break;
case EM_M32: printf("AT&T WE 32100\n"); break;
case EM_SPARC: printf("SPARC\n"); break;
case EM_386: printf("Intel 80386\n"); break;
case EM_68K: printf("Motorola 68000\n"); break;
case EM_88K: printf("Motorola 88000\n"); break;
case EM_860: printf("Intel 80860\n"); break;
case EM_MIPS: printf("MIPS RS3000 Big-Endian\n"); break;

default: printf("Unknow\n");
}
}

void parse_sections(Elf32_Ehdr *hdr, int fd)
{
int i;
Elf32_Shdr header[40];
Elf32_Shdr *strtab; /* point to string table */

printf("Num of secionts: %d\n", hdr->e_shnum);
/* file offset of section header table */
lseek(fd, hdr->e_shoff, SEEK_SET);

for (i = 0; i < hdr->e_shnum; i++) {
read(fd, &header[i], sizeof(Elf32_Shdr));

/* find out string table ! */
if (header[i].sh_type == SHT_STRTAB) strtab = &header[i];
}
}

int main(int argc, char *argv[])
{
int fd;
Elf32_Ehdr f_header;

if (argc != 2) {
printf("Usage: loader [filename]\n");
return -1;
}

fd = open(argv[1], S_IRUSR);
if (fd < 0) {
printf("\nfile open error\n");
return -1;
}

/* Read ELF Header */
read(fd, &f_header, sizeof(Elf32_Ehdr));

/* Parse header information */
if (elf_ident(f_header.e_ident)) {
parse_ident(f_header.e_ident);
parse_machine(f_header.e_machine);
parse_sections(&f_header, fd);
} else {
printf("not a ELF binary file\n");
}

close(fd);
} 

執行結果

$ ./loader-0.3 ./loader-0.3
ELF Identification
Class: 32-bit objects
Machine: Intel 80386
Num of secionts: 34



阅读(422) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~