a.out形式はUNIXで長い間使われてきたが、Unix System Vの登場時に、クロスコンパイルや動的リンクなどに、対応するためにより良い形式が必要だとして、ELF(Executable and Linking Format)が作られました。
ELFは、再配置可可能オブジェクト・実行可能・共有オブジェクト・コアという4種類のファイルがあます。
- 再配置可可能オブジェクト
- 再配置可可能ファイルは、アセンブラやコンパイラが作成する、オブジェクトファイル
- 実行可能
- 実行可能は、再配置が全て完了し、シンボルも解決してある(実行時に解決される、共有ライブラリのシンボルは除く) "lsコマンド"のような実行可能なファイル
- 共有オブジェクト
- リンカ用のシンボル情報と実行時に使われるコードを格納する、共有ライブラリ等のファイル
- コア
ELF ファイルフォーマットを使っている実行可能ファイルはELFヘッダを持ちます。そして、プログラムヘッダテーブルか、セクションヘッダテーブル、あるいはその両方が続きます。
システムローダはプログラムヘッダテーブに記述されたセグメント集合として扱い、コンパイラ・アセンブラ・リンカはセクションヘッダテーブルがに記述された論理セクション集合としてELFを扱います。
セグメントとセクション
ELF ヘッダは、常にファイルのオフセット 0 にあります。プログラムヘッダテーブルとセクションヘッダテーブルのファイル中のオフセットは、 ELF ヘッダで定義されています。OpenBSDではsys/exec_elf.hに定義されていて、32bit アーキテクチャと64bit アーキテクチャ用にそれぞれ別に定義されています。
32bit アーキテクチャ
typedef struct elfhdr{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
Elf32_Addr 符号無しプログラムアドレス
Elf32_Half 符号無しハーフワードフィールド
Elf32_Off 符号無しファイルオフセット
Elf32_Sword 符号付き大整数
Elf32_Word フィールドまたは符号無し大整数
Elf32_Size 符号無しオブジェクトサイズ
64bit アーキテクチャ
typedef struct {
unsigned char e_ident[EI_NIDENT];
Elf64_Quarter e_type;
Elf64_Quarter e_machine;
Elf64_Half e_version;
Elf64_Addr e_entry;
Elf64_Off e_phoff;
Elf64_Off e_shoff;
Elf64_Half e_flags;
Elf64_Quarter e_ehsize;
Elf64_Quarter e_phentsize;
Elf64_Quarter e_phnum;
Elf64_Quarter e_shentsize;
Elf64_Quarter e_shnum;
Elf64_Quarter e_shstrndx;
} Elf64_Ehdr;
Elf64_Addr 符号無しプログラムアドレス
Elf64_Shalf 符号付きハーフワードフィールド
Elf64_Half 符号無しハーフワードフィールド
Elf64_Off 符号無しファイルオフセット
Elf64_Sword 符号付き大整数
Elf64_Word フィールドまたは符号無し大整数
Elf64_Xword 符号無しオブジェクトサイズまたはalignment
Elf64_Sxword 符号付きオブジェクトサイズまたはalignment
Elf64_Quarter 符号無しクォータワードフィールド
そして、これらのヘッダは次の値を持ちます。
実行可能ファイルまたは共有オブジェクトファイルはプログラムヘッダテーブルを持ちます。プログラムヘッダはプログラム実行にシステムが必要とするセグメントなどの情報が定義されています。セグメント情報は次のような構成になります。
typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
typedef struct {
Elf64_Half p_type;
Elf64_Half p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
Elf64_Xword p_filesz;
Elf64_Xword p_memsz;
Elf64_Xword p_align;
} 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_LOPROC | PT_LOPROCからPT_HIPROCまでは、プロセッサ固有のセマンティクスのために予約されている。 |
PT_HIPROC | PT_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;
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;
typedef struct {
Elf64_Half sh_name;
Elf64_Half sh_type;
Elf64_Xword sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Half sh_link;
Elf64_Half sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
} 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_LOPROC | SHT_LOPROCからSHT_HIPROCまでの値は、プロセッサ固有のセマンティクスのために予約されている。 |
SHT_HIPROC | SHT_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_link | このメンバは、セクションヘッダテーブルインデックスリンクを持つ。この解釈は、セクションタイプ依存する。 |
sh_info | このメンバは、追加情報を持つ。この解釈は、セクションタイプ依存する。 |
sh_addralign | 若干のセクションには、アドレス境界の制約がありる。セクションがダブルワードを持つならば、システムはダブルワード境界をセクション全体に保証する必要がある。 sh_addr の値は、 sh_addralign で割った値が 0 となることが必要です。 0と正の 2 の羃乗だけが許される。0 または 1 の値は、セクションには境界の制約がないことを意味する。 |
sh_entsize | 若干のセクションは、固定長エントリのテーブルを持つ。例えばシンボルテーブルがこれに該当する。そのようなセクションのために、このメンバは、各エントリのバイトでの大きさを与える。セクションが固定サイズのエントリのテーブルを持たないならば、このメンバは0になる。 |