[インデックス 10888] ファイルの概要
このコミットは、Go言語のリンカー(5l、6l、8l)において、NetBSD向けのELF署名ノートセクションのサポートを追加する重要な変更です。NetBSDシステムでGoプログラムを実行するために必要な.note.netbsd.ident
セクションの生成機能を実装しています。
コミット
コミット作成者: Joel Sing jsing@google.com
コミット日時: 2011年12月20日 12:25:06 +1100
コミットメッセージ: 5l/6l/8l: add support for netbsd signature note section
レビュー: R=m4dh4tt3r, jsing, rsc
メーリングリスト: CC=golang-dev
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5842f7e46a278cbf75e37f8a489e1f02e4acc8ac
元コミット内容
このコミットは以下のファイルを変更しています:
- src/cmd/5l/asm.c: 33行追加、5行削除 (ARM用リンカー)
- src/cmd/6l/asm.c: 28行追加、4行削除 (AMD64用リンカー)
- src/cmd/8l/asm.c: 33行追加、5行削除 (x86用リンカー)
- src/cmd/ld/elf.c: 72行追加、10行削除 (ELF共通処理)
- src/cmd/ld/elf.h: 6行追加、2行削除 (ELFヘッダー定義)
合計で141行の追加と31行の削除が行われています。
変更の背景
NetBSDにおけるELF実行可能ファイルの問題
2011年頃のNetBSDでは、様々なオペレーティングシステムベンダーが異なるシステムコールインターフェースを期待するELFバイナリを配布していました。この状況を受けて、NetBSDカーネルは認識可能なPT_NOTEセクションを含まないELFバイナリに対してENOEXECエラーを返すようになりました。
Go言語の課題
Go言語は当時、静的リンクによってバイナリを生成していましたが、NetBSD環境では適切な識別セクションが欠如しているため、生成されたバイナリが正常に実行されない問題がありました。この問題を解決するためには、NetBSD固有の.note.netbsd.ident
セクションをELFバイナリに追加する必要がありました。
前提知識の解説
ELFフォーマットとノートセクション
**ELF(Executable and Linkable Format)**は、Unix系オペレーティングシステムで使用される標準的な実行可能ファイル形式です。ELFファイルは以下の主要構成要素を持ちます:
- ELFヘッダー: ファイルの基本情報
- プログラムヘッダーテーブル: 実行時に必要なセグメント情報
- セクションヘッダーテーブル: リンク時に必要なセクション情報
ノートセクションは、ELFファイルにベンダー固有の情報を埋め込むための仕組みです:
- SHT_NOTE: セクションヘッダータイプ
- PT_NOTE: プログラムヘッダータイプ
Go言語のリンカー(2011年時点)
Go言語は当時、Plan 9の命名規則に従った複数のリンカーを使用していました:
- 5l: ARM アーキテクチャ用リンカー
- 6l: AMD64 (x86-64) アーキテクチャ用リンカー
- 8l: x86 (i386) アーキテクチャ用リンカー
これらのリンカーは静的リンクを標準とし、Goランタイムと必要な型情報をすべて含んだバイナリを生成していました。
NetBSDのELF識別機構
NetBSDは以下の構造でELFバイナリを識別します:
// NetBSDのELF注記定義
#define ELF_NOTE_TYPE_NETBSD_TAG 1
#define ELF_NOTE_NETBSD_NAMESZ 7
#define ELF_NOTE_NETBSD_DESCSZ 4
#define ELF_NOTE_NETBSD_NAME "NetBSD\0\0"
#define ELF_NOTE_NETBSD_VERSION 599000000 /* NetBSD 5.99 */
技術的詳細
実装アーキテクチャ
このコミットは、以下の階層構造で実装されています:
- 共通ELF処理層 (
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
)
主要な新機能
1. NetBSD識別文字列の追加
各リンカーのasm.cファイルに新しい文字列インデックスを追加:
enum {
// 既存の定義...
ElfStrNoteNetbsdIdent, // 新規追加
NElfStr
};
2. セクション文字列の動的追加
NetBSDターゲットの場合のみ、セクション文字列テーブルに.note.netbsd.ident
を追加:
if(HEADTYPE == Hnetbsd)
elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident");
3. 新しいELF処理関数の実装
elfnetbsdsig関数: NetBSD署名セクションの設定
int elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff)
{
int n;
n = sizeof(Elf_Note) + ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1;
n += resoff % 4; // 4バイト境界への調整
sh->addr = startva + resoff - n;
sh->off = resoff - n;
sh->size = n;
return n;
}
elfwritenetbsdsig関数: NetBSD署名データの書き込み
int elfwritenetbsdsig(vlong stridx)
{
// セクションの検索
ElfShdr *sh = nil;
int i;
for(i = 0; i < hdr.shnum; i++)
if(shdr[i]->name == stridx)
sh = shdr[i];
if(sh == nil) return 0;
// ELF_Note構造の書き込み
cseek(sh->off);
LPUT(ELF_NOTE_NETBSD_NAMESZ); // 名前サイズ: 7
LPUT(ELF_NOTE_NETBSD_DESCSZ); // 説明サイズ: 4
LPUT(ELF_NOTE_TYPE_NETBSD_TAG); // タイプ: 1
cwrite(ELF_NOTE_NETBSD_NAME, 8); // "NetBSD\0\0"
LPUT(ELF_NOTE_NETBSD_VERSION); // バージョン: 599000000
return sh->size;
}
4. elfinterp関数の改良
既存のインタープリター処理を改良し、より柔軟なオフセット管理を実現:
// 変更前
void elfinterp(ElfShdr *sh, uint64 startva, char *p)
// 変更後
int elfinterp(ElfShdr *sh, uint64 startva, uint64 resoff, char *p)
メモリ管理の改善
このコミットでは、ELFヘッダー周辺のメモリ管理も改善されています:
- resoff変数の導入: 予約領域のオフセット管理
- 動的オフセット計算: 複数のセクションに対応した柔軟なオフセット計算
- 境界調整: 4バイト境界への自動調整
コアとなるコードの変更箇所
1. src/cmd/ld/elf.c (line 378-416)
NetBSD署名セクションの核となる実装:
int elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff)
{
int n;
n = sizeof(Elf_Note) + ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1;
n += resoff % 4;
sh->addr = startva + resoff - n;
sh->off = resoff - n;
sh->size = n;
return n;
}
int elfwritenetbsdsig(vlong stridx) {
ElfShdr *sh = nil;
int i;
for(i = 0; i < hdr.shnum; i++)
if(shdr[i]->name == stridx)
sh = shdr[i];
if(sh == nil)
return 0;
cseek(sh->off);
LPUT(ELF_NOTE_NETBSD_NAMESZ);
LPUT(ELF_NOTE_NETBSD_DESCSZ);
LPUT(ELF_NOTE_TYPE_NETBSD_TAG);
cwrite(ELF_NOTE_NETBSD_NAME, 8);
LPUT(ELF_NOTE_NETBSD_VERSION);
return sh->size;
}
2. src/cmd/5l/asm.c, src/cmd/6l/asm.c, src/cmd/8l/asm.c (各87-98行付近)
NetBSDターゲット判定とセクション作成:
if(HEADTYPE == Hnetbsd) {
sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]);
sh->type = SHT_NOTE;
sh->flags = SHF_ALLOC;
sh->addralign = 4;
resoff -= elfnetbsdsig(sh, startva, resoff);
ph = newElfPhdr();
ph->type = PT_NOTE;
ph->flags = PF_R;
phsh(ph, sh);
}
3. src/cmd/ld/elf.h (line 429-432)
新しい関数のプロトタイプ宣言:
int elfinterp(ElfShdr*, uint64, uint64, char*);
int elfwriteinterp(vlong);
int elfnetbsdsig(ElfShdr*, uint64, uint64);
int elfwritenetbsdsig(vlong);
コアとなるコードの解説
NetBSD識別メカニズム
NetBSDの識別は、ELFノート構造を使用して実現されます:
+-------------------+
| Name Size (4bytes)| = 7 ("NetBSD\0" + パディング)
+-------------------+
| Desc Size (4bytes)| = 4 (バージョン番号)
+-------------------+
| Type (4bytes) | = 1 (NetBSD識別タグ)
+-------------------+
| Name (8bytes) | = "NetBSD\0\0"
+-------------------+
| Description | = 599000000 (NetBSD 5.99)
+-------------------+
セクション配置戦略
コミットは以下の戦略でセクションを配置します:
- 予約領域の活用: ELFRESERVEから逆算してセクションを配置
- 動的サイズ計算: セクションサイズに応じたオフセット調整
- 境界調整: 4バイト境界への自動調整
エラーハンドリング
実装では以下のエラーハンドリングが含まれています:
if(a > ELFRESERVE)
diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE);
予約領域を超過した場合の診断メッセージ出力により、メモリ不足を防いでいます。