[インデックス 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 セグメントが必ず最後に配置される必要があります。これは以下の理由によります:
- コード署名: macOSのコード署名機能では、署名データが__LINKEDIT セグメントの後に追加されるため
- 動的リンク: dyldが適切に動作するためには、__LINKEDIT セグメントが予測可能な位置にある必要があります
- セキュリティ: 署名検証プロセスが正しく機能するための制約
デバッグ情報の配置問題
変更前の実装では、macOS向けのDWARF デバッグ情報が__LINKEDIT セグメントの後に配置されていました。これにより以下の問題が発生していました:
- 配置違反: Mach-Oファイルフォーマットの仕様に違反
- デバッグ不可: GDBなどのデバッガーが正しく動作しない
- コード署名エラー: 署名検証が失敗する可能性
コアとなるコードの変更箇所
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)にアライメントする関数
ファイル配置の順序
修正後のファイル配置は以下のようになります:
- Mach-Oヘッダー
- __TEXT セグメント(実行可能コード)
- __DATA セグメント(初期化済みデータ)
- __DWARF セグメント(デバッグ情報)- 新規追加
- __LINKEDIT セグメント(動的リンク情報)
デバッグ情報の出力
segdwarf.fileoff = cpos();
dwarfemitdebugsections();
segdwarf.filelen = cpos() - segdwarf.fileoff;
この処理では:
- cpos(): 現在のファイル位置を取得
- dwarfemitdebugsections(): DWARF セクションを出力
- filelen: 出力されたデータのサイズを計算
条件分岐の修正
// switchステートメントからHdarwinのcaseを削除
case Hdarwin: // この行が削除された
case Hwindows:
これにより、macOS向けのDWARF処理が専用のブロックで実行され、既存の処理フローから分離されました。