ELFの実行可能ヘッダーについて

実行可能ヘッダーを詳しく見ていこうと思います。

実行可能ヘッダーの定義

ターミナルでman elfを入力することで確認することができます。

#define EI_NIDENT 16

typedef struct {
    unsigned char e_ident[EI_NIDENT];
    uint16_t      e_type;
    uint16_t      e_machine;
    uint32_t      e_version;
    ElfN_Addr     e_entry;
    ElfN_Off      e_phoff;
    ElfN_Off      e_shoff;
    uint32_t      e_flags;
    uint16_t      e_ehsize;
    uint16_t      e_phentsize;
    uint16_t      e_phnum;
    uint16_t      e_shentsize;
    uint16_t      e_shnum;
    uint16_t      e_shstrndx;
} ElfN_Ehdr;

e_ident配列

 e_identは16バイトの配列です。この配列の先頭にはELFファイルであることを示す、0x7f454c46という値が入っています。
 続いて、このELFファイルが何ビットのアーキテクチャで構成されているかを表す値が入ります。

  • 32ビット => 1
  • 64ビット => 2

 その後にはバイトオーダーを示すビット列が並びます。

 次のバイトには、ELFのバージョンが入ります。現在のバージョンは1しかありません。
 最後は、ABI(Application Binary Interface)とOSの情報を示す値が格納されます。ここは難しくてよく分かりませんでしたが、デフォルトの値は0入るそうです。

e_typeフィールド

 このフィールドには、バイナリの種類が入ります。

  • ET_NONE(ファイルタイプが存在しない) => 0
  • ET_REL(再配置可能ファイル) => 1
  • ET_EXEC(実行可能ファイル) => 2
  • ET_DYN(共有オブジェクトファイル) => 3
  • ET_CORE(コアファイル) => 4

e_machineフィールド

 このフィールドには、バイナリを実行するアーキテクチャが入ります。下記に有名なものを抜粋しました。

  • ARM 32-bit architecture => 40(0x28)
  • AMD x86-64 architecture => 62(0x3E)
  • ARM 64-bit architecture => 183(0xB7)

e_versionフィールド

 ここには、e_ident配列にも記述されていたELFファイルのバージョンが入ります。
 現状は1しかありません。

e_entryフィールド

 このフィールドには、バイナリのエントリポイントと呼ばれる、実行を開始すべき場所の仮想アドレスが入ります。
 環境によって入る値が変わります。
 例:0x400100

e_phoffフィールド

 これは、プログラムヘッダーテーブルのファイルオフセットを示します。64ビットアーキテクチャの場合、64(0x40)となります。

 ※ ファイルオフセット‥ヘッダーにたどり着くまでにファイルから読み取らなければならないバイト数のこと

e_shoffフィールド

 ここは、セクションヘッダーテーブルのファイルオフセットを示します。環境によって大きく値が違ってきます。
 セクションヘッダーテーブルが存在しない場合、0が入ります。

e_flagsフィールド

 このフィールドには、プロセッサ固有のフラグが入ります。
 x86バイナリの場合は、基本的に0が入るそうです。

e_ehsizeフィールド

 実行可能ヘッダーの大きさを表します。

  • 32ビットx86バイナリ => 52バイト(0x34)
  • 64ビットx86バイナリ => 64バイト(0x40)

e_phentsizeフィールド

 ELFファイルのプログラムヘッダーテーブルのエントリサイズをバイト単位で示します。
 例:56バイト(0x38)

e_phnumフィールド

 このフィールドは、プログラムヘッダテーブルのエントリ数を表します。
 例:9

 e_phentsizeフィールドとe_phnumフィールドの値を掛けることによって、プログラムヘッダーテーブル内の合計サイズを求めることができます。

e_shentsizeフィールド

 このフィールドには、セクションヘッダーテーブルのエントリサイズをバイト単位で示します。
 例:64バイト(0x40)

e_shnumフィールド

 ここには、セクションヘッダーテーブルのエントリ数が入ります。
 例:31(0x1F)

 e_shentsizeフィールドとe_shnumフィールドの値を掛けることによって、セクションヘッダーテーブルの内の合計サイズを求めることができます。

e_shstrndxフィールド

 このフィールドには、セクションヘッダー文字列テーブルのインデックスが記述されています。
 例:28(0x1C)

まとめ

 最初の方はイメージしやすかったのですが、後半のフィールド値の役割や意味をあまり理解できませんでした。 しかし、プログラムヘッダーやセクションヘッダーについて少し触れることができました。
 次は、セクションヘッダーについて詳しく勉強していこうと思います。