ELF(Executable and Linking Format)格式教學文件, #4: 第一個範例:loader v0.2(ELF Identification)

jollen 發表於 November 26, 2006 12:27 PM

ELF Identification

ELF header 資料結構裡的 e_ident 欄位用來判斷檔案是否為ELF格式的欄位,e_ident 欄位是一個陣列資料結構,其長度由 SysV ABI所定義的常數 EI_NIDENT指定。

EI_NIDENT 的值為 16 代表 e_ident 欄位有 16 個元素,SysV ABI 定義了 8 個常數來索引 e_ident 陣列的元素。

e_ident[] 索引值定義

Name  Value  說明
EI_MAG0  0  ELF 識別字元
EI_MAG1  1  ELF 識別字元
EI_MAG2  2  ELF 識別字元
EI_MAG3  3  ELF 識別字元
EI_CLASS  4  檔案類別 (class)
EI_DATA  5  資料編碼方式 (Data encoding)
EI_VERSION  6  檔案版本
EI_PAD  7  padding bytes 的開頭

loader-0.2.c主要的改變在於加入了判斷檔案是否為 ELF 格式的程式碼。要判斷檔案是否為 ELF 格式,必須根據 e_ident[] 裡的識別字元來做判斷:

  • e_ident[EI_MAG0] 必須等於 ELFMAG0

  • e_ident[EI_MAG1] 必須等於 ELFMAG1

  • e_ident[EI_MAG2] 必須等於 ELFMAG2

  • e_ident[EI_MAG3] 必須等於 ELFMAG3

表 ELF識別字元(magic number)定義

Name

 Value

 說明

ELFMAG0

 0x7f

 e_ident[EI_MAG0] 之值

ELFMAG1

 ‘E’

 e_ident[EI_MAG1] 之值

ELFMAG2

 ‘L’

 e_ident[EI_MAG2] 之值

ELFMAG3

 ‘F’

 e_ident[EI_MAG3] 之值

判斷是否為 ELF 格式

設計一個 elf_ident() 函數來判斷檔案是否為 ELF 格式,其程式碼如下:

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;
}

e_ident[] 裡還存放許多 ELF 的資訊。以下再舉一例,我們新增一個函數 parse_ident() 來分析 e_ident[] 的 "CLASS" 資訊:

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;
   }
}

程式列表:loader-0.2.c

/*
 * Copyright(c) 2003,2006 www.jollen.org
 *
 * ELF programming. ver 0.2
 *
 */
#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");
   }
}

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);
   } else {
      printf("not a ELF binary file\n");
   }
}
Also See

Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue

您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw