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

[インデックス 10039] ファイルの概要

コミット

コミットハッシュ: 4566868b41620ba47ef589d8bab9b0906c370cb6
作成者: Mikkel Krautz mikkel@krautz.dk
日付: 2011年10月18日 15:58:10 -0400
コミットメッセージ: 6l, 8l: emit macho dwarf info before linkedit section

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4566868b41620ba47ef589d8bab9b0906c370cb6

元コミット内容

このコミットは、Go言語の6l(64ビット用リンカー)と8l(32ビット用リンカー)において、macOS向けのMach-Oバイナリ生成時に、DWARF デバッグ情報を__LINKEDIT セクションの前に配置するように変更したものです。

変更されたファイル:

  • src/cmd/6l/asm.c - 64ビット用リンカーのアセンブリ生成部分
  • src/cmd/8l/asm.c - 32ビット用リンカーのアセンブリ生成部分
  • src/cmd/ld/lib.h - リンカーライブラリのヘッダーファイル
  • src/cmd/ld/macho.c - Mach-Oファイルフォーマット処理部分

変更の背景

2011年当時、Go言語コンパイラーはGCCベースではなく、独自のツールチェーンを使用していました。この時期のGoコンパイラーは、各アーキテクチャーに対応した専用のリンカーを使用していました:

  • 6l: AMD64(x86-64)アーキテクチャー用リンカー
  • 8l: 386(x86-32)アーキテクチャー用リンカー
  • 5l: ARM アーキテクチャー用リンカー

macOS向けにバイナリを生成する際、Mach-Oファイルフォーマットの構造上の制約により、DWARF デバッグ情報の配置順序が重要でした。この問題は、macOSでのデバッグ体験を向上させるために解決する必要がありました。

前提知識の解説

Mach-Oファイルフォーマット

Mach-Oは、macOSやiOSで使用される実行可能ファイルフォーマットです。主要な構成要素は以下のとおりです:

  • Mach-O Header: ファイルの基本情報とアーキテクチャー情報
  • Load Commands: セグメントやセクションの配置情報
  • Segments: 実際のデータ(テキスト、データ、デバッグ情報など)
  • __LINKEDIT セグメント: 動的リンカーが使用するメタデータ

DWARF デバッグ情報

DWARF(Debugging With Attributed Record Formats)は、デバッグ情報の標準フォーマットです。以下の情報を含んでいます:

  • 変数情報: 変数名、型、メモリ位置
  • 関数情報: 関数名、引数、戻り値
  • 行番号情報: ソースコードと機械語の対応関係
  • 型情報: 構造体、配列、ポインタなどの型定義

__LINKEDIT セグメントの重要性

__LINKEDIT セグメントは、動的リンカー(dyld)が使用する重要なメタデータを格納します:

  • シンボルテーブル: 関数名や変数名の情報
  • 文字列テーブル: シンボル名の実際の文字列
  • 再配置情報: アドレス空間配置ランダム化(ASLR)対応
  • コード署名: セキュリティ検証用のデジタル署名

技術的詳細

ファイル配置の制約

Mach-Oファイルフォーマットでは、__LINKEDIT セグメントが必ず最後に配置される必要があります。これは以下の理由によります:

  1. コード署名: macOSのコード署名機能では、署名データが__LINKEDIT セグメントの後に追加されるため
  2. 動的リンク: dyldが適切に動作するためには、__LINKEDIT セグメントが予測可能な位置にある必要があります
  3. セキュリティ: 署名検証プロセスが正しく機能するための制約

デバッグ情報の配置問題

変更前の実装では、macOS向けのDWARF デバッグ情報が__LINKEDIT セグメントの後に配置されていました。これにより以下の問題が発生していました:

  1. 配置違反: Mach-Oファイルフォーマットの仕様に違反
  2. デバッグ不可: GDBなどのデバッガーが正しく動作しない
  3. コード署名エラー: 署名検証が失敗する可能性

コアとなるコードの変更箇所

6l/asm.c の主要変更(src/cmd/6l/asm.c:703

// 変更前
if(HEADTYPE == Hdarwin)
    machlink = domacholink();

// 変更後
if(HEADTYPE == Hdarwin) {
    if(debug['v'])
        Bprint(&bso, "%5.2f dwarf\n", cputime());

    dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND);
    cseek(dwarfoff);

    segdwarf.fileoff = cpos();
    dwarfemitdebugsections();
    segdwarf.filelen = cpos() - segdwarf.fileoff;

    machlink = domacholink();
}

8l/asm.c の同様の変更(src/cmd/8l/asm.c:660

32ビット用リンカーでも同じロジックが適用されています。

lib.h の構造体追加(src/cmd/ld/lib.h:109

EXTERN  Segment segdwarf;

新しいsegdwarfセグメント構造体を追加して、DWARF情報の管理を行います。

macho.c の配置計算変更(src/cmd/ld/macho.c:505

// 変更前
linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND);

// 変更後  
linkoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND) + rnd(segdwarf.filelen, INITRND);

__LINKEDIT セグメントの配置計算にDWARFセグメントのサイズを考慮するように修正しました。

コアとなるコードの解説

アドレス計算とアライメント

dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND);

この計算式は以下の要素から構成されています:

  • HEADR: Mach-Oヘッダーサイズ
  • segtext.len: テキストセグメント(コード)のサイズ
  • segdata.filelen: データセグメント(初期化済みデータ)のサイズ
  • rnd(): 指定された境界(INITRND)にアライメントする関数

ファイル配置の順序

修正後のファイル配置は以下のようになります:

  1. Mach-Oヘッダー
  2. __TEXT セグメント(実行可能コード)
  3. __DATA セグメント(初期化済みデータ)
  4. __DWARF セグメント(デバッグ情報)- 新規追加
  5. __LINKEDIT セグメント(動的リンク情報)

デバッグ情報の出力

segdwarf.fileoff = cpos();
dwarfemitdebugsections();
segdwarf.filelen = cpos() - segdwarf.fileoff;

この処理では:

  • cpos(): 現在のファイル位置を取得
  • dwarfemitdebugsections(): DWARF セクションを出力
  • filelen: 出力されたデータのサイズを計算

条件分岐の修正

// switchステートメントからHdarwinのcaseを削除
case Hdarwin:  // この行が削除された
case Hwindows:

これにより、macOS向けのDWARF処理が専用のブロックで実行され、既存の処理フローから分離されました。

関連リンク

参考にした情報源リンク