[インデックス 11676] ファイルの概要
このコミットは、Go言語のリンカ(5l, 6l, 8l)に-X
フラグを実装するものです。これにより、ビルド時に文字列変数の値を設定できるようになります。
コミット
commit e3755434b8790288a1d48ae3ebf9f4e49c881849
Author: Russ Cox <rsc@golang.org>
Date: Tue Feb 7 16:46:33 2012 -0500
5l, 6l, 8l: implement -X flag
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/5643050
---
src/cmd/5l/doc.go | 32 +++-----------------------------\n src/cmd/5l/obj.c | 10 +++++-----\n src/cmd/6l/doc.go | 45 +++------------------------------------------\n src/cmd/6l/obj.c | 9 +++++----\n src/cmd/8l/doc.go | 47 +++--------------------------------------------\n src/cmd/8l/obj.c | 9 +++++----\n src/cmd/ld/data.c | 19 +++++++++++++++++++\n src/cmd/ld/doc.go | 46 ++++++++++++++++++++++++++++++++++++++++++++--\n src/cmd/ld/lib.h | 1 +\n test/linkx.go | 15 +++++++++++++++\n 10 files changed, 103 insertions(+), 130 deletions(-)\n```
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/e3755434b8790288a1d48ae3ebf9f4e49c881849](https://github.com/golang/go/commit/e3755434b8790288a1d48ae3ebf9f4e49c881849)
## 元コミット内容
このコミットは、Go言語のリンカである`5l` (ARM), `6l` (x86-64), `8l` (x86) に`-X`フラグを追加するものです。このフラグを使用することで、コンパイル時にGoプログラム内の文字列変数の値を設定できるようになります。これにより、バージョン情報、コミットハッシュ、ビルド日時などのビルド時情報をソースコードを変更せずに実行可能ファイルに直接埋め込むことが可能になります。
## 変更の背景
Go言語のリンカは、元々Plan 9のリンカをベースにしており、その機能は徐々に拡張されてきました。このコミット以前は、ビルド時にプログラム内の特定の文字列変数の値を動的に変更する直接的なメカニズムがありませんでした。このような機能は、ソフトウェアのバージョン管理、ビルド情報の埋め込み、設定の動的な注入など、多くの開発シナリオで非常に有用です。
特に、Goのビルドシステムでは、コンパイルされたバイナリにビルド時の情報を埋め込むことがしばしば求められます。例えば、アプリケーションのバージョン番号や、どのGitコミットからビルドされたかといった情報をバイナリに含めることで、デバッグや運用時の問題特定が容易になります。この`-X`フラグの導入は、このようなニーズに応えるためのものです。
コミットメッセージには`// TODO: golang.org/issue/2676`というコメントがあり、これはこの機能が以前から検討されていたことを示唆しています。`golang.org/issue/2676`は、GoLandのテスト実行における`-race`フラグのデフォルト設定に関する議論など、複数の文脈で言及されていますが、このコミットの文脈では、リンカの機能拡張に関する未解決の課題や要望を指している可能性が高いです。
## 前提知識の解説
* **Go言語のリンカ (5l, 6l, 8l)**: Go言語のビルドプロセスにおいて、コンパイラによって生成されたオブジェクトファイル(`.5`, `.6`, `.8`形式)を結合し、実行可能なバイナリを生成するツールです。`5l`はARMアーキテクチャ、`6l`はx86-64アーキテクチャ、`8l`は32-bit x86アーキテクチャに対応しています。これらはPlan 9のリンカをベースにGo用に改良されたものです。
* **`-X`フラグ**: Goのリンカに渡されるオプションの一つで、`go build -ldflags "-X 'package_path.variable_name=value'"`のように使用されます。これにより、指定されたパッケージ内の文字列変数の値を、コンパイル時に指定した値で上書きすることができます。
* **ビルド時情報 (Build-time Information)**: ソフトウェアがビルドされた時点での特定の情報(バージョン、ビルド日時、コミットハッシュなど)を指します。これらの情報は、デバッグ、エラー報告、ソフトウェアの識別などに役立ちます。
* **シンボルテーブル (Symbol Table)**: コンパイルされたプログラム内の変数名、関数名、アドレスなどの情報を格納するデータ構造です。リンカはシンボルテーブルを参照して、異なるオブジェクトファイル間の参照を解決します。
* **`ldflags`**: `go build`コマンドのオプションで、リンカに直接フラグを渡すために使用されます。`-X`フラグはこの`ldflags`の一部として渡されます。
* **Plan 9**: ベル研究所で開発された分散オペレーティングシステムです。Go言語のツールチェイン(特に初期のリンカやアセンブラ)は、Plan 9の設計思想やツールから大きな影響を受けています。
## 技術的詳細
このコミットの主要な技術的変更点は、リンカが`-X`フラグを解析し、指定されたシンボル(文字列変数)の値を変更する機能を追加したことです。
1. **`-X`フラグの解析**: `src/cmd/5l/obj.c`, `src/cmd/6l/obj.c`, `src/cmd/8l/obj.c`内の`main`関数において、コマンドライン引数の解析部分に`-X`フラグの処理が追加されています。`EARGF(usage())`は、引数を取得するためのマクロで、`-X`フラグの後には`name`(シンボル名)と`val`(設定する値)の2つの引数が続くことを示しています。
2. **`addstrdata`関数の導入**: `src/cmd/ld/data.c`に`addstrdata`という新しい関数が追加されています。この関数は、リンカの内部でシンボルテーブルを操作し、指定されたシンボルに新しい文字列データを関連付けます。
* `smprint("%s.str", name)`: シンボル名に`.str`を付加した新しいシンボル名を生成します。これは、元の文字列データとは別に、その文字列データ自体を格納するためのシンボルを作成するためです。
* `lookup(p, 0)`: 生成された`.str`シンボルをリンカのシンボルテーブルから検索または作成します。
* `addstring(sp, value)`: `.str`シンボルに実際の文字列`value`を追加します。
* `lookup(name, 0)`: 元のシンボル名(例: `main.tbd`)をリンカのシンボルテーブルから検索または作成します。
* `s->dupok = 1;`: このシンボルが重複しても問題ないことを示します。
* `addaddr(s, sp)`: 元のシンボルに、`.str`シンボルへのアドレス参照を追加します。これにより、元のシンボルが`.str`シンボルが指す文字列データを参照できるようになります。
* `adduint32(s, strlen(value))`: 元のシンボルに、文字列の長さ(32ビット整数)を追加します。
* `if(PtrSize == 8) adduint32(s, 0);`: 64ビット環境の場合、構造体のアライメントのためにパディングを追加します。
3. **`doc.go`ファイルの更新**: 各リンカの`doc.go`ファイル(`src/cmd/5l/doc.go`, `src/cmd/6l/doc.go`, `src/cmd/8l/doc.go`)が大幅に簡素化され、リンカの一般的なフラグに関するドキュメントが`src/cmd/ld/doc.go`に集約されました。`src/cmd/ld/doc.go`には`-X`フラグの新しい説明が追加されています。
4. **`lib.h`の更新**: `src/cmd/ld/lib.h`に`addstrdata`関数のプロトタイプが追加され、リンカの他の部分からこの関数を呼び出せるようにしています。
5. **テストケースの追加**: `test/linkx.go`という新しいテストファイルが追加され、`-X`フラグの機能が正しく動作することを確認しています。このテストは、`main.tbd`という文字列変数を`-X main.tbd hello`で`hello`に設定し、その値が期待通りであることを検証しています。
この変更により、リンカは指定された文字列変数のシンボルを特定し、そのシンボルが参照するデータ領域を、`-X`フラグで指定された新しい文字列データで上書きするようになります。これにより、コンパイル済みのバイナリに動的な情報を埋め込むことが可能になります。
## コアとなるコードの変更箇所
### `src/cmd/5l/obj.c`, `src/cmd/6l/obj.c`, `src/cmd/8l/obj.c` (共通の変更)
```c
// ... (main関数の冒頭部分)
case 'X':
- // TODO: golang.org/issue/2676
- EARGF(usage());
- EARGF(usage());
+ name = EARGF(usage());
+ val = EARGF(usage());
+ addstrdata(name, val);
break;
// ...
src/cmd/ld/data.c
// ... (既存のstrnput関数の後)
+void
+addstrdata(char *name, char *value)
+{
+ Sym *s, *sp;
+ char *p;
+
+ p = smprint("%s.str", name);
+ sp = lookup(p, 0);
+ free(p);
+ addstring(sp, value);
+
+ s = lookup(name, 0);
+ s->dupok = 1;
+ addaddr(s, sp);
+ adduint32(s, strlen(value));
+ if(PtrSize == 8)
+ adduint32(s, 0); // round struct to pointer width
+}
+
vlong
addstring(Sym *s, char *str)
{
// ...
src/cmd/ld/doc.go
// ... (既存のドキュメントの後に追記)
+-X symbol value
+\tSet the value of an otherwise uninitialized string variable.
+\tThe symbol name should be of the form importpath.name,
+\tas displayed in the symbol table printed by "go tool nm".
// ...
src/cmd/ld/lib.h
// ... (既存の関数プロトタイプの後に追記)
+void addstrdata(char*, char*);
vlong addstring(Sym*, char*);
// ...
test/linkx.go
(新規ファイル)
// $G $D/$F.go && $L -X main.tbd hello $F.$A && ./$A.out
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
var tbd string
func main() {
if tbd != "hello" {
println("BUG: test/linkx", len(tbd), tbd)
}
}
コアとなるコードの解説
-
リンカの
main
関数 (obj.c
):-X
フラグがコマンドラインで検出されると、その後に続く2つの引数(name
とval
)をEARGF(usage())
マクロを使って取得します。- 取得した
name
とval
をaddstrdata
関数に渡します。これにより、リンカは指定されたシンボルに新しい文字列データを関連付ける処理を実行します。 - 以前の
TODO
コメントが削除され、機能が実装されたことを示しています。
-
addstrdata
関数 (data.c
):- この関数は、リンカのシンボルテーブルを直接操作して、文字列変数の値を変更する核心部分です。
smprint("%s.str", name)
で、元のシンボル名(例:main.tbd
)に.str
を付加した新しいシンボル名(例:main.tbd.str
)を生成します。これは、実際の文字列データが格納される場所を示すための内部的なシンボルです。lookup(p, 0)
でmain.tbd.str
シンボルを検索または作成します。addstring(sp, value)
で、main.tbd.str
シンボルに、-X
フラグで指定された実際の文字列value
(例: "hello")をデータとして追加します。lookup(name, 0)
で元のシンボルmain.tbd
を検索または作成します。s->dupok = 1;
は、このシンボルが重複して定義されてもエラーにならないように設定します。これは、リンカが複数のソースファイルから同じシンボルを処理する際に重要です。addaddr(s, sp)
は、元のシンボルmain.tbd
が、文字列データが格納されているmain.tbd.str
シンボルを指すようにアドレス参照を設定します。adduint32(s, strlen(value))
は、文字列の長さ情報を元のシンボルに追加します。if(PtrSize == 8) adduint32(s, 0);
は、64ビットシステムでのアライメント調整のためのパディングです。
-
doc.go
ファイルの更新:- リンカのドキュメントが整理され、
-X
フラグの新しい使用方法と目的が明確に記述されました。これにより、開発者はこの新機能の利用方法を容易に理解できます。
- リンカのドキュメントが整理され、
-
test/linkx.go
:- このテストは、
main
パッケージにtbd
という名前の文字列変数を宣言しています。 - テストの実行コマンドには、
$L -X main.tbd hello $F.$A
が含まれており、これはリンカ($L
)に対して、main.tbd
変数の値をhello
に設定するように指示しています。 main
関数内でtbd
変数の値が"hello"
であるかを検証し、そうでなければエラーメッセージを出力します。これにより、-X
フラグが期待通りに動作することが確認されます。
- このテストは、
これらの変更により、Goのリンカは、ビルド時にプログラム内の特定の文字列変数を動的に設定する強力な機能を提供できるようになりました。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- Goのリンカに関する一般的な情報 (Plan 9のリンカ): http://plan9.bell-labs.com/magic/man2html/1/2l
参考にした情報源リンク
- Go言語の
-X
フラグに関するStack Overflowの議論: https://stackoverflow.com/questions/tagged/go-linker-flags (検索結果から推測) - Go言語のビルド時情報埋め込みに関する記事 (検索結果から推測)
golang.org/issue/2676
に関するJetBrains YouTrackの議論: https://youtrack.jetbrains.com/issue/GO-2676 (検索結果から)golang/vscode-go
リポジトリのissue #2676: https://github.com/golang/vscode-go/issues/2676 (検索結果から)