[インデックス 15838] ファイルの概要
このコミットは、Go言語のリンカである cmd/ld
におけるデバッグ情報 (DWARF) の生成方法に関する重要な変更を導入しています。特に、hostobj
モードでの DWARF 情報のリロケーション(再配置)と、macOS (OS X) の gdb
との互換性のため DWARF バージョンを 2 にダウングレードする点が主要な変更点です。
コミット
commit 40d356e9ab116fbf0f44b5b562ceb787f69fdc09
Author: Russ Cox <rsc@golang.org>
Date: Tue Mar 19 16:31:52 2013 -0400
cmd/ld: generate relocated DWARF in hostobj mode
While we're here, downgrade DWARF to version 2.
We're not using any version 3 features, and OS X gdb
only supports version 2.
Fixes #3436.
R=golang-dev, minux.ma
CC=golang-dev
https://golang.org/cl/7891044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/40d356e9ab116fbf0f44b5b562ceb787f69fdc09
元コミット内容
cmd/ld: generate relocated DWARF in hostobj mode
(cmd/ld
: hostobj
モードで再配置された DWARF を生成する)
While we're here, downgrade DWARF to version 2.
(ついでに、DWARF をバージョン 2 にダウングレードする)
We're not using any version 3 features, and OS X gdb only supports version 2.
(バージョン 3 の機能は使用しておらず、OS X の gdb
はバージョン 2 のみをサポートしているため)
Fixes #3436.
(Issue #3436 を修正)
変更の背景
このコミットの背景には、主に以下の2つの問題がありました。
hostobj
モードでの DWARF デバッグ情報の不完全性: Go のリンカ (cmd/ld
) は、Cgo を使用する際にhostobj
モードで動作することがあります。このモードでは、Go のコードとC/C++のコードが連携してコンパイル・リンクされます。しかし、このモードで生成される DWARF デバッグ情報が、外部リンカによって適切に処理されず、デバッガが正確な情報を提供できないという問題がありました。特に、アドレス参照が正しく解決されないことが原因でした。- macOS (OS X) の
gdb
との互換性問題: 当時の macOS に同梱されていたgdb
(GNU Debugger) は、DWARF デバッグ情報のバージョン 3 を完全にサポートしていませんでした。Go のリンカが DWARF バージョン 3 を出力すると、gdb
がデバッグ情報を正しく解釈できず、デバッグ体験が損なわれるという問題が発生していました。Go は DWARF バージョン 3 の特定の機能を使用していなかったため、バージョン 2 にダウングレードしても機能的な損失はないと判断されました。
これらの問題を解決し、Go プログラムのデバッグ体験を向上させることが、このコミットの目的でした。特に、Fixes #3436
は、この DWARF バージョンと gdb
の互換性に関する具体的なバグ報告を指しています。
前提知識の解説
DWARF (Debugging With Attributed Record Formats)
DWARF は、ソースコードとコンパイルされたバイナリコードとの間のマッピングを記述するための標準的なデバッグ情報フォーマットです。デバッガは DWARF 情報を使用して、実行中のプログラムの変数、関数、ソースコードの行番号などを特定し、開発者がデバッグ作業を行うのを助けます。
- DWARF の構造: DWARF 情報は、主に以下のセクションで構成されます。
.debug_info
: プログラムの構造(コンパイル単位、関数、変数、型など)を記述する主要なセクション。DIE (Debugging Information Entry) と呼ばれるツリー構造で表現されます。.debug_abbrev
: DIE の構造を定義するアブレビエーションコードを格納します。これにより、.debug_info
のサイズを削減します。.debug_line
: ソースコードの行番号と実行可能コードのアドレスとのマッピングを記述します。.debug_loc
: 変数のメモリ上の位置(ロケーション)を記述します。.debug_aranges
: アドレス範囲とコンパイル単位のマッピングを記述します。
- DWARF バージョン: DWARF には複数のバージョンが存在し、新しいバージョンでは機能拡張や改善が行われます。
- DWARF Version 2: 広くサポートされている初期の安定バージョン。
- DWARF Version 3: DWARF Version 2 の拡張版で、より多くの情報や柔軟性を提供します。しかし、古いデバッガではサポートされていない場合があります。
- DWARF Version 4, 5: さらに新しいバージョンも存在します。
Go のリンカ (cmd/ld
)
cmd/ld
は Go 言語の標準リンカです。Go のコンパイラによって生成されたオブジェクトファイル (.o
ファイル) を結合し、実行可能なバイナリファイルやライブラリを生成します。リンカは、シンボルの解決、セクションの配置、デバッグ情報の埋め込みなど、様々な重要なタスクを実行します。
Cgo
Cgo は、Go プログラムから C 言語のコードを呼び出すための Go の機能です。Cgo を使用すると、既存の C ライブラリを Go プロジェクトに統合したり、パフォーマンスが重要な部分を C で記述したりすることができます。Cgo を使用する際には、Go のリンカが C のリンカと連携して動作する必要があり、これが hostobj
モードの背景となります。
hostobj
モードと外部リンカ
Go のビルドプロセスにおいて、Cgo を使用する場合、Go のリンカ (cmd/ld
) は、最終的なバイナリの生成を外部のシステムリンカ(例: gcc
や clang
)に委ねることがあります。このモードが LinkExternal
または hostobj
モードと呼ばれます。このモードでは、Go のリンカは中間的なオブジェクトファイルを生成し、それを外部リンカに渡して最終的な実行ファイルを生成させます。このプロセスにおいて、デバッグ情報のアドレスが外部リンカによって再配置されるため、Go のリンカが生成する DWARF 情報もこの再配置に対応している必要があります。
gdb
(GNU Debugger)
gdb
は、GNU プロジェクトによって開発された強力なコマンドラインデバッガです。C, C++, Go, Fortran など、多くのプログラミング言語をサポートしています。gdb
は、プログラムの実行を制御し、ブレークポイントを設定し、変数の値を検査し、メモリをダンプするなどの機能を提供します。デバッグ情報を解釈するために DWARF などのフォーマットを使用します。
リロケーション (Relocation)
リロケーションとは、コンパイル時に決定できなかったアドレス参照を、リンク時に実際のメモリ上のアドレスに解決するプロセスです。プログラムが複数のオブジェクトファイルから構成される場合、各オブジェクトファイル内のコードやデータは、最終的な実行ファイル内のどこに配置されるかを知りません。リンカは、これらの相対的な参照を、最終的な実行ファイル内の絶対アドレスに変換します。デバッグ情報も同様に、プログラム内の特定のアドレスを参照するため、リロケーションの対象となります。
技術的詳細
このコミットは、主に src/cmd/ld/dwarf.c
、src/cmd/ld/dwarf_defs.h
、src/cmd/ld/lib.c
、src/cmd/ld/macho.c
の4つのファイルにわたる変更を含んでいます。
DWARF バージョンのダウングレード
src/cmd/ld/dwarf.c
の変更:flushunit
関数とwritelines
関数内で、DWARF のバージョンをWPUT(3)
からWPUT(2)
に変更しています。これは、生成される DWARF 情報のバージョンを 3 から 2 に明示的にダウングレードするものです。writeinfo
関数でも同様に、コンパイル単位のヘッダで DWARF バージョンを 2 に設定しています。
src/cmd/ld/lib.c
の変更:hostlink
関数内で、外部リンカ (gcc
など) に渡す引数に-ggdb
の代わりに-gdwarf-2
を追加しています。これにより、外部リンカが生成するデバッグ情報も DWARF バージョン 2 になります。
hostobj
モードでの DWARF リロケーションのサポート
src/cmd/ld/dwarf.c
の変更:putattr
関数にabbrev
引数が追加され、DW_FORM_block1
形式の属性がDW_CLS_ADDRESS
クラスを持つ場合に、リロケーション情報を追加するロジックが導入されました。linkmode == LinkExternal
の場合、addrel
を呼び出してリロケーションエントリを作成します。このリロケーションエントリは、シンボル (r->sym
)、オフセット (r->off
)、サイズ (r->siz
)、タイプ (D_ADDR
)、および加算値 (r->add
) を含みます。これにより、外部リンカがこのアドレスを適切に再配置できるようになります。
newabslocexprattr
関数が変更され、Sym *sym
引数が追加されました。これにより、DW_AT_location
属性が参照するアドレスが、単なる数値ではなく、関連するシンボルと共に扱われるようになります。これは、リロケーションが必要なアドレスを識別するために重要です。flushunit
関数とwritelines
関数で、DW_AT_high_pc
およびDW_AT_low_pc
属性にシンボル情報 (pcsym
,s
) を渡すように変更されました。これにより、これらのアドレスもリロケーションの対象としてマークされます。writeinforeloc
関数が新しく追加されました。この関数は、.debug_info
セクション内のリロケーションエントリを生成し、リンカがこれらのリロケーションを処理できるようにします。dwarfemitdebugsections
関数内でwriteinforeloc
が呼び出され、生成されたリロケーション情報のオフセットとサイズがinforeloco
とinforelocsize
に格納されます。
src/cmd/ld/dwarf_defs.h
の変更:- 新しい DWARF クラス
DW_CLS_ADDRLOC
が追加されました。これは、アドレスロケーションを表すために使用されます。
- 新しい DWARF クラス
src/cmd/ld/macho.c
の変更:dwarfaddmachoheaders
関数内で、Mach-O 形式のデバッグ情報セクション (__debug_info
) にリロケーション情報 (msect->reloc
とmsect->nreloc
) を追加するように変更されました。これにより、Mach-O リンカが DWARF 情報内のアドレス参照を正しく解決できるようになります。isobj
フラグの代わりにlinkmode == LinkExternal
を使用するように、多くの条件分岐が変更されました。これは、リンカの動作モードをより明確に反映し、hostobj
モードでの外部リンカとの連携を強化するためです。machosymtab
関数内で、C シンボルにのみ_
プレフィックスを追加するロジックが追加されました。Go シンボルはすでに.
を含むため、重複を避けるためです。
型定義の改善
src/cmd/ld/dwarf.c
の変更:walktypedef
関数が追加されました。この関数は、DW_ABRV_TYPEDECL
(typedef) を解決し、実際の型定義を返すようにします。これにより、デバッガが型エイリアスを追跡し、基になる型を正しく表示できるようになります。dotypedef
関数が追加されました。この関数は、Go の型 (KindArray
,KindFunc
,KindInterface
,KindPtr
,KindSlice
,KindStruct
) に対して DWARF のDW_ABRV_TYPEDECL
エントリを生成します。これにより、Go の複雑な型システムが DWARF でより正確に表現され、デバッガでの型の表示が改善されます。synthesizestringtypes
,synthesizeslicetypes
,synthesizemaptypes
,synthesizechantypes
関数内でwalktypedef
が使用されるようになりました。これにより、これらの合成された型も typedef を適切に解決できるようになります。
Cgo 関連のリンカモードの調整
src/cmd/ld/lib.c
の変更:loadlib
関数内で、linkmode
がLinkExternal
でなく、かつiscgo
(Cgo を使用しているか) でない場合、linkmode
をLinkInternal
に設定するロジックが追加されました。これは、Cgo を使用しない場合は内部リンカを使用することを保証します。LinkAuto
モードの場合、最終的にLinkInternal
に切り替える際に、SHOSTOBJ
タイプのシンボル(外部リンカが必要とするシンボル)を削除するロジックが追加されました。これは、内部リンカを使用する場合にこれらのシンボルが不要になるためです。internalpkg
リストにcrypto/x509
とruntime/race
が追加されました。これらのパッケージは、Cgo を使用していても内部リンカで処理できることを示しています。
これらの変更により、Go のリンカは hostobj
モードでより正確な DWARF デバッグ情報を生成できるようになり、特に macOS 上の gdb
との互換性が向上しました。
コアとなるコードの変更箇所
src/cmd/ld/dwarf.c
putattr
関数の変更:DW_FORM_block1
形式でDW_CLS_ADDRESS
クラスの属性に対してリロケーション情報を追加するロジックが追加されました。static void putattr(int abbrev, int form, int cls, vlong value, char *data) { Reloc *r; switch(form) { // ... case DW_FORM_block1: // block if(cls == DW_CLS_ADDRESS) { cput(1+PtrSize); cput(DW_OP_addr); if(linkmode == LinkExternal) { r = addrel(dsym); r->sym = (Sym*)data; r->xsym = r->sym; r->off = cpos() - infoo; r->siz = PtrSize; r->type = D_ADDR; r->add = value - r->sym->value; r->xadd = r->add; value = r->add; } addrput(value); break; } // ... } // ... }
newabslocexprattr
関数の変更: シンボル情報を引数として受け取るようになりました。static void newabslocexprattr(DWDie *die, vlong addr, Sym *sym) { newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, (char*)sym); }
flushunit
関数の変更:high_pc
にシンボル情報を渡すようになりました。static void flushunit(DWDie *dwinfo, vlong pc, Sym *pcsym, vlong unitstart, int32 header_length) { vlong here; if (dwinfo != nil && pc != 0) { newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, (char*)pcsym); } // ... WPUT(2); // dwarf version (changed from 3) // ... }
writelines
関数の変更:low_pc
とhigh_pc
にシンボル情報を渡すようになりました。また、DWARF バージョンを 2 に設定しています。static void writelines(void) { // ... // In new compilation unit block newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->text->pc, (char*)s); WPUT(2); // dwarf version (changed from 3) // ... // In function block newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, (char*)s); newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, (char*)s); // ... }
writeinforeloc
関数の追加: DWARF 情報のリロケーションエントリを書き込みます。static vlong writeinforeloc(void) { int i; vlong start; Reloc *r; start = cpos(); for(r = dsym->r; r < dsym->r+dsym->nr; r++) { if(iself) i = elfreloc1(r, r->off); else if(HEADTYPE == Hdarwin) i = machoreloc1(r, r->off); else i = -1; if(i < 0) diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); } return start; }
dwarfemitdebugsections
関数の変更:writeinforeloc
を呼び出すようになりました。void dwarfemitdebugsections(void) { // ... inforeloco = writeinforeloc(); inforelocsize = cpos() - inforeloco; align(inforelocsize); }
walktypedef
およびdotypedef
関数の追加: 型エイリアスを解決し、typedef エントリを生成します。
src/cmd/ld/dwarf_defs.h
DW_CLS_ADDRLOC
の追加:enum { // ... DW_CLS_FLAG, DW_CLS_PTR, // lineptr, loclistptr, macptr, rangelistptr DW_CLS_REFERENCE, DW_CLS_ADDRLOC, // New class DW_CLS_STRING };
src/cmd/ld/lib.c
hostlink
関数の変更: 外部リンカに-gdwarf-2
を渡すようになりました。void hostlink(void) { // ... if(!debug['s']) argv[argc++] = "-gdwarf-2"; // Changed from "-ggdb" // ... }
loadlib
関数の変更:linkmode
の調整とSHOSTOBJ
シンボルの削除ロジック。void loadlib(void) { // ... if(linkmode == LinkExternal && !iscgo) linkmode = LinkInternal; if(linkmode == LinkAuto) { linkmode = LinkInternal; // Drop all the cgo_import_static declarations. // Turns out we won't be needing them. for(s = allsym; s != S; s = s->allsym) if(s->type == SHOSTOBJ) s->type = 0; } // ... }
internalpkg
の更新:const char *internalpkg[] = { "crypto/x509", // Added "net", "os/user", "runtime/cgo", "runtime/race" // Added };
src/cmd/ld/macho.c
dwarfaddmachoheaders
関数の変更: Mach-O の DWARF セクションにリロケーション情報を追加します。void dwarfaddmachoheaders(void) { // ... msect = newMachoSect(ms, "__debug_info", "__DWARF"); msect->off = infoo; msect->size = infosize; msect->reloc = inforeloco; // Added msect->nreloc = inforelocsize / 8; // Added ms->filesize += msect->size; // ... }
isobj
からlinkmode == LinkExternal
への変更: 多くの場所で条件分岐が変更されました。// Example from machowrite if(linkmode == LinkExternal) // Changed from isobj LPUT(1); /* file type - mach object */ else LPUT(2); /* file type - mach executable */
machosymtab
関数の変更: C シンボルにのみ_
プレフィックスを追加。void machosymtab(void) { // ... // Only add _ to C symbols. Go symbols have dot in the name. if(strstr(s->extname, ".") == nil) adduint8(symstr, '_'); addstring(symstr, s->extname); // ... }
コアとなるコードの解説
このコミットの核となる変更は、Go のリンカが DWARF デバッグ情報を生成する際に、外部リンカによるアドレスのリロケーションを考慮に入れるようになった点と、macOS の gdb
との互換性を確保するために DWARF バージョンをダウングレードした点です。
-
DWARF リロケーションのサポート:
- 以前は、
hostobj
モードで生成された DWARF 情報内のアドレス参照は、外部リンカによって再配置されることを想定していませんでした。そのため、デバッガがこれらのアドレスを正しく解決できず、デバッグが困難になることがありました。 - このコミットでは、
putattr
関数内でDW_CLS_ADDRESS
クラスの属性(特にDW_AT_location
など、アドレスを指す属性)に対して、明示的にリロケーションエントリ (Reloc
構造体) を生成するようになりました。これにより、Go のリンカは、これらのアドレスが外部リンカによって再配置される必要があることを示します。 writeinforeloc
関数は、これらのリロケーションエントリを収集し、Mach-O などのオブジェクトファイル形式に埋め込むための処理を行います。これにより、外部リンカは DWARF 情報内のアドレスを、最終的な実行ファイル内の正しいアドレスに調整できます。newabslocexprattr
やflushunit
、writelines
でシンボル情報 (Sym *sym
) をアドレスと共に渡すようになったのは、どのアドレスがどのシンボルに関連しているかをリンカが把握し、正確なリロケーションエントリを生成するために不可欠です。
- 以前は、
-
DWARF バージョンのダウングレード:
src/cmd/ld/dwarf.c
内のWPUT(3)
をWPUT(2)
に変更することで、生成される DWARF のバージョンが 3 から 2 に明示的に設定されます。src/cmd/ld/lib.c
で外部リンカに渡すフラグを-ggdb
から-gdwarf-2
に変更したことも、このダウングレードを補完します。- この変更は、Go が DWARF バージョン 3 の特定の機能を使用していなかったことと、当時の macOS の
gdb
がバージョン 2 のみを完全にサポートしていたという実用的な理由に基づいています。これにより、macOS ユーザーのデバッグ体験が向上しました。
-
型定義の改善:
walktypedef
とdotypedef
の導入は、Go の複雑な型システム(特に構造体、配列、関数、インターフェースなど)が DWARF でより正確に表現されるようにするためのものです。これにより、デバッガが Go の型エイリアスや複合型を正しく解釈し、ユーザーに分かりやすい形で表示できるようになります。
-
リンカモードの調整:
isobj
フラグからlinkmode == LinkExternal
への移行は、リンカの動作モードをより明確に表現し、外部リンカとの連携ロジックを整理するためのものです。internalpkg
の更新やSHOSTOBJ
シンボルの削除ロジックは、Cgo を使用しない場合に不必要な外部リンカへの依存を排除し、ビルドプロセスを最適化するためのものです。
これらの変更は、Go のデバッグツールチェーンの堅牢性と互換性を大幅に向上させ、特に Cgo を使用するプロジェクトや macOS 環境での開発において、よりスムーズなデバッグ体験を提供することに貢献しました。
関連リンク
- Go Issue #3436: https://github.com/golang/go/issues/3436
- このコミットが修正した具体的なバグ報告。DWARF バージョンと
gdb
の互換性に関する議論が含まれています。
- このコミットが修正した具体的なバグ報告。DWARF バージョンと
- Go CL 7891044: https://golang.org/cl/7891044
- このコミットに対応する Go のコードレビューシステム (Gerrit) のチェンジリスト。詳細な議論や変更履歴が含まれています。
参考にした情報源リンク
- DWARF Debugging Information Format:
- https://dwarfstd.org/ (DWARF の公式ウェブサイト)
- https://en.wikipedia.org/wiki/DWARF (Wikipedia の DWARF 記事)
- Go のリンカ (
cmd/ld
) について:- Go のソースコード (
src/cmd/ld
ディレクトリ) - Go のビルドプロセスに関するドキュメントやブログ記事 (例: "Go's linker" で検索)
- Go のソースコード (
- Cgo について:
- https://go.dev/blog/cgo (Go 公式ブログの Cgo 記事)
- GNU Debugger (GDB) について:
- https://www.gnu.org/software/gdb/ (GDB 公式ウェブサイト)
- Mach-O ファイル形式について:
- Apple Developer Documentation (Mach-O Executable and Object File Format)
- https://en.wikipedia.org/wiki/Mach-O (Wikipedia の Mach-O 記事)
- リロケーションについて:
- リンカの動作に関する一般的なコンピュータサイエンスの教科書やオンラインリソース。