Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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ファイルは以下の主要構成要素を持ちます:

  1. ELFヘッダー: ファイルの基本情報
  2. プログラムヘッダーテーブル: 実行時に必要なセグメント情報
  3. セクションヘッダーテーブル: リンク時に必要なセクション情報

ノートセクションは、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 */

技術的詳細

実装アーキテクチャ

このコミットは、以下の階層構造で実装されています:

  1. 共通ELF処理層 (src/cmd/ld/elf.c, src/cmd/ld/elf.h)
  2. アーキテクチャ固有層 (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ヘッダー周辺のメモリ管理も改善されています:

  1. resoff変数の導入: 予約領域のオフセット管理
  2. 動的オフセット計算: 複数のセクションに対応した柔軟なオフセット計算
  3. 境界調整: 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)
+-------------------+

セクション配置戦略

コミットは以下の戦略でセクションを配置します:

  1. 予約領域の活用: ELFRESERVEから逆算してセクションを配置
  2. 動的サイズ計算: セクションサイズに応じたオフセット調整
  3. 境界調整: 4バイト境界への自動調整

エラーハンドリング

実装では以下のエラーハンドリングが含まれています:

if(a > ELFRESERVE)    
    diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE);

予約領域を超過した場合の診断メッセージ出力により、メモリ不足を防いでいます。

関連リンク

参考にした情報源リンク