[インデックス 14876] ファイルの概要
このコミットは、Go言語のツールチェインの一部であるcmd/5l
リンカにおける、Plan 9オペレーティングシステム向けの実行可能ファイルのヘッダが不正になる問題を修正するものです。具体的には、テキストセグメントのサイズ計算と、Plan 9バイナリにおけるpclntab
(PC-lineテーブル)の扱いに関するバグが修正されています。
コミット
commit 1a9a63961b03e386af000ed83fde5fca325c6bd1
Author: Anthony Martin <ality@pbrane.org>
Date: Sat Jan 12 03:13:55 2013 -0800
cmd/5l: fix invalid executable header on Plan 9
R=minux.ma, lucio.dere
CC=golang-dev
https://golang.org/cl/7094048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1a9a63961b03e386af000ed83fde5fca325c6bd1
元コミット内容
cmd/5l: fix invalid executable header on Plan 9
このコミットメッセージは簡潔に、cmd/5l
(ARMアーキテクチャ向けGoリンカ)がPlan 9オペレーティングシステム用の実行可能ファイルを生成する際に、不正なヘッダが出力される問題を修正したことを示しています。
変更の背景
Go言語は、その初期からPlan 9オペレーティングシステムをサポートしていました。cmd/5l
は、ARMアーキテクチャ(Goの内部では5
で表現される)向けのGoプログラムをリンクし、実行可能ファイルを生成する役割を担っています。
このコミットが行われた2013年当時、Goのツールチェインはまだ活発に開発されており、様々なプラットフォームやアーキテクチャへの対応が改善されていました。Plan 9向けのバイナリ生成において、リンカが生成する実行可能ファイルのヘッダ情報に誤りがあり、その結果、Plan 9システム上で正しくロード・実行できない問題が発生していました。
具体的には、実行可能ファイルのヘッダには、プログラムのコードセグメント(テキストセグメント)のサイズなど、OSがプログラムをメモリにロードするために必要なメタデータが含まれています。このサイズ情報が誤っていると、OSはメモリ割り当てや実行開始アドレスの特定に失敗し、プログラムの起動が不可能になります。
また、Goのバイナリには、デバッグやプロファイリング、スタックトレースの生成に不可欠なpclntab
(PC-lineテーブル)という情報が含まれています。このテーブルは、プログラムカウンタ(PC)の値とソースコードのファイル名・行番号をマッピングする役割を果たします。Plan 9向けのバイナリ生成プロセスにおいて、このpclntab
が適切に処理され、実行可能ファイルに組み込まれていなかった可能性も背景にあります。
このコミットは、これらの問題を解決し、Plan 9上でのGoプログラムの実行可能性を確保することを目的としています。
前提知識の解説
- Go言語のツールチェイン: Go言語は、コンパイラ、リンカ、アセンブラなどを含む独自のツールチェインを持っています。
cmd/5l
はそのリンカの一つで、ARMアー32ビットアーキテクチャ(GoではGOARCH=arm
、内部的には5
)向けの実行可能ファイルを生成します。 - リンカ (Linker): コンパイラによって生成されたオブジェクトファイル(機械語コード)を結合し、必要なライブラリとリンクして、最終的な実行可能ファイルを生成するプログラムです。リンカは、コード、データ、シンボル情報などを配置し、実行可能ファイルのヘッダを構築します。
- 実行可能ファイルヘッダ: 実行可能ファイルの先頭に位置するデータ構造で、オペレーティングシステムがプログラムをメモリにロードし、実行を開始するために必要な情報(例: エントリポイントのアドレス、コードセグメントとデータセグメントのサイズ、メモリレイアウト、シンボルテーブルの位置など)を含んでいます。ヘッダの形式はOSやアーキテクチャによって異なります(例: LinuxのELF、WindowsのPE、macOSのMach-O、Plan 9のa.out派生形式)。
- Plan 9: ベル研究所で開発された分散オペレーティングシステムです。ファイルシステムを中心としたユニークな設計思想を持ち、Go言語の設計にも影響を与えたと言われています。Goは初期からPlan 9をサポートしており、Goのソースコード内にはPlan 9関連のコードが多数存在します。
pclntab
(PC-line table): Goの実行可能ファイルに含まれる重要なデバッグ情報の一つです。プログラムカウンタ(PC)のアドレスと、対応するソースコードのファイル名、行番号、関数名などの情報をマッピングします。これにより、パニック発生時のスタックトレース表示や、プロファイリングツールでの関数ごとの実行時間分析などが可能になります。HEADTYPE
: Goリンカの内部で使用される変数で、ターゲットとするオペレーティングシステムとアーキテクチャに応じた実行可能ファイルの形式を識別します。例えば、Hplan9x32
は32ビットARMアーキテクチャ向けのPlan 9バイナリを、iself
はELF形式(Linuxなどで使用)を指します。segtext.filelen
とtextsize
:segtext
: 実行可能ファイルのテキスト(コード)セグメントを表す構造体または変数。segtext.filelen
: テキストセグメントがファイル内で占める実際のバイト長。textsize
: 以前使用されていた可能性のある、テキストセグメントのサイズを示す変数。このコミットでは、textsize
が正確なファイルサイズを反映していない、または冗長であると判断され、segtext.filelen
に置き換えられています。
技術的詳細
このコミットの主要な変更点は、src/cmd/5l/asm.c
ファイル内のasmb
関数に集中しています。asmb
関数は、リンカの最終段階で実行可能ファイルの各セクションをアセンブルし、ヘッダ情報やシンボルテーブルなどを書き込む役割を担っています。
-
HEADTYPE
による処理の分岐: 変更前は、ELF形式(iself
)の処理が直接記述されていましたが、変更後はswitch(HEADTYPE)
文が導入され、ターゲットOSのタイプに応じて処理が分岐するようになりました。これにより、異なるOS向けのバイナリ生成ロジックが明確に分離され、保守性が向上しています。 -
Plan 9 (Hplan9x32) 向けの
pclntab
処理の追加:case Hplan9x32:
ブロックが新設され、Plan 9向けの特別な処理が追加されました。asmplan9sym()
: Plan 9固有のシンボル情報をアセンブルする関数が呼び出されます。pclntab
の明示的な書き込み:lookup("pclntab", 0)
によってpclntab
シンボルが検索され、その内容(sym->p
)とサイズ(sym->np
、lcsize
に格納)が実行可能ファイルに直接書き込まれるようになりました。これは、Plan 9バイナリがpclntab
を正しく含むために不可欠なステップです。以前は、この情報が適切にヘッダに組み込まれていなかったか、または誤った方法で処理されていた可能性があります。
-
textsize
からsegtext.filelen
への置き換え: 複数の箇所で、テキストセグメントのサイズを示すために使用されていたグローバル変数textsize
が、segtext.filelen
に置き換えられました。- 実行可能ファイルのヘッダに書き込まれるテキストセグメントのサイズ情報 (
lputl(textsize+HEADR)
がlputl(segtext.filelen+HEADR)
に変更)。 - Plan 9向けのヘッダ情報 (
lput(textsize)
がlput(segtext.filelen)
に変更)。 - デバッグ出力 (
print("textsize=%d\\n", textsize)
がprint("textsize=%d\\n", segtext.filelen)
に変更)。 - 合計サイズ計算 (
textsize+segdata.len+symsize+lcsize
がsegtext.filelen+segdata.len+symsize+lcsize
に変更)。
この変更は、
textsize
がリンカの処理過程で常に正確なテキストセグメントの最終的なファイルサイズを反映していなかったためと考えられます。segtext.filelen
は、実際にファイルに書き込まれるテキストセグメントの長さを正確に表しており、これにより実行可能ファイルのヘッダに正しいサイズ情報が記録されるようになります。 - 実行可能ファイルのヘッダに書き込まれるテキストセグメントのサイズ情報 (
-
src/cmd/5l/l.h
からのtextsize
の削除:textsize
変数が不要になったため、ヘッダファイルsrc/cmd/5l/l.h
からその宣言が削除されました。これは、コードのクリーンアップと、冗長な変数の排除を意味します。
これらの変更により、cmd/5l
はPlan 9向けの実行可能ファイルを生成する際に、正しいヘッダ情報(特にテキストセグメントのサイズ)とpclntab
を適切に含めることができるようになり、Plan 9システム上でのGoバイナリの互換性と信頼性が向上しました。
コアとなるコードの変更箇所
src/cmd/5l/asm.c
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -546,18 +548,34 @@ asmb(void)\
break;
}
cseek(symo);
- if(iself) {
- if(debug['v'])
- Bprint(&bso, "%5.2f elfsym\n", cputime());
- asmelfsym();
+ switch(HEADTYPE) {
+ default:
+ if(iself) {
+ if(debug['v'])
+ Bprint(&bso, "%5.2f elfsym\n", cputime());
+ asmelfsym();
+ cflush();
+ cwrite(elfstrdat, elfstrsize);
+
+ if(debug['v'])
+ Bprint(&bso, "%5.2f dwarf\n", cputime());
+ dwarfemitdebugsections();
+ }
+ break;
+ case Hplan9x32:
+ asmplan9sym();
cflush();
- cwrite(elfstrdat, elfstrsize);
- if(debug['v'])
- Bprint(&bso, "%5.2f dwarf\n", cputime());
- dwarfemitdebugsections();
+ sym = lookup("pclntab", 0);
+ if(sym != nil) {
+ lcsize = sym->np;
+ for(i=0; i < lcsize; i++)
+ cput(sym->p[i]);
+
+ cflush();
+ }
+ break;
}
- cflush();
}
cursym = nil;
@@ -581,7 +599,7 @@ asmb(void)\
- 8) / 4); /* BL - entry code */
lputl(0xef000011); /* SWI - exit code */
- lputl(textsize+HEADR); /* text size */
+ lputl(segtext.filelen+HEADR); /* text size */
lputl(segdata.filelen); /* data size */
lputl(0); /* sym size */
@@ -601,7 +619,7 @@ asmb(void)\
break;
case Hplan9x32: /* plan 9 */
lput(0x647); /* magic */
- lput(textsize); /* sizes */
+ lput(segtext.filelen); /* sizes */
lput(segdata.filelen);
lput(segdata.len - segdata.filelen);
lput(symsize); /* nsyms */
@@ -626,12 +644,12 @@ asmb(void)\
}\n\
cflush();
if(debug['c']){
- print("textsize=%d\n", textsize);
+ print("textsize=%d\n", segtext.filelen);
print("datsize=%ulld\n", segdata.filelen);
print("bsssize=%ulld\n", segdata.len - segdata.filelen);
print("symsize=%d\n", symsize);
print("lcsize=%d\n", lcsize);
- print("total=%lld\n", textsize+segdata.len+symsize+lcsize);
+ print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize);
}
}
src/cmd/5l/l.h
--- a/src/cmd/5l/l.h
+++ b/src/cmd/5l/l.h
@@ -304,7 +304,6 @@ EXTERN char* rpath;
EXTERN uint32 stroffset;
EXTERN int32 symsize;
EXTERN Sym* textp;
-EXTERN int32 textsize;
EXTERN int version;
EXTERN char xcmp[C_GOK+1][C_GOK+1];
EXTERN Prog zprg;
コアとなるコードの解説
src/cmd/5l/asm.c
の変更点
-
asmb
関数内のswitch(HEADTYPE)
ブロック:- 以前は
if(iself)
という条件分岐でELF形式の処理を行っていましたが、これをswitch(HEADTYPE)
に置き換え、より汎用的な構造にしました。 default:
ケースには、既存のELF関連の処理(asmelfsym()
,dwarfemitdebugsections()
など)が移動されました。これにより、他のHEADTYPE
が追加された場合でも、既存のELF処理に影響を与えずに新しいケースを追加できるようになります。case Hplan9x32:
の追加:asmplan9sym()
: Plan 9固有のシンボルテーブルを生成する関数を呼び出します。sym = lookup("pclntab", 0);
:pclntab
という名前のシンボルをリンカのシンボルテーブルから検索します。このシンボルは、Goコンパイラが生成したPC-lineテーブルのデータを含んでいます。if(sym != nil) { ... }
:pclntab
シンボルが見つかった場合、その内容をファイルに書き込みます。lcsize = sym->np;
:sym->np
はpclntab
のバイトサイズを表します。これをlcsize
変数に格納します。for(i=0; i < lcsize; i++) cput(sym->p[i]);
:sym->p
はpclntab
のデータへのポインタであり、このループでその内容をバイト単位で出力ファイルに書き込みます。cflush();
: バッファリングされた出力をファイルにフラッシュします。 このpclntab
の明示的な書き込みは、Plan 9バイナリがデバッグ情報を正しく含むために重要です。
- 以前は
-
textsize
からsegtext.filelen
への置き換え:lputl(textsize+HEADR)
がlputl(segtext.filelen+HEADR)
に変更された箇所は、実行可能ファイルのヘッダに書き込まれるテキストセグメントのサイズを修正しています。HEADR
はヘッダの固定サイズ部分を表す定数です。この変更により、ヘッダに書き込まれるテキストセグメントのサイズが、実際にファイルに書き込まれるセグメントの長さと一致するようになります。case Hplan9x32:
内のlput(textsize)
がlput(segtext.filelen)
に変更された箇所も同様に、Plan 9固有のヘッダにおけるテキストセグメントのサイズ情報を修正しています。- デバッグ出力 (
print
文) や合計サイズ計算の箇所でも、textsize
がsegtext.filelen
に置き換えられています。これは、デバッグ情報や内部計算においても、より正確なテキストセグメントのサイズを使用するためです。
src/cmd/5l/l.h
の変更点
EXTERN int32 textsize;
の行が削除されました。これは、textsize
変数がもはや使用されなくなったため、リンカのグローバル変数リストから削除されたことを意味します。これにより、コードの冗長性が減り、保守性が向上します。
これらの変更は、GoリンカがPlan 9向けの実行可能ファイルを生成する際の正確性を大幅に向上させ、特にヘッダの整合性とデバッグ情報の完全性を確保しています。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- Goのツールチェインに関する情報: https://golang.org/cmd/
- Plan 9 from Bell Labs: https://9p.io/plan9/
- GoのGerritコードレビューシステム (CL 7094048): https://golang.org/cl/7094048 (このリンクはコミットメッセージに記載されているもので、当時のGerritのURL形式です。現在はGitHubにミラーされています。)
参考にした情報源リンク
- Go言語のソースコード (特に
cmd/5l
ディレクトリ): https://github.com/golang/go/tree/master/src/cmd/5l - Goのリンカに関する一般的な情報 (Goの内部構造に関するブログ記事やドキュメント):
- "A Quick Guide to Go's Object Files" by Russ Cox: https://go.dev/s/objfile (Goのオブジェクトファイルとリンカの基本的な概念を理解するのに役立ちます)
- "Go's Linker" by Russ Cox: https://go.dev/s/go11link (Goのリンカの進化と内部について説明しています)
- Plan 9の実行可能ファイル形式に関する情報 (a.out形式の派生):
- Plan 9
a.out
man page: https://9p.io/magic/man2html/2/a.out
- Plan 9
- ELF (Executable and Linkable Format) に関する一般的な情報:
- Dwarf Debugging Format:
- Wikipedia: https://ja.wikipedia.org/wiki/DWARF
- Goの
pclntab
に関する情報:- "Go's runtime.Func and the pclntab" by Dave Cheney: https://dave.cheney.net/2014/03/20/gos-runtime-func-and-the-pclntab
- "Go's runtime.Func and the pclntab (part 2)" by Dave Cheney: https://dave.cheney.net/2014/03/23/gos-runtime-func-and-the-pclntab-part-2
- "Go's runtime.Func and the pclntab (part 3)" by Dave Cheney: https://dave.cheney.net/2014/03/26/gos-runtime-func-and-the-pclntab-part-3
(これらの記事は
pclntab
の構造と重要性について詳しく解説しています。)