ELF は、実行可能でリンク可能なファイル形式の略です。
ELF は、Linux のオブジェクト ファイルの標準ファイル形式として使用されます。これ以前は、a.out ファイル形式が標準として使用されていましたが、最近では ELF が標準として使用されています。
ELF のサポート:
- さまざまなプロセッサー
- 異なるデータ エンコーディング
- さまざまなクラスのマシン
この記事では、さまざまなタイプの ELF オブジェクト ファイルと ELF ヘッダーについて説明します。
ELF オブジェクト ファイル
コンパイルされたコードを含むファイルは、オブジェクト ファイルと呼ばれます。オブジェクト ファイルは次のいずれかのタイプです:
1.再配置可能ファイル
このタイプのオブジェクト ファイルには、実行可能なバイナリまたは共有オブジェクト ファイルを生成するために、他の再配置可能なファイルと一緒にリンクできるデータとコードが含まれています。簡単に言うと、リロケータブル ファイルは、次の方法でコードをコンパイルしたときに生成される .o ファイルと同じです。
gcc -Wall -c test.c -o test.o
したがって、上記の操作の後に生成される test.o は再配置可能なファイルになります。
2.共有オブジェクト ファイル
このタイプのオブジェクト ファイルは、動的リンカーによって使用され、それを実行可能ファイルや他の共有オブジェクト ファイルと組み合わせて、完全なプロセス イメージを作成します。簡単に言うと、共有オブジェクト ファイルは、次のように -fPIC フラグを指定してコードをコンパイルしたときに生成される .so ファイルと同じです。
gcc -c -Wall -Werror -fPIC shared.c gcc -shared -o libshared.so shared.o
上記の 2 つのコマンドを実行すると、共有オブジェクト ファイル libshared.o が出力として生成されます。
注:Linux 共有ライブラリの詳細については、記事 Linux 共有ライブラリを参照してください
3.実行ファイル
このタイプのオブジェクト ファイルは、実行時にプログラムを実行できるファイルです。簡単に言えば、次のようなコマンドの出力です:
gcc -Wall test.c -o test
したがって、出力「test」は、実行時に test.c ファイルに記述されたロジックを実行する実行可能ファイルになります。
したがって、上記のオブジェクト ファイルの種類から結論付けられるように、オブジェクト ファイルはプログラムのビルドから実行まで、またはリンクから実行段階まで関与します (Linux/GCC のコンパイル段階について詳しくは、私たちの記事「Journey of C プログラム)
ELF ヘッダー
上記のすべてのオブジェクト ファイルは、Linux では ELF タイプです。
これは、これらの各ファイルを調べることで簡単に証明できます。たとえば、システム内の 3 つのファイルをそれぞれ調べました。
再配置可能ファイル:
$ vim func.o ^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^A^@>^@^A^@^@^@^@^@^@^@^@^@^@^@ ^@UHå¿^@^@^@^@è^@^@^@^@¸^@^@^@^@è^@^@^@^@ÉÃ^@^@ Inside func()^@^@GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3^@^T^@^@^@^@ ^@^@^@^AzR^@^Ax^P^A^[^L^G^H^A^@^@^\^@^.symtab^@.strtab^@.shstrtab^ @.rela.text^@.data^@.bss^@.rodata^@.comment^@.note.GNU-stack^@.rela.eh_frame ^@^@^@^@^@^@^@^@^@^@^@^@^@
共有オブジェクト ファイル:
$ vim libshared.so ^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^C^@>^@^A^@^@^@ ^@^@^@^@^@^A^@^@^@^F^@^ ^@^@^@^@^@è^A^@^@^@^@^@^@è^A^@^@^@^@^@^@^A^@^@^@^@^@^@^@^D^@^@^@^T^@^@^ @^C^@^@^@GNU^@·YG®z^L^ZÊ7uÈí,?^N^@^@^@^@^C^@^@^@^L^@^@^@
実行ファイル:
$ vim test ^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^B^@>^@^A^@^@^@P@^@^@^@^@^@@^@^@^@^@^@< ^@^@^@D^@^@^^B^@^@^@^@^@^@^A^@^@^@^@^@^@^@/lib64/ld-linux-x86-64.so.2^@^D^ @^@^@^D^@^@^@^T^@^@^@^C^@^@^@GNU^@òÁ}CKbE;ära`6"^O^N\^C^@^@^@
したがって、これらはバイナリ ファイルであるため、各ファイルの先頭にある ELF 文字列以外は何も理解できないことがわかります。これは、これらのファイルが ELF 形式のみであることを示しています。
各ファイルは、ファイルの完全な構成をほとんど示す ELF ヘッダーで始まります。たとえば、リロケータブルおよび共有オブジェクト ファイルにはセクションが含まれていますが、実行可能ファイルはセグメントで構成されています。そのため、オブジェクト ファイルのタイプに応じて、ELF ヘッダーはファイルに関する詳細情報を提供します。
ほとんどの場合、実行可能ファイルの場合、ELF ヘッダーの後にプログラム ヘッダー テーブルが続きます。プログラム ヘッダー テーブルは、プロセス イメージの作成に役立ちます。プロセス イメージ (実行可能ファイルの実行後に作成される) の作成に役立つため、プログラム ヘッダー テーブルは実行可能ファイルでは必須になりますが、再配置可能および共有オブジェクト ファイルではオプションです。
以下は、ELF ヘッダーの構成です:
#define EI_NIDENT 16 typedef struct { e_ident[EI_NIDENT]; unsigned char e_type; Elf32_Half e_machine; Elf32_Half e_version; Elf32_Word e_entry; Elf32_Addr e_phoff; Elf32_Off e_shoff; Elf32_Off e_flags; Elf32_Word e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; } Elf32_Ehdr;
したがって、上記の組織は構造の形をしていることがわかります。ここで各メンバーを詳細に説明すると物事が複雑になるため、この構造の各メンバーが保持する基本的な意味と情報を調べて、その特定のフィールドのアイデアを得ることができます.
1. e_ident
ELF 形式がさまざまなクラスのマシン、プロセッサなどをサポートしていることはすでにわかっているため、これらすべてをサポートするために、ELF ファイルの初期情報には、実行可能ファイルが実行されているプロセッサとは無関係にファイルを解釈する方法に関する情報が含まれています。配列「e_ident」はまったく同じ情報を提供します:
Name Value Purpose EI_MAG 0 File identification EI_MAG1 1 File identification EI_MAG2 2 File identification EI_MAG3 3 File identification EI_CLASS 4 File class EI_DATA 5 Data encoding EI_VERSION 6 File version EI_PAD 7 Start of padding bytes EI_NIDENT 16 Size of e_ident[]
- EI_MAG 上記の最初の 4 バイトは、マジック ナンバー「0x7fELF」を保持します。
- EI_CLASS ELF は、32 ビットまたは 64 ビットの 2 つのクラスを持つことができます。これにより、ファイル形式が移植可能になります。
- EI_DATA このメンバーは、データのエンコードに関する情報を提供します。簡単に言えば、この情報は、データがビッグ エンディアン形式かリトル エンディアン形式かを示します。
- EI_VERSION このメンバーは、オブジェクト ファイルのバージョンに関する情報を提供します。
- EI_PAD このメンバーは、情報の e_indent 配列内の未使用バイトの開始をマークします。
- EI_NIDENT このメンバーは、配列 e_indent のサイズを提供します。これは、ELF ファイルの解析に役立ちます。
2. e_type
このメンバーは、オブジェクト ファイルの種類を識別します。たとえば、オブジェクト ファイルの種類は次のとおりです。
Name Value Meaning ET_NONE 0 No file type ET_REL 1 Relocatable file ET_EXEC 2 Executable file ET_DYN 3 Shared object file ET_CORE 4 Core file
注:上記のリストはすべてを網羅しているわけではありませんが、ELF が参照できる主なオブジェクト ファイルの種類に関する情報を提供しています。
3. e_machine
このメンバーは、ELF ファイルが必要とするアーキテクチャに関する情報を提供します。
Name Value Meaning ET_NONE 0 No machine EM_M32 1 AT&T WE 32100 EM_SPARC 2 SPARC EM_386 3 Intel Architecture EM_68K 4 Motorola 68000 EM_88K 5 Motorola 88000 EM_860 7 Intel 80860 EM_MIPS 8 MIPS RS3000 Big-Endian EM_MIPS_RS4_BE 10 MIPS RS4000 Big-Endian RESERVED 11-16 Reserved for future use
4.追加メンバー
上記の 3 つのメンバーとは別に、次のメンバーもあります。
- e_version:このメンバーは、ELF オブジェクト ファイルのバージョン情報を提供します。
- e_entry:このメンバーは、プロセスを開始できるようにシステムが制御を転送する必要があるエントリ ポイントの仮想アドレス情報を提供します。
- e_phoff:このメンバーは、プログラム ヘッダー テーブルへのオフセットを保持します。この情報はバイト単位で格納されます。プログラム ヘッダー テーブルがない場合、このメンバーに含まれる情報はゼロです。
- e_shoff:このメンバーは、セクション ヘッダー テーブルへのオフセットを保持します。 e_phoff と同様に、この情報もバイト形式で保存され、セクション ヘッダー テーブルがない場合、このフィールドに含まれる情報はゼロです。
- e_flags:このメンバーは、プロセス固有のフラグに関連する情報を保持します。
- e_ehsize:このメンバーは、ELF ヘッダー サイズに関連する情報を byes で保持します。
- e_phentsize:このメンバーは、オブジェクト ファイルのプログラム ヘッダー テーブル内の 1 つのエントリのサイズに関する情報を保持します。すべてのエントリのサイズが同じであることに注意してください。
- e_phnum:このメンバーは、プログラム ヘッダー テーブルのエントリ数に関する情報を保持します。
- e_shentsize:このメンバーは、セクション ヘッダー テーブルの 1 つのエントリのサイズに関する情報を保持します。サイズはバイト数で表されます。
- e_shnum:このメンバーは、セクション ヘッダー テーブルのエントリ数に関する情報を提供します。
ephnum と ephentsize の積はプログラム ヘッダー テーブルの合計サイズをバイト単位で示し、同様に eshnum と eshentsize の積はセクション ヘッダー テーブルの合計サイズをバイト単位で示すことに注意してください。