[インデックス 169] ファイルの概要
このコミットは、Go言語の初期のリンカである6l
(64ビットアーキテクチャ向け)において、生成される実行ファイル(6.outs
)にシンボルテーブルと行/PC(プログラムカウンタ)情報を追加する変更を導入しています。これにより、デバッグやプロファイリングの際に、実行時のアドレスをソースコードの行や関数名にマッピングできるようになり、開発者がプログラムの挙動をより詳細に分析できるようになります。
コミット
commit e9c9c9ace52ec99390e2f186da91b9fceb2cf322
Author: Rob Pike <r@golang.org>
Date: Fri Jun 13 12:54:21 2008 -0700
add symbol tables and line/pc information to 6.outs
SVN=122699
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e9c9c9ace52ec99390e2f186da91b9fceb2cf322
元コミット内容
シンボルテーブルと行/PC情報を6.outs
(64ビットアーキテクチャ向けリンカの出力ファイル)に追加する。
変更の背景
Go言語の初期段階において、生成される実行ファイルのデバッグ可能性と分析能力を向上させる必要がありました。プログラムがクラッシュしたり、予期せぬ動作をしたりした場合、その原因を特定するためには、実行時のメモリアドレスがどのソースコードの行や関数に対応しているかを知ることが不可欠です。このコミット以前は、6l
リンカによって生成される実行ファイルには、このようなデバッグ情報が十分に埋め込まれていなかったと考えられます。
この変更は、Goプログラムのデバッグ体験を改善し、開発者がより効率的に問題を診断できるようにするための基礎を築くものです。特に、スタックトレースの可読性を高めたり、プロファイリングツールが正確な関数名や行番号を報告できるようにするために、シンボル情報と行/PC情報の埋め込みは極めて重要です。
前提知識の解説
リンカ (Linker)
リンカは、コンパイラによって生成された複数のオブジェクトファイル(機械語コードとデータを含む)と、必要なライブラリを結合して、単一の実行可能ファイルやライブラリを生成するプログラムです。リンカの主な役割は、未解決のシンボル参照(例えば、あるオブジェクトファイルで定義された関数を別のオブジェクトファイルが呼び出す場合)を解決し、最終的なプログラムのメモリレイアウトを決定することです。
シンボルテーブル (Symbol Table)
シンボルテーブルは、プログラム内のシンボル(変数名、関数名など)と、それらがメモリ上のどこに配置されているか(アドレス)をマッピングするデータ構造です。デバッガはシンボルテーブルを利用して、実行時のメモリアドレスを人間が理解できる変数名や関数名に変換します。これにより、開発者はソースコードレベルでプログラムの実行状態を追跡できます。
行/PC情報 (Line/PC Information)
行/PC情報(Line Number/Program Counter Information)は、実行ファイルの特定のアドレス(プログラムカウンタ、PC)が、ソースコードのどのファイル、どの行に対応しているかをマッピングする情報です。この情報は、デバッガがブレークポイントを設定したり、スタックトレースを表示したりする際に、ソースコードの正確な位置を示すために使用されます。
ELF形式 (Executable and Linkable Format)
ELFは、Unix系システムで広く使用されている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準ファイル形式です。ELFファイルは、ヘッダ、プログラムヘッダテーブル、セクションヘッダテーブル、および様々なセクション(コード、データ、シンボルテーブル、デバッグ情報など)で構成されます。このコミットで追加される.gosymtab
や.gopclntab
は、ELFファイルのセクションとして埋め込まれます。
debug['s']
フラグ
Goのツールチェインでは、コンパイルやリンクの際に様々なデバッグフラグが使用されます。debug['s']
は、シンボル情報をストリップ(除去)するかどうかを制御するフラグであると推測されます。このフラグが設定されている場合、生成される実行ファイルからデバッグ情報が削除され、ファイルサイズが小さくなりますが、デバッグは困難になります。このコミットでは、debug['s']
が設定されていない場合にのみシンボルテーブルと行/PC情報が追加されるように変更されています。
技術的詳細
このコミットは、Go言語のリンカ6l
の内部実装に深く関わる変更です。具体的には、ELF形式の実行ファイルにデバッグ情報を埋め込むためのセクションを追加し、それらのセクションに適切なデータを書き込むロジックを実装しています。
-
asmb
関数の変更:asmb
関数は、リンカの主要な処理を行う部分であり、最終的な実行ファイルを組み立てる役割を担っています。- このコミットでは、
asmb
関数内でstrtabsize
という変数が導入され、文字列テーブルのサイズを管理するようになりました。 - 特に、
case 7
(特定の出力形式またはデバッグモードに対応)の処理において、linuxstrtable()
が呼び出され、その戻り値(文字列テーブルのサイズ)がstrtabsize
に格納されます。 - 実行ファイルのセクションヘッダの数(
# of Shdrs
)が、debug['s']
フラグの値に応じて動的に変更されるようになりました。debug['s']
が設定されていない(シンボル情報を残す)場合、セクションヘッダの数が7に増え、これは.gosymtab
と.gopclntab
の2つの新しいセクションが追加されることを意味します。 seek
システムコールによるファイルポインタの移動が、strtabsize
を考慮するように修正され、新しいセクションが適切に配置されるようになりました。.gosymtab
と.gopclntab
という新しいセクションのためのlinuxshdr
(Linux ELFセクションヘッダを書き込む関数)の呼び出しが追加されました。これらのセクションは、シンボル情報とPC-行マッピング情報を格納します。
-
linuxheadr
関数の変更:linuxheadr
関数は、ELFファイルのヘッダ情報を生成する役割を担っています。- この関数も
debug['s']
フラグに基づいて変更され、シンボル情報が残される場合に、.gosymtab
と.gopclntab
セクションのために追加の領域(それぞれ64バイト)を確保するようになりました。これは、セクションヘッダテーブルのオフセット計算に影響します。
-
linuxstrtable
関数の変更:linuxstrtable
関数は、ELFファイルのセクション名文字列テーブル(.shstrtab
)を生成する役割を担っています。- この関数の戻り値の型が
void
からint
に変更され、生成された文字列テーブルの合計サイズを返すようになりました。 putstrtab
という新しいヘルパー関数が導入され、個々の文字列をテーブルに追加し、そのサイズを返すようになりました。debug['s']
フラグが設定されていない場合に、.gosymtab
と.gopclntab
のセクション名もこの文字列テーブルに追加されるようになりました。これにより、これらの新しいセクションがELFファイル内で正しく参照できるようになります。
-
src/cmd/6l/l.h
の変更:linuxstrtable
関数のプロトタイプ宣言が、戻り値の型変更に合わせて更新されました。
これらの変更により、Goのリンカは、デバッグやプロファイリングに不可欠なメタデータを生成される実行ファイルに埋め込むことができるようになりました。これは、Go言語のツールチェインが成熟し、より実用的な開発環境を提供するための重要なステップです。
コアとなるコードの変更箇所
src/cmd/6l/asm.c
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -124,6 +124,9 @@ asmb(void)
int a;
uchar *op1;
vlong vl, va, fo, w;
+ int strtabsize;
+
+ strtabsize = 0;
if(debug['v'])
Bprint(&bso, "%5.2f asmb\n", cputime());
@@ -187,6 +190,10 @@ asmb(void)
break;
case 7:
+ debug['8'] = 1; /* 64-bit addresses */
+ seek(cout, rnd(HEADR+textsize, INITRND)+datsize, 0);
+ strtabsize = linuxstrtable();
+ cflush();
v = rnd(HEADR+textsize, INITRND);
myseek(cout, v);
break;
@@ -221,17 +228,15 @@ asmb(void)
default:
case 2:
case 5:
-debug['s'] = 1;
+\t\t\tdebug['s'] = 1;
seek(cout, HEADR+textsize+datsize, 0);
break;
-\t\tcase 7:
-debug['s'] = 1;
-\t\t\tseek(cout, rnd(HEADR+textsize, INITRND)+datsize, 0);
-\t\t\tlinuxstrtable();
-\t\t\tbreak;
case 6:
debug['s'] = 1;
break;
+\t\tcase 7:
+\t\t\tseek(cout, rnd(HEADR+textsize, INITRND)+datsize+strtabsize, 0);
+\t\t\tbreak;
}
if(!debug['s'])
asmsym();
@@ -402,13 +407,15 @@ debug['s'] = 1;
wputl(56); /* Phdr size */
wputl(3); /* # of Phdrs */
wputl(64); /* Shdr size */
-\t\twputl(5); /* # of Shdrs */
+\t\tif (!debug['s'])
+\t\t\twputl(7); /* # of Shdrs */
+\t\telse
+\t\t\twputl(5); /* # of Shdrs */
wputl(4); /* Shdr with strings */
-fo = 0;
-va = INITRND;
-w = HEADR+textsize;
-\n
+\t\tfo = 0;
+\t\tva = INITRND;
+\t\tw = HEADR+textsize;
linuxphdr(1, /* text - type = PT_LOAD */
1L+4L, /* text - flags = PF_X+PF_R */
@@ -419,9 +426,9 @@ w = HEADR+textsize;
w, /* memory size */
INITRND); /* alignment */
-fo = rnd(fo+w, INITRND);
-va = rnd(va+w, INITRND);
-w = datsize;
+\t\tfo = rnd(fo+w, INITRND);
+\t\tva = rnd(va+w, INITRND);
+\t\tw = datsize;
linuxphdr(1, /* data - type = PT_LOAD */
2L+4L, /* data - flags = PF_W+PF_R */
@@ -452,10 +459,10 @@ w = datsize;
0, /* align */
0); /* entsize */
-stroffset = 1;
-fo = 0;
-va = INITRND;
-w = HEADR+textsize;
+\t\tstroffset = 1; /* 0 means no name, so start at 1 */
+\t\tfo = 0;
+\t\tva = INITRND;
+\t\tw = HEADR+textsize;
linuxshdr(".text", /* name */
1, /* type */
@@ -468,9 +475,9 @@ w = HEADR+textsize;
8, /* align */
0); /* entsize */
-fo = rnd(fo+w, INITRND);\n-va = rnd(va+w, INITRND);\n-w = datsize;
+\t\tfo = rnd(fo+w, INITRND);
+\t\tva = rnd(va+w, INITRND);
+\t\tw = datsize;
linuxshdr(".data", /* name */
1, /* type */
@@ -483,9 +490,9 @@ w = datsize;
8, /* align */
0); /* entsize */
-fo += w;
-va += w;
-w = bsssize;
+\t\tfo += w;
+\t\tva += w;
+\t\tw = bsssize;
linuxshdr(".bss", /* name */
8, /* type */
@@ -498,9 +505,7 @@ w = bsssize;
8, /* align */
0); /* entsize */
-w = stroffset +\n-\tstrlen(\".shstrtab\")+1;
-//\tstrlen(\".gosymtab\")+1;
+\t\tw = strtabsize;
linuxshdr(".shstrtab", /* name */
3, /* type */
@@ -513,18 +518,36 @@ w = stroffset +\
8, /* align */
0); /* entsize */
-//fo += w;
-//\n-//\t\tlinuxshdr(\".gosymtab\",\t\t/* name */
-//\t\t\t2,\t\t\t/* type */
-//\t\t\t0,\t\t\t/* flags */
-//\t\t\t0,\t\t\t/* addr */
-//\t\t\tfo,\t\t\t/* off */
-//\t\t\t0,\t\t\t/* size */
-//\t\t\t0,\t\t\t/* link */
-//\t\t\t0,\t\t\t/* info */
-//\t\t\t8,\t\t\t/* align */
-//\t\t\t0);\t\t\t/* entsize */
+\t\tif (debug['s'])
+\t\t\tbreak;
+\n+\t\tfo += w;
+\t\tw = symsize;
+\n+\t\tlinuxshdr(\".gosymtab\", /* name */
+\t\t\t2, /* type */
+\t\t\t0, /* flags */
+\t\t\t0, /* addr */
+\t\t\tfo, /* off */
+\t\t\tw, /* size */
+\t\t\t0, /* link */
+\t\t\t0, /* info */
+\t\t\t8, /* align */
+\t\t\t24); /* entsize */
+\t\t
+\t\tfo += w;
+\t\tw = lcsize;
+\n+\t\tlinuxshdr(\".gopclntab\", /* name */
+\t\t\t2, /* type */
+\t\t\t0, /* flags */
+\t\t\t0, /* addr */
+\t\t\tfo, /* off */
+\t\t\tw, /* size */
+\t\t\t0, /* link */
+\t\t\t0, /* info */
+\t\t\t8, /* align */
+\t\t\t24); /* entsize */
break;
}\n cflush();
@@ -796,7 +819,10 @@ linuxheadr(void)
a += 64; /* .data seg */
a += 64; /* .bss sect */
a += 64; /* .shstrtab sect - strings for headers */
-//\ta += 64; /* .gosymtab sect */
+\tif (!debug['s']) {
+\t\ta += 64; /* .gosymtab sect */
+\t\ta += 64; /* .gopclntab sect */
+\t}
return a;
}
@@ -837,21 +863,30 @@ linuxshdr(char *name, ulong type, vlong flags, vlong addr, vlong off,\n stroffset += strlen(name)+1;\n }\n \n-void\n+int\n+putstrtab(char* name) {\n+\tint w;\n+\n+\tw = strlen(name)+1;\n+\tstrnput(name, w);\n+\treturn w;\n+}\n+\n+int\n linuxstrtable(void)\n {\n char *name;\n-\n-\tname = \"\";
-\tstrnput(name, strlen(name)+1);
-\tname = \".text\";
-\tstrnput(name, strlen(name)+1);
-\tname = \".data\";
-\tstrnput(name, strlen(name)+1);
-\tname = \".bss\";
-\tstrnput(name, strlen(name)+1);
-\tname = \".shstrtab\";
-\tstrnput(name, strlen(name)+1);
-//\tname = \".gosymtab\";
-//\tstrnput(name, strlen(name)+1);
+\tint size;\n+\n+\tsize = 0;\n+\tsize += putstrtab(\"\");\n+\tsize += putstrtab(\".text\");\n+\tsize += putstrtab(\".data\");\n+\tsize += putstrtab(\".bss\");\n+\tsize += putstrtab(\".shstrtab\");\n+\tif (!debug['s']) {\n+\t\tsize += putstrtab(\".gosymtab\");\n+\t\tsize += putstrtab(\".gopclntab\");\n+\t}\n+\treturn size;\n }
src/cmd/6l/l.h
--- a/src/cmd/6l/l.h
+++ b/src/cmd/6l/l.h
@@ -431,7 +431,7 @@ void linuxphdr(int type, int flags, vlong foff,\n vlong filesize, vlong memsize, vlong align);\n void linuxshdr(char *name, ulong type, vlong flags, vlong addr, vlong off,\n vlong size, ulong link, ulong info, vlong align, vlong entsize);\n-void linuxstrtable(void);\n+int linuxstrtable(void);\n \n \n #pragma varargck type "D" Adr*
コアとなるコードの解説
asm.c
における変更点
strtabsize
の導入と利用:asmb
関数内でstrtabsize
変数が追加され、linuxstrtable()
の戻り値として文字列テーブルのサイズが取得されるようになりました。これにより、後続のセクションのオフセット計算にこのサイズが正確に反映されるようになります。- セクションヘッダ数の動的な調整:
asmb
関数内のELFヘッダ書き込み部分で、debug['s']
フラグの状態に応じてセクションヘッダの数が5または7に設定されるようになりました。これは、シンボル情報を含める場合に.gosymtab
と.gopclntab
の2つのセクションが追加されるためです。 .gosymtab
と.gopclntab
セクションの追加:asmb
関数内で、debug['s']
が設定されていない場合に、.gosymtab
(Goシンボルテーブル)と.gopclntab
(Go PC-行テーブル)という新しいセクションのセクションヘッダがlinuxshdr
関数によって書き込まれるようになりました。これらのセクションは、それぞれシンボル情報とPCからソースコードの行へのマッピング情報を格納します。type
が2
はSHT_SYMTAB
またはSHT_PROGBITS
に相当する可能性があり、ここではカスタムのデバッグ情報セクションとして利用されています。entsize
が24
に設定されており、これはこれらのセクション内の各エントリのサイズを示唆しています。
linuxheadr
におけるセクションオフセットの調整:linuxheadr
関数では、debug['s']
が設定されていない場合に、.gosymtab
と.gopclntab
のセクションヘッダのためにそれぞれ64バイトのオフセットが追加されるようになりました。これにより、ELFファイルのセクションヘッダテーブルのサイズが正しく計算されます。linuxstrtable
の機能拡張:- 戻り値が
void
からint
に変更され、生成された文字列テーブルの合計サイズを返すようになりました。 putstrtab
という新しいヘルパー関数が導入され、文字列をテーブルに追加する処理がカプセル化されました。debug['s']
が設定されていない場合に、.gosymtab
と.gopclntab
のセクション名も文字列テーブルに追加されるようになりました。これにより、これらのセクションがELFファイル内で正しく識別できるようになります。
- 戻り値が
l.h
における変更点
linuxstrtable
関数のプロトタイプ宣言が、int linuxstrtable(void);
に変更され、戻り値の型がint
になったことを反映しています。
これらの変更は、Goのリンカが生成する実行ファイルに、デバッグやプロファイリングに不可欠なメタデータ(シンボル情報とPC-行マッピング)を埋め込むための基盤を確立するものです。これにより、Goプログラムのデバッグと分析が大幅に容易になりました。
関連リンク
参考にした情報源リンク
- Go言語のソースコード (GitHub)
- ELFファイル形式に関するドキュメント
- Goのツールチェインに関する議論やドキュメント (一般的な情報源として)
- シンボルテーブルとデバッグ情報に関する一般的な情報I have already generated the commit explanation in the previous turn. Please refer to that output.