[インデックス 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 記事)
- リロケーションについて:
- リンカの動作に関する一般的なコンピュータサイエンスの教科書やオンラインリソース。