ELF Formatについて

a.out形式はUNIXで長い間使われてきたが、Unix System Vの登場時に、クロスコンパイルや動的リンクなどに、対応するためにより良い形式が必要だとして、ELF(Executable and Linking Format)が作られました。

ELFは、再配置可可能オブジェクト・実行可能・共有オブジェクト・コアという4種類のファイルがあます。

再配置可可能オブジェクト
再配置可可能ファイルは、アセンブラやコンパイラが作成する、オブジェクトファイル
実行可能
実行可能は、再配置が全て完了し、シンボルも解決してある(実行時に解決される、共有ライブラリのシンボルは除く) "lsコマンド"のような実行可能なファイル
共有オブジェクト
リンカ用のシンボル情報と実行時に使われるコードを格納する、共有ライブラリ等のファイル
コア

ELF ファイルフォーマットを使っている実行可能ファイルはELFヘッダを持ちます。そして、プログラムヘッダテーブルか、セクションヘッダテーブル、あるいはその両方が続きます。

システムローダはプログラムヘッダテーブに記述されたセグメント集合として扱い、コンパイラ・アセンブラ・リンカはセクションヘッダテーブルがに記述された論理セクション集合としてELFを扱います。

セグメントとセクション
セグメントとセクション

ELFヘッダ

ELF ヘッダは、常にファイルのオフセット 0 にあります。プログラムヘッダテーブルとセクションヘッダテーブルのファイル中のオフセットは、 ELF ヘッダで定義されています。OpenBSDではsys/exec_elf.hに定義されていて、32bit アーキテクチャと64bit アーキテクチャ用にそれぞれ別に定義されています。

32bit アーキテクチャ

typedef struct elfhdr{
        unsigned char   e_ident[EI_NIDENT];   /* ELF Identification */
        Elf32_Half      e_type;               /* object file type */
        Elf32_Half      e_machine;            /* machine */
        Elf32_Word      e_version;            /* object file version */
        Elf32_Addr      e_entry;              /* virtual entry point */
        Elf32_Off       e_phoff;              /* program header table offset */
        Elf32_Off       e_shoff;              /* section header table offset */
        Elf32_Word      e_flags;              /* processor-specific flags */
        Elf32_Half      e_ehsize;             /* ELF header size */
        Elf32_Half      e_phentsize;          /* program header entry size */
        Elf32_Half      e_phnum;              /* number of program header entries */
        Elf32_Half      e_shentsize;          /* section header entry size */
        Elf32_Half      e_shnum;              /* number of section header entries */
        Elf32_Half      e_shstrndx;           /* section header table's "section 
	                                         header string table" entry offset */
} Elf32_Ehdr;

Elf32_Addr      符号無しプログラムアドレス
Elf32_Half      符号無しハーフワードフィールド
Elf32_Off       符号無しファイルオフセット
Elf32_Sword     符号付き大整数
Elf32_Word      フィールドまたは符号無し大整数
Elf32_Size      符号無しオブジェクトサイズ      
64bit アーキテクチャ

typedef struct {
        unsigned char   e_ident[EI_NIDENT];   /* Id bytes */
        Elf64_Quarter   e_type;               /* file type */
        Elf64_Quarter   e_machine;            /* machine type */
        Elf64_Half      e_version;            /* version number */
        Elf64_Addr      e_entry;              /* entry point */
        Elf64_Off       e_phoff;              /* Program hdr offset */
        Elf64_Off       e_shoff;              /* Section hdr offset */
        Elf64_Half      e_flags;              /* Processor flags */
        Elf64_Quarter   e_ehsize;             /* sizeof ehdr */
        Elf64_Quarter   e_phentsize;          /* Program header entry size */
        Elf64_Quarter   e_phnum;              /* Number of program headers */
        Elf64_Quarter   e_shentsize;          /* Section header entry size */
        Elf64_Quarter   e_shnum;              /* Number of section headers */
        Elf64_Quarter   e_shstrndx;           /* String table index */
} Elf64_Ehdr;

Elf64_Addr      符号無しプログラムアドレス
Elf64_Shalf     符号付きハーフワードフィールド
Elf64_Half      符号無しハーフワードフィールド
Elf64_Off       符号無しファイルオフセット
Elf64_Sword     符号付き大整数
Elf64_Word      フィールドまたは符号無し大整数
Elf64_Xword     符号無しオブジェクトサイズまたはalignment
Elf64_Sxword    符号付きオブジェクトサイズまたはalignment
Elf64_Quarter   符号無しクォータワードフィールド      

そして、これらのヘッダは次の値を持ちます。

ELFヘッダ内容
変数詳細
e_ident[EI_NIDENT]indexdefine内容
0EI_MAG0マジックNO.0byte目 '\177'
1EI_MAG1マジックNO.1byte目 'E'
2EI_MAG2マジックNO.2byte目 'L'
3EI_MAG3マジックNO.3byte目 'F'
4EI_CLASSバイナリファイルのアーキテクチャ
定義意味
ELFCLASSNONE不正なクラス
ELFCLASS3232bit アーキテクチャを定義
ファイル空間と仮想アドレス空間が 4 ギガバイトまでのマシンに対応します。
ELFCLASS6464bit アーキテクチャを定義
5EI_DATAプロセッサ固有のバイトオーダー指定
定義意味
ELFDATANONE未知のデータフォーマット
ELFDATA2LSBリトルエンディアン
ELFDATA2MSBビッグエンディアン
6EI_VERSIONELF 仕様書のバージョンナンバ
定義意味
EV_NONE不当なバージョン
EV_CURRENT現在のバージョン
7EI_OSABIオペレーティングシステムとABIを識別
現在OpenBSDでは、定義されているが使われていないようだ
8EI_ABIVERSIONABIのバージョンを識別
現在OpenBSDでは、定義されているが使われていないようだ
9EI_PADパディングの始め。これらのバイトは、予約されており、0 にセットされます。
10-15---未定義(0 パディング)
e_typeオブジェクトファイルタイプ
定義意味
ET_NONE未知のタイプ
ET_REL再配置可能なファイル
ET_EXEC実行可能ファイル
ET_DYN共有オブジェクト
ET_COREコアファイル
e_machineアーキテクチャ
定義意味
EM_NONE未知のマシン
EM_M32AT&T WE 32100
EM_SPARCSPARC
EM_386Intel 80386
EM_68KMotorola 68000
EM_88KMotorola 88000
EM_486Intel 80486
EM_860Intel 80860
EM_MIPSMIPS R3000 Big-Endian only
EM_MIPS_RS4_BEMIPS R4000 Big-Endian
EM_SPARC64SPARC v9 64-bit unoffical
EM_PARISCHPPA
EM_SPARC32PLUSEnhanced instruction set SPARC
EM_PPCPowerPC
EM_ALPHADEC ALPHA
EM_SPARCV9SPARC version 9
EM_ALPHA_EXPDEC ALPHA
EM_VAXDEC VAX
e_versionファイルバージョン
定義意味
EV_NONE不当なバージョン
EV_CURRENT現在のバージョン
e_entryプロセスを開始する仮想アドレスを示す
ファイルにエントリポイントがないならば、0になります
e_phoffプログラムヘッダテーブルが存在する場所のファイルオフセット値(byte)
プログラムヘッダテーブルが存在しない場合は0がセットされる
e_shoffセクションヘッダテーブルが存在する場所のファイルオフセット値(byte)
セクションヘッダテーブルが存在しない場合は0がセットされる
e_flagsファイルに関連する、プロセッサに固有なフラグ
現在、定義されたフラグはありません。
e_ehsizeELF ヘッダのサイズ(byte)
e_phentsizeプログラムヘッダテーブルにあるエントリ 1個のサイズ
全てのエントリは同じ大きさ
e_phnumプログラムヘッダテーブル中のエントリの個数
e_phentsize と e_phnum の積は、テーブルのバイト単位の大きさになる。
ファイルにプログラムヘッダがないならば、e_phnumの値は 0 になる。
e_shentsizeセクションヘッダテーブルにあるエントリ 1個のサイズ
全てのエントリは同じ大きさ
e_shnumセクションヘッダテーブル中のエントリの個数
e_shentsize と e_shnum の積は、テーブルのバイト単位の大きさになる。
ファイルにセクションヘッダがないならば、e_shnumの値は 0 になる。
e_shstrndxセクションヘッダテーブルの、セクション名文字列テーブルに結びつけられたエントリへのインデックス
ファイルにセクション名文字列テーブルがないならば、SHN_UNDEFになる。
定義意味
SHN_UNDEF未定義か、存在しないか、無関係であるか、意味がないセクション参照を示す。
SHN_UNDEF からの相対で ``定義'' されるシンボルは、未定義シンボルになる。
SHN_LORESERVE予約のインデックスの範囲の下限を指定する。
SHN_LOPROCSHN_LOPROCからSHN_HIPROCまでの値は、プロセッサ固有のセマンティクスのために予約されている。
SHN_HIPROCSHN_LOPROCからSHN_HIPROCまでの値は、プロセッサ固有のセマンティクスのために予約されている。
SHN_ABSこれらの値は、セクションヘッダテーブルを参照しません。セクションヘッダテーブルは予約のインデックスのためにエントリを 含みません。対応する参照のために絶対的な値を指定します。
例えば、 SHN_ABS からの相対で定義されるシンボルは、絶対的な値を持ち、再配置による影響を受けません
SHN_COMMONこのセクションからの相対で定義されるシンボルは、共通シンボルであり、 Fortranの COMMON や領域が確保されていない C の外部変数が該当します。
SHN_HIRESERVE予約インデックス範囲の上限を指定します。
この範囲は、 SHN_LORESERVE と SHN_HIRESERVE の間であり、両端を含みます。
これらの値は、セクションヘッダテーブルを参照しません。セクションヘッダテーブルは予約のインデックスのためにエントリを含みません。

プログラムヘッダテーブル

実行可能ファイルまたは共有オブジェクトファイルはプログラムヘッダテーブルを持ちます。プログラムヘッダはプログラム実行にシステムが必要とするセグメントなどの情報が定義されています。セグメント情報は次のような構成になります。

typedef struct {
        Elf32_Word      p_type;         /* segment type */
        Elf32_Off       p_offset;       /* segment offset */
        Elf32_Addr      p_vaddr;        /* virtual address of segment */
        Elf32_Addr      p_paddr;        /* physical address - ignored? */
        Elf32_Word      p_filesz;       /* number of bytes in file for seg. */
        Elf32_Word      p_memsz;        /* number of bytes in mem. for seg. */
        Elf32_Word      p_flags;        /* flags */
        Elf32_Word      p_align;        /* memory alignment */
} Elf32_Phdr;	
typedef struct {
        Elf64_Half      p_type;         /* entry type */
        Elf64_Half      p_flags;        /* flags */
        Elf64_Off       p_offset;       /* offset */
        Elf64_Addr      p_vaddr;        /* virtual address */
        Elf64_Addr      p_paddr;        /* physical address */
        Elf64_Xword     p_filesz;       /* file size */
        Elf64_Xword     p_memsz;        /* memory size */
        Elf64_Xword     p_align;        /* memory & file alignment */
} Elf64_Phdr;	

プログラムヘッダテーブルは、セグメント情報がe_phnum個ある配列より構成されており、プログラムヘッダは、実行可能ファイルと共有オブジェクトファイルだけで意味があります。

セグメント情報は次のような値を持ちます。

プログラムヘッダテーブル内容
変数詳細
p_typeこの配列要素が記述しているセグメントの種類を示し、どのように配列要素を解釈すべきかを示します。
定義意味
PT_NULLこの配列要素は使われていないか他のメンバの値は未定義であることを示す。
これにより配列中にNULLエントリーを持つことが出来る。
PT_LOADこの配列要素は、ロード可能なセグメントであること示す。
ロードサイズは、p_filesz と p_memsz で記述される。
ファイルからのバイトは、メモリセグメントの先頭にマップされる。
セグメントのメモリサイズ (p_memsz) がファイルサイズ (p_filesz)より大きいならば、 ``余分な'' バイトは0で埋められる。
ファイルサイズは、メモリサイズを越えてはいけない。プログラムヘッダテーブルの中のロード可能なセグメントエントリは、昇順で現れ、p_vaddr メンバでソートされます。
PT_DYNAMICこの配列要素は、動的リンク情報であること示す。
PT_INTERPこの配列要素は、インタプリタとして起動するヌル文字で終わるパス名の場所と大きさを指定する。
このセグメントタイプは、実行可能ファイルのみで意味がある (本セグメントタイプは、共有オブジェクト中にあるかもしれない)。
本セグメントは、ファイル中で複数個存在してはいけない。存在する場合、全ロード可能セグメントエントリに先行する必要がある。
PT_NOTEこの配列要素は、ロケーションとサイズの為の補助情報であることを示す。
PT_SHLIBこのセグメントタイプは、予約されており、明記されていないセマンティクスを持つ。このタイプの配列要素を含むプログラムは、ABI に従わない。
PT_PHDRこの配列要素が存在する場合、ファイル中とメモリイメージ中における、プログラムヘッダテーブル自身の位置と大きさを指定する。
本セグメントタイプは、ファイル中で複数個存在してはいけない。さらに、プログラムヘッダテーブルがプログラムのメモリイメージに含まれる場合のみ、存在が許される。存在する場合、全ロード可能セグメントエントリに先行する必要がある。
PT_LOPROCPT_LOPROCからPT_HIPROCまでは、プロセッサ固有のセマンティクスのために予約されている。
PT_HIPROCPT_LOPROCからPT_HIPROCまでは、プロセッサ固有のセマンティクスのために予約されている。
p_offsetこのセグメントのファイルのオフセット。
p_vaddrこのセグメントの仮想アドレス。
p_paddr物理アドレッシングのシステム上では、このメンバは、セグメントの物理アドレスのために予約されている。
p_fileszこのメンバは、セグメントのファイルイメージのバイト数をを示す。0であるかもしれない。
p_memszこのメンバは、セグメントのメモリイメージのバイト数を示す。0であるかもしれない。
p_flagsセグメントの属性を表すフラグ。
定義意味
PF_X実行可能セグメント
PF_W書き込み可能なセグメント
PF_R読み取り可能なセグメント
テキストセグメントは、一般的にフラグ PF_X と PF_R を持つ。
データセグメントは、一般的に PF_X, PF_W , PF_R を持つ。
p_alignこのメンバは、メモリ中およびファイル中でセグメントが整列すべき値を持つ。

セクションヘッダテーブル

クションヘッダテーブルは、全てのファイルのセクションの位置決定を可能とします。各セクションにはプログラムコードや読取り専用データ、読書き可能データ、再配置エントリー、シンボル等の情報が種類ごとに格納されています。セクション情報は下記のようになります

typedef struct {
        Elf32_Word      sh_name;        /* name - index into section header
                                           string table section */
        Elf32_Word      sh_type;        /* type */
        Elf32_Word      sh_flags;       /* flags */
        Elf32_Addr      sh_addr;        /* address */
        Elf32_Off       sh_offset;      /* file offset */
        Elf32_Word      sh_size;        /* section size */
        Elf32_Word      sh_link;        /* section header table index link */
        Elf32_Word      sh_info;        /* extra information */
        Elf32_Word      sh_addralign;   /* address alignment */
        Elf32_Word      sh_entsize;     /* section entry size */
} Elf32_Shdr;      
typedef struct {
        Elf64_Half      sh_name;        /* section name */
        Elf64_Half      sh_type;        /* section type */
        Elf64_Xword     sh_flags;       /* section flags */
        Elf64_Addr      sh_addr;        /* virtual address */
        Elf64_Off       sh_offset;      /* file offset */
        Elf64_Xword     sh_size;        /* section size */
        Elf64_Half      sh_link;        /* link to another */
        Elf64_Half      sh_info;        /* misc info */
        Elf64_Xword     sh_addralign;   /* memory alignment */
        Elf64_Xword     sh_entsize;     /* table entry size */
} Elf64_Shdr;      

セクションヘッダテーブルは、セクション情報がe_phnum個ある配列より構成されています。

セクション情報は次のような値を持ちます。

セクションヘッダテーブル内容
変数詳細
sh_nameセクション名前のインデックス値を示す。
この値は、セクションヘッダテーブル上に存在する、文字列テーブルセクションへのインデックスであり、ヌル文字で終わる文字列の場所を示す。
sh_typeセクションの内容とセマンティクスを示す。
定義意味
SHT_NULLこの配列要素は使われていないか他のメンバの値は未定義であることを示す。
関連づけられたセクションを持たない。
SHT_PROGBITSこのセクションは、プログラムによって定義される情報を持つ。フォーマットと意味は、プログラムだけによってのみ決定される。
SHT_SYMTABこのセクションは、シンボルテーブルであることを表す。
一般的に、SHT_SYMTABはリンクエディットのためのシンボルを提供する。
これは、動的リンクにも使用可能である。これは完全なシンボルテーブルであるため、動的リンクのためには不必要な多くのシンボルを含む場合がある。
SHT_STRTABこのセクションは、文字列テーブルであることを表す。オブジェクトファイルは、複数の文字列テーブルセクションを持ち得る。
SHT_RELAこのセクションは、明示的な加数を持つ、再配置エントリであることを表す。オブジェクトは、複数の再配置セクションを持ち得る。
SHT_HASHこのセクションは、シンボルハッシュテーブルであることを表す。動的リンクに関連する全オブジェクトは、シンボルハッシュテーブルを含む必要がある。オブジェクトファイルは、単一のハッシュテーブルのみを持ち得る。
SHT_DYNAMICこのセクションは、動的リンクのための情報であることを表す。オブジェクトファイルは、単一の動的セクションのみを持ち得る。
SHT_NOTEこのセクションは、いくばくかの方法でファイルに印をする情報を持つ。
SHT_NOBITSこのタイプのセクションは、ファイル中の空間を占有しませんが、SHN_PROGBITS に似てる。このセクションはバイトを含まないが、sh_offset メンバは概念上のファイルオフセットを含む。
SHT_RELこのセクションは、明示的な加数無しの再配置オフセットであることを表す。オブジェクトファイルは、複数の再配置セクションを持ち得る。
SHT_SHLIBこのセクションは、予約されており、明記されていないセマンティクスを持つ。
SHT_DYNSYMこのセクションは、動的リンクシンボルの最小のセットを持つ。オブジェクトファイルは、SHN_SYMTAB セクションも含むことがでる。
SHT_LOPROCSHT_LOPROCからSHT_HIPROCまでの値は、プロセッサ固有のセマンティクスのために予約されている。
SHT_HIPROCSHT_LOPROCからSHT_HIPROCまでの値は、プロセッサ固有のセマンティクスのために予約されている。
SHT_LOUSERこの値は、アプリケーションプログラムのために予約されているインデックス範囲の下限を指定する。
SHT_HIUSERこの値は、アプリケーションプログラムのために予約されているインデックス範囲の上限を指定する。
HT_LOUSER と SHT_HIUSER の間のセクションタイプは、アプリケーションによって使用可能であり、現在または将来のシステム定義セクションタイプと衝突しない。
sh_flags セクションは、雑多な属性を記述する 1 ビットフラグをサポートする。フラグビットが sh_flags でセットされるならば、そのセクションの属性は``オン'' になる。そうでなければ、属性は ``オフ'' であるか、あてはまりません。未定義属性は、0にセットされる。
定義意味
SHF_WRITEセクションは、プロセス実行の間、書き込み可能であるべきデータを含む。
SHF_ALLOCセクションは、プロセス実行の間、メモリを占有する。
SHF_EXECINSTRセクションは、実行可能な機械語命令を含む。
SHF_MASKPROCこのマスクで含まれる全てのビットは、プロセッサ固有のセマンティクスのために確保される。
sh_addrセクションがプロセスのメモリイメージに現れる場合、このメンバは、セクションの最初のバイトが存在するアドレスを持つ。そうでない場合、このメンバは0を持つ。
sh_offsetこのメンバ値は、このセクションの、ファイル先頭からのバイトオフセットを表す。 1 つのセクションタイプ、すなわちSHT_NOBITS は、ファイル中の空間を占有せず、その sh_offsetメンバは、ファイル中の概念上の位置を指定する。
sh_sizeこのメンバは、セクションのバイトでの大きさを持つ。セクションタイプが SHT_NOBITS でない限り、セクションはファイル中の sh_size バイトを占有する。タイプ SHT_NOBITS のセクションは 0 以外の大きさを持ち得るが、ファイル中の空間を占有しない。
sh_infoこのメンバは、追加情報を持つ。この解釈は、セクションタイプ依存する。
sh_addralign若干のセクションには、アドレス境界の制約がありる。セクションがダブルワードを持つならば、システムはダブルワード境界をセクション全体に保証する必要がある。 sh_addr の値は、 sh_addralign で割った値が 0 となることが必要です。 0と正の 2 の羃乗だけが許される。0 または 1 の値は、セクションには境界の制約がないことを意味する。
sh_entsize若干のセクションは、固定長エントリのテーブルを持つ。例えばシンボルテーブルがこれに該当する。そのようなセクションのために、このメンバは、各エントリのバイトでの大きさを与える。セクションが固定サイズのエントリのテーブルを持たないならば、このメンバは0になる。

関係資料

KernelのObject構造
elf_exec関数
Last modified: Fri Jan 25 14:20:32 2008 JST