ELF Format-1-File Header


這個分析過程可以在windows中用010editor的elf模板打開一個elf文件對照分析。

elf文件大致結構:

|   file header       | readelf -h
|---------------------|
|program header table | readelf -l
|---------------------|
|       data          |
|---------------------|
|   string "table"    |
|---------------------|
|section header table | readelf -S
|---------------------|

0. 獲取readelf源碼

> which readelf
/usr/bin/readelf
> dpkg -S /usr/bin/readelf
binutils: /usr/bin/readelf
> apt-get source binutils
> cd binutils-2.24/binutils

可以用CLion導入binutils-2.24,但是慢。推薦ctags -R.

結構體定義位於include/elf/ineternal.h,主要函數位於binutils/readelf.c,其中有一個static int process_file (char * file_name),裏面又調用了ret = process_object (file_name, file);,解析整個文件。

static int
process_object (char * file_name, FILE * file)
{
  unsigned int i;

  if (! get_file_header (file))
    {
      error (_("%s: Failed to read file header\n"), file_name);
      return 1;
    }

  /* Initialise per file variables.  */
  for (i = ARRAY_SIZE (version_info); i--;)
    version_info[i] = 0;

  for (i = ARRAY_SIZE (dynamic_info); i--;)
    dynamic_info[i] = 0;
  dynamic_info_DT_GNU_HASH = 0;

  /* Process the file.  */
  if (show_name)
    printf (_("\nFile: %s\n"), file_name);

  /* Initialise the dump_sects array from the cmdline_dump_sects array.
     Note we do this even if cmdline_dump_sects is empty because we
     must make sure that the dump_sets array is zeroed out before each
     object file is processed.  */
  if (num_dump_sects > num_cmdline_dump_sects)
    memset (dump_sects, 0, num_dump_sects * sizeof (* dump_sects));

  if (num_cmdline_dump_sects > 0)
    {
      if (num_dump_sects == 0)
	/* A sneaky way of allocating the dump_sects array.  */
	request_dump_bynumber (num_cmdline_dump_sects, 0);

      assert (num_dump_sects >= num_cmdline_dump_sects);
      memcpy (dump_sects, cmdline_dump_sects,
	      num_cmdline_dump_sects * sizeof (* dump_sects));
    }

  if (! process_file_header ())
    return 1;

  if (! process_section_headers (file))
    {
      /* Without loaded section headers we cannot process lots of
	 things.  */
      do_unwind = do_version = do_dump = do_arch = 0;

      if (! do_using_dynamic)
	do_syms = do_dyn_syms = do_reloc = 0;
    }

  if (! process_section_groups (file))
    {
      /* Without loaded section groups we cannot process unwind.  */
      do_unwind = 0;
    }

  if (process_program_headers (file))
    process_dynamic_section (file);

  process_relocs (file);
  process_unwind (file);
  process_symbol_table (file);
  process_syminfo (file);
  process_version_sections (file);
  process_section_contents (file);
  process_notes (file);
  process_gnu_liblist (file);
  process_arch_specific (file);

  //free memory
  /*
  if (program_headers)
    {
      free (program_headers);
      program_headers = NULL;
    }

  */
  free_debug_memory ();

  return 0;
}

1. File Header

readelf.c的源碼使用了BFD開發庫,不方便閱讀,所有轉而查閱/usr/include/elf.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;

下面是32位文件頭定義,64位中把類型名的32改爲64即可。

/* 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;

1.1 基本信息

e_ident, 16字節magic number,

  • [0-3], “elf” ascii碼;
  • [4], CLASS(類別), 0/1/2, 無效/32bit/64bit!!!!!!
  • [5], 字節序, 0/1/2,無效/小段/大端
  • [6], elf版本,默認1
  • [7], 系統版本, 默認ELFOSABI_NONE(0)
  • [8], ABI版本, 默認0
  • 剩餘7字節都是0
/* 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_GNU		3	/* Object uses GNU ELF extensions.  */
#define ELFOSABI_LINUX		ELFOSABI_GNU /* Compatibility alias.  */
#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 */

  • e_type
/* Legal values for e_type (object file type).  */

#define ET_NONE		0		/* No file type */
#define ET_REL		1		/* Relocatable file */
#define ET_EXEC		2		/* Executable file */
#define ET_DYN		3		/* Shared object file */
#define ET_CORE		4		/* Core file */
#define	ET_NUM		5		/* Number of defined types */
#define ET_LOOS		0xfe00		/* OS-specific range start */
#define ET_HIOS		0xfeff		/* OS-specific range end */
#define ET_LOPROC	0xff00		/* Processor-specific range start */
#define ET_HIPROC	0xffff		/* Processor-specific range end */

彙編器輸出的是可重定位文件1,靜態鏈接器輸出的是可執行文件2 和 共享目標文件3.


  • e_machine:如EM_386(3, Intel 80386)

  • e_version:elf文件版本,默認1;

  • e_entry:程序入口點線性地址,一般用於可執行文件;重定位文件中則爲0;

  • e_flags:平臺屬性,默認0

  • e_ehsize:文件頭大小

  • e_shstrndx.shstrtab在段表中的下標。

1.2 程序頭表

  • e_phoff:程序頭表在elf文件中的偏移
  • e_phentsize:程序頭表大小
  • e_phnum:程序頭表數量

1.3 段表

  • e_shoff:段表在elf文件中的偏移
  • e_shentsize:段表大小
  • e_shnum:段表數量

程序頭表和段表是兩個很重要的表。

1.4 readelf中的代碼

/* Decode the data held in 'elf_header'.  */

static int
process_file_header (void)
{
  if (   elf_header.e_ident[EI_MAG0] != ELFMAG0
      || elf_header.e_ident[EI_MAG1] != ELFMAG1
      || elf_header.e_ident[EI_MAG2] != ELFMAG2
      || elf_header.e_ident[EI_MAG3] != ELFMAG3)
    {
      error
	(_("Not an ELF file - it has the wrong magic bytes at the start\n"));
      return 0;
    }

  init_dwarf_regnames (elf_header.e_machine);

  if (do_header)
    {
      int i;

      printf (_("ELF Header:\n"));
      printf (_("  Magic:   "));
      for (i = 0; i < EI_NIDENT; i++)
	printf ("%2.2x ", elf_header.e_ident[i]);
      printf ("\n");
      printf (_("  Class:                             %s\n"),
	      get_elf_class (elf_header.e_ident[EI_CLASS]));
      printf (_("  Data:                              %s\n"),
	      get_data_encoding (elf_header.e_ident[EI_DATA]));
      printf (_("  Version:                           %d %s\n"),
	      elf_header.e_ident[EI_VERSION],
	      (elf_header.e_ident[EI_VERSION] == EV_CURRENT
	       ? "(current)"
	       : (elf_header.e_ident[EI_VERSION] != EV_NONE
		  ? _("<unknown: %lx>")
		  : "")));
      printf (_("  OS/ABI:                            %s\n"),
	      get_osabi_name (elf_header.e_ident[EI_OSABI]));
      printf (_("  ABI Version:                       %d\n"),
	      elf_header.e_ident[EI_ABIVERSION]);
      printf (_("  Type:                              %s\n"),
	      get_file_type (elf_header.e_type));
      printf (_("  Machine:                           %s\n"),
	      get_machine_name (elf_header.e_machine));
      printf (_("  Version:                           0x%lx\n"),
	      (unsigned long) elf_header.e_version);

      printf (_("  Entry point address:               "));
      print_vma ((bfd_vma) elf_header.e_entry, PREFIX_HEX);
      printf (_("\n  Start of program headers:          "));
      print_vma ((bfd_vma) elf_header.e_phoff, DEC);
      printf (_(" (bytes into file)\n  Start of section headers:          "));
      print_vma ((bfd_vma) elf_header.e_shoff, DEC);
      printf (_(" (bytes into file)\n"));

      printf (_("  Flags:                             0x%lx%s\n"),
	      (unsigned long) elf_header.e_flags,
	      get_machine_flags (elf_header.e_flags, elf_header.e_machine));
      printf (_("  Size of this header:               %ld (bytes)\n"),
	      (long) elf_header.e_ehsize);
      printf (_("  Size of program headers:           %ld (bytes)\n"),
	      (long) elf_header.e_phentsize);
      printf (_("  Number of program headers:         %ld"),
	      (long) elf_header.e_phnum);
      if (section_headers != NULL
	  && elf_header.e_phnum == PN_XNUM
	  && section_headers[0].sh_info != 0)
	printf (" (%ld)", (long) section_headers[0].sh_info);
      putc ('\n', stdout);
      printf (_("  Size of section headers:           %ld (bytes)\n"),
	      (long) elf_header.e_shentsize);
      printf (_("  Number of section headers:         %ld"),
	      (long) elf_header.e_shnum);
      if (section_headers != NULL && elf_header.e_shnum == SHN_UNDEF)
	printf (" (%ld)", (long) section_headers[0].sh_size);
      putc ('\n', stdout);
      printf (_("  Section header string table index: %ld"),
	      (long) elf_header.e_shstrndx);
      if (section_headers != NULL
	  && elf_header.e_shstrndx == (SHN_XINDEX & 0xffff))
	printf (" (%u)", section_headers[0].sh_link);
      else if (elf_header.e_shstrndx != SHN_UNDEF
	       && elf_header.e_shstrndx >= elf_header.e_shnum)
	printf (_(" <corrupt: out of range>"));
      putc ('\n', stdout);
    }

  if (section_headers != NULL)
    {
      if (elf_header.e_phnum == PN_XNUM
	  && section_headers[0].sh_info != 0)
	elf_header.e_phnum = section_headers[0].sh_info;
      if (elf_header.e_shnum == SHN_UNDEF)
	elf_header.e_shnum = section_headers[0].sh_size;
      if (elf_header.e_shstrndx == (SHN_XINDEX & 0xffff))
	elf_header.e_shstrndx = section_headers[0].sh_link;
      else if (elf_header.e_shstrndx >= elf_header.e_shnum)
	elf_header.e_shstrndx = SHN_UNDEF;
      free (section_headers);
      section_headers = NULL;
    }

  return 1;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章