[インデックス 13890] ファイルの概要
このコミットは、Go言語のリンカツール (cmd/ld, cmd/5l, cmd/6l, cmd/8l) において、OpenBSDのELFバイナリ署名への対応を追加し、既存のNetBSDのELF署名コードをリファクタリングするものです。これにより、GoでビルドされたバイナリがOpenBSDのシステム要件を満たすようになります。
コミット
commit 31758b2c1a4fef9c387d039190e55c640bda9408
Author: Joel Sing <jsing@google.com>
Date: Fri Sep 21 12:51:39 2012 +1000
cmd/{ld,5l,6l,8l}: add support for OpenBSD ELF signatures
OpenBSD now requires ELF binaries to have a PT_NOTE that identifies
it as an OpenBSD binary. Refactor the existing NetBSD ELF signature
code and implement support for OpenBSD ELF signatures.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6489131
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/31758b2c1a4fef9c387d039190e55c640bda9408
元コミット内容
cmd/{ld,5l,6l,8l}: add support for OpenBSD ELF signatures
OpenBSD now requires ELF binaries to have a PT_NOTE that identifies
it as an OpenBSD binary. Refactor the existing NetBSD ELF signature
code and implement support for OpenBSD ELF signatures.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6489131
変更の背景
この変更の背景には、OpenBSDオペレーティングシステムがELF (Executable and Linkable Format) バイナリに対して、特定の識別子を持つ PT_NOTE セグメントの存在を要求するようになったという要件があります。PT_NOTE は、バイナリに関する追加情報(例えば、ビルド情報、OSのバージョン、セキュリティ関連のメタデータなど)を格納するために使用されるELFセグメントの一種です。OpenBSDは、システムがバイナリを正しく認識し、セキュリティポリシーを適用するために、この PT_NOTE セグメント内に特定の署名(識別情報)を必要とするようになりました。
Go言語のリンカは、実行可能ファイルを生成する際に、ターゲットOSの特定の要件を満たす必要があります。NetBSDも同様にELFバイナリに署名を要求していましたが、OpenBSDの新しい要件に対応するためには、既存のNetBSD向けコードをリファクタリングし、OpenBSD固有の署名生成ロジックを追加する必要が生じました。これにより、GoでビルドされたプログラムがOpenBSD上で問題なく実行できるようになります。
前提知識の解説
ELF (Executable and Linkable Format)
ELFは、Unix系オペレーティングシステム(Linux, BSD系OSなど)で広く使用されている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準ファイルフォーマットです。ELFファイルは、ヘッダ、プログラムヘッダテーブル、セクションヘッダテーブル、そして実際のデータ(コード、データ、シンボルテーブルなど)で構成されます。
- ELFヘッダ: ファイルの基本的な情報(マジックナンバー、アーキテクチャ、OS ABIなど)を含みます。
- プログラムヘッダテーブル (Program Header Table): 実行時にメモリにロードされるセグメント(コード、データなど)のレイアウトを記述します。各エントリは
Elf_Phdr構造体で表されます。 - セクションヘッダテーブル (Section Header Table): リンク時に使用されるファイルの論理的な構造(
.text、.data、.bssなどのセクション)を記述します。各エントリはElf_Shdr構造体で表されます。
PT_NOTE セグメント
PT_NOTE はプログラムヘッダテーブルのエントリタイプの一つで、バイナリに関する特別な情報を格納するために使用されます。このセグメントは実行可能コードやデータを含まず、通常はOSやツールがバイナリを識別したり、特定の動作を決定したりするためのメタデータを提供します。PT_NOTE セグメント内のデータは、通常 Elf_Note 構造体のシーケンスとして構成されます。
Elf_Note 構造体
Elf_Note 構造体は、PT_NOTE セグメント内に格納される個々のノートエントリのヘッダを定義します。
typedef struct {
Elf32_Word n_namesz; // Name size
Elf32_Word n_descsz; // Descriptor size
Elf32_Word n_type; // Type of note
} Elf32_Nhdr; // For 32-bit ELF
typedef struct {
Elf64_Word n_namesz; // Name size
Elf64_Word n_descsz; // Descriptor size
Elf64_Word n_type; // Type of note
} Elf64_Nhdr; // For 64-bit ELF
n_namesz: ノートの所有者を示す名前のサイズ(ヌル終端文字を含む)。n_descsz: ノートの記述子(データ)のサイズ。n_type: ノートのタイプ。これは、記述子の内容を解釈するためのベンダー固有の識別子です。
Elf_Note ヘッダの後に、n_namesz バイトの名前データ、そして n_descsz バイトの記述子データが続きます。これらのデータは通常、ワード境界にアラインされます。
Go言語のリンカ (cmd/ld, cmd/5l, cmd/6l, cmd/8l)
Go言語のツールチェインには、異なるアーキテクチャ向けのリンカが含まれています。
cmd/ld: 汎用リンカ。cmd/5l: ARMアーキテクチャ向けのリンカ。cmd/6l: AMD64 (x86-64) アーキテクチャ向けのリンカ。cmd/8l: x86 (32-bit) アーキテクチャ向けのリンカ。
これらのリンカは、Goのコンパイラによって生成されたオブジェクトファイルを結合し、実行可能なバイナリを生成する役割を担っています。OS固有のバイナリ要件(今回のELF署名など)は、これらのリンカによって処理されます。
SHT_NOTE, SHF_ALLOC
SHT_NOTE: セクションヘッダのタイプの一つで、ノートセクションを示します。ノートセクションは、PT_NOTEセグメントの元となるデータを含みます。SHF_ALLOC: セクションフラグの一つで、実行時にメモリにロードされるセクションであることを示します。PT_NOTEセグメントは通常、このフラグを持ちます。
技術的詳細
このコミットの主要な技術的変更点は、OpenBSDのELF署名要件を満たすために、GoリンカがELFバイナリに特定の PT_NOTE セグメントを追加するロジックを実装したことです。これは、既存のNetBSDの署名生成ロジックをリファクタリングし、共通の処理を抽象化することで実現されています。
具体的には、以下の変更が行われています。
-
OpenBSD識別子の追加:
ElfStrNoteOpenbsdIdentという新しい文字列識別子が追加され、OpenBSDのPT_NOTEセグメントのセクション名 (.note.openbsd.ident) を表します。- リンカの初期化フェーズ (
doelf関数) で、HEADTYPE == Hopenbsdの場合にこの識別子が設定されます。
-
共通のELFノート処理の抽象化:
- 既存の
elfnetbsdsig関数(NetBSDの署名生成ロジック)が、より汎用的なelfnote関数にリファクタリングされました。 elfnote関数は、Elf_Noteのサイズ計算、セクションヘッダ (ElfShdr) のタイプ、フラグ、アラインメント、アドレス、オフセット、サイズの設定を行います。これにより、NetBSDとOpenBSDの両方の署名生成で共通のロジックが利用できるようになりました。elfwritenotehdr関数が新しく導入され、Elf_Noteヘッダの書き込みを抽象化します。これにより、名前サイズ、記述子サイズ、タイプタグを引数として受け取り、実際のノートヘッダをファイルに書き込むことができます。
- 既存の
-
OpenBSD署名生成ロジックの実装:
elfopenbsdsig関数が追加され、OpenBSDのPT_NOTEセグメントのサイズを計算します。elfwriteopenbsdsig関数が追加され、OpenBSD固有のElf_Noteヘッダとそれに続く名前 ("OpenBSD\0") およびバージョン (0) をELFファイルに書き込みます。- OpenBSDの署名に必要な定数 (
ELF_NOTE_OPENBSD_NAMESZ,ELF_NOTE_OPENBSD_DESCSZ,ELF_NOTE_OPENBSD_TAG,ELF_NOTE_OPENBSD_NAME,ELF_NOTE_OPENBSD_VERSION) が定義されています。
-
リンカのフローへの統合:
asmb関数(リンカの主要なアセンブル処理)内で、HEADTYPE == Hopenbsdの場合にOpenBSDの署名生成ロジックが呼び出されるように変更されました。- 具体的には、
PT_NOTEセグメントのセクションヘッダとプログラムヘッダが適切に設定され、バイナリの最後に署名データが書き込まれるようになります。
この変更により、GoリンカはOpenBSDの新しいバイナリ要件に準拠し、Goでビルドされた実行可能ファイルがOpenBSDシステムで正しく認識され、実行されることが保証されます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に src/cmd/ld/elf.c と src/cmd/ld/elf.h に集中しています。また、各アーキテクチャ固有のリンカファイル (src/cmd/5l/asm.c, src/cmd/6l/asm.c, src/cmd/8l/asm.c) も、新しいOpenBSDの識別子と署名生成ロジックを呼び出すように変更されています。
src/cmd/ld/elf.c
elfnetbsdsig関数のリファクタリング:- 以前はNetBSD固有のロジックを含んでいた
elfnetbsdsigが、より汎用的なelfnote関数を呼び出すように変更されました。 elfnote関数は、Elf_Noteのサイズ計算とセクションヘッダの基本設定を行います。
- 以前はNetBSD固有のロジックを含んでいた
elfwritenetbsdsig関数のリファクタリング:elfwritenetbsdsigは、新しく導入されたelfwritenotehdrを呼び出してElf_Noteヘッダを書き込み、その後NetBSD固有のデータ(名前とバージョン)を書き込むように変更されました。
elfnote関数の新規追加:
この関数は、int elfnote(ElfShdr *sh, uint64 startva, uint64 resoff, int sz) { uint64 n; n = sizeof(Elf_Note) + sz + resoff % 4; // Calculate total size including padding sh->type = SHT_NOTE; sh->flags = SHF_ALLOC; sh->addralign = 4; sh->addr = startva + resoff - n; sh->off = resoff - n; sh->size = n; return n; }PT_NOTEセグメントのセクションヘッダ (ElfShdr) のプロパティを設定し、そのサイズを計算します。elfwritenotehdr関数の新規追加:
この関数は、ElfShdr * elfwritenotehdr(vlong stridx, uint32 namesz, uint32 descsz, uint32 tag) { ElfShdr *sh = nil; int i; for(i=0; i<nshdr; i++) if(shdr[i]->name == stridx) sh = shdr[i]; if(sh == nil) return nil; // Write Elf_Note header. cseek(sh->off); LPUT(namesz); LPUT(descsz); LPUT(tag); return sh; }Elf_Noteヘッダのn_namesz,n_descsz,n_typeフィールドをファイルに書き込みます。- OpenBSD署名関連の定数定義:
#define ELF_NOTE_OPENBSD_NAMESZ 8 #define ELF_NOTE_OPENBSD_DESCSZ 4 #define ELF_NOTE_OPENBSD_TAG 1 #define ELF_NOTE_OPENBSD_NAME "OpenBSD\0" #define ELF_NOTE_OPENBSD_VERSION 0 elfopenbsdsig関数の新規追加:
OpenBSDの署名に必要なサイズを計算し、int elfopenbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) { int n; n = ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ; return elfnote(sh, startva, resoff, n); }elfnoteを呼び出します。elfwriteopenbsdsig関数の新規追加:
OpenBSDの署名データをファイルに書き込みます。int elfwriteopenbsdsig(vlong stridx) { ElfShdr *sh; // Write Elf_Note header. sh = elfwritenotehdr(stridx, ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG); if(sh == nil) return 0; // Followed by OpenBSD string and version. cwrite(ELF_NOTE_OPENBSD_NAME, ELF_NOTE_OPENBSD_NAMESZ); LPUT(ELF_NOTE_OPENBSD_VERSION); return sh->size; }
src/cmd/ld/elf.h
elfopenbsdsigとelfwriteopenbsdsig関数のプロトタイプ宣言が追加されました。
src/cmd/{5l,6l,8l}/asm.c
ElfStrNoteOpenbsdIdentがenumに追加されました。doelf関数内で、HEADTYPE == Hopenbsdの場合にElfStrNoteOpenbsdIdentが初期化されるようになりました。asmb関数内で、HEADTYPE == Hopenbsdの場合にelfopenbsdsigとelfwriteopenbsdsigが呼び出されるように変更されました。これにより、OpenBSDのELF署名がバイナリに組み込まれます。
コアとなるコードの解説
このコミットの核心は、ELFバイナリにOS固有の識別子を埋め込むための汎用的なメカニズムを導入し、それをOpenBSDの要件に適用した点にあります。
-
elfnote関数の役割: この関数は、PT_NOTEセグメントに対応するセクションヘッダ (ElfShdr) の基本的なプロパティを設定します。SHT_NOTEタイプ、SHF_ALLOCフラグ、アラインメント、そして最も重要なのは、バイナリ内のどこにノートデータが配置されるかを示すアドレスとオフセット、およびそのサイズを計算することです。resoffは、バイナリの末尾からのオフセットを示し、ノートデータがバイナリの最後に配置されることを意味します。 -
elfwritenotehdr関数の役割: この関数は、Elf_Note構造体のヘッダ部分(n_namesz,n_descsz,n_type)をELFファイルに書き込む責任を持ちます。これにより、各OS固有の署名生成関数は、この共通のヘッダ書き込みロジックを再利用できます。stridxは、セクションヘッダテーブル内のノートセクションの名前(例:.note.netbsd.identや.note.openbsd.ident)へのインデックスです。 -
OpenBSD固有の署名ロジック (
elfopenbsdsig,elfwriteopenbsdsig):elfopenbsdsigは、OpenBSDの署名に必要なElf_Noteの名前と記述子の合計サイズを計算し、elfnoteを呼び出してセクションヘッダを設定します。elfwriteopenbsdsigは、elfwritenotehdrを使って共通のノートヘッダを書き込んだ後、OpenBSD固有の名前文字列 ("OpenBSD\0") とバージョン (0) をファイルに書き込みます。これにより、OpenBSDシステムがバイナリを正しく識別できるようになります。
-
リンカの統合: 各アーキテクチャのリンカ (
5l,6l,8l) のasmb関数は、最終的なバイナリをアセンブルする場所です。ここで、ターゲットOSがOpenBSDである場合 (HEADTYPE == Hopenbsd) に、新しく追加されたelfopenbsdsigとelfwriteopenbsdsigが呼び出されます。これにより、Goでビルドされた実行可能ファイルにOpenBSDが必要とするPT_NOTEセグメントが確実に含まれるようになります。
この一連の変更により、GoリンカはELFバイナリの生成において、より柔軟かつ拡張性の高い方法でOS固有の要件に対応できるようになりました。
関連リンク
- Go Issue/Change List: https://golang.org/cl/6489131 (コミットメッセージに記載されているGoの変更リストへのリンク)
- OpenBSD ELF Binary Requirements (General Information): OpenBSDの公式ドキュメントやメーリングリストのアーカイブで、ELFバイナリの要件に関する詳細情報が見つかる可能性があります。
参考にした情報源リンク
- ELF (Executable and Linkable Format) Specification:
- PT_NOTE Segment:
- https://docs.oracle.com/cd/E19683-01/817-3677/6m751112j/index.html (Oracle Solaris Studioのドキュメントですが、PT_NOTEの一般的な説明があります)
- NetBSD ELF Notes (Example Context):
- NetBSDのソースコード (
sys/exec_elf.hなど) は、NetBSDがどのようにELFノートを使用しているかの参考になります。
- NetBSDのソースコード (
- OpenBSD ELF Notes (Example Context):
- OpenBSDのソースコードや開発者メーリングリストの議論は、OpenBSDがなぜ特定のELFノートを要求するようになったかの背景情報を提供します。
- Go Toolchain Documentation:
- Goの公式ドキュメントやソースコードは、リンカの内部動作を理解する上で役立ちます。