Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10224] ファイルの概要

このコミットは、Go言語のコンパイラ(gc)とリンカ(ld)におけるpathtoprefix関数の同期とコメントの追加を目的としています。pathtoprefix関数は、ファイルパスをシンボルテーブルで使用されるプレフィックス形式に変換する役割を担っています。この変更により、両ツール間でのパス変換ロジックの一貫性が確保され、特にパス内のドット(.)の扱いと非ASCII文字のエスケープ処理が改善されました。

コミット

  • コミットハッシュ: 80bce97e45d99e7784bfa1d7d3563126d8c233e8
  • Author: Russ Cox rsc@golang.org
  • Date: Thu Nov 3 12:44:51 2011 -0400

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/80bce97e45d99e7784bfa1d7d3563126d8c233e8

元コミット内容

    gc, ld: sync pathtoprefix + add comments
    
    R=lvd, lvd
    CC=golang-dev
    https://golang.org/cl/5332051

変更の背景

Go言語のツールチェインにおいて、コンパイラ(gc)とリンカ(ld)は、ソースファイルパスやパッケージパスを内部的なシンボル名に変換する際に、pathtoprefixという共通のロジックを使用していました。しかし、この関数がsrc/cmd/gc/subr.csrc/cmd/ld/lib.cの2箇所にそれぞれ実装されており、その実装に差異が生じていました。

この差異は、特にファイルパスに含まれる特殊文字(例: ドット.)や非ASCII文字の処理において、コンパイラとリンカの間で不整合を引き起こす可能性がありました。シンボル名の一貫性は、ビルドプロセスの正確性と安定性にとって極めて重要です。

このコミットの主な目的は以下の通りです。

  1. pathtoprefix関数の同期: gcldの両方でpathtoprefix関数の実装を完全に一致させることで、パス変換ロジックの一貫性を保証します。
  2. コメントの追加: コードの可読性と保守性を向上させるため、pathtoprefix関数の動作原理、特にエスケープルールに関する詳細なコメントを追加します。また、両実装が同期されていることを明示するコメントも追加されています。
  3. エスケープロジックの改善: パス内のドットの扱いをより適切にし、非ASCII文字もエスケープ対象に含めることで、より堅牢なシンボル名生成を実現します。

前提知識の解説

Go言語のツールチェイン (gc, ld)

  • gc (Go Compiler): Go言語のソースコードをコンパイルし、オブジェクトファイル(.oファイル)を生成するコンパイラです。このオブジェクトファイルには、コンパイルされたコードと、そのコード内で定義された関数や変数などのシンボル情報が含まれます。
  • ld (Go Linker): gcによって生成された複数のオブジェクトファイルやライブラリを結合し、実行可能なバイナリファイルを生成するリンカです。リンカは、異なるオブジェクトファイル間で参照されるシンボルを解決し、最終的なプログラムを構築します。

シンボルテーブルとシンボル名

コンパイラやリンカは、プログラム内の関数、変数、型などの要素を「シンボル」として扱います。これらのシンボルは、シンボルテーブルに格納され、その名前(シンボル名)によって識別されます。シンボル名は、プログラムの異なる部分が互いに参照し合うための重要な識別子となります。

ファイルパスやパッケージパスがシンボル名の一部として使用される場合、それらのパスに含まれる特殊文字(例: /, ., %, "など)は、シンボル名として有効な文字セットに変換(エスケープ)される必要があります。これは、シンボル名がファイルシステムパスとは異なる命名規則を持つためです。

pathtoprefix関数

pathtoprefix関数は、Goのコンパイラとリンカの内部で、ファイルパスやパッケージパスをシンボルテーブルで使用可能な形式のプレフィックス文字列に変換するために使用されるユーティリティ関数です。この関数は、パス内の特定の文字をエスケープすることで、シンボル名としての有効性を確保します。エスケープは通常、%xx形式(xxは文字の16進数表現)で行われます。

技術的詳細

このコミットにおけるpathtoprefix関数の変更は、主に以下の2点に集約されます。

  1. パスの最終セグメントにおけるドット(.)の扱い:

    • 以前の実装では、パス内のすべてのドットがエスケープ対象でした。
    • 新しい実装では、パスの「最終セグメント」(最後のスラッシュ/以降の部分、通常はファイル名)にあるドットのみがエスケープされるようになりました。これは、ファイル名にドットが含まれる場合(例: main.go)に、そのドットを不必要にエスケープしないようにするためです。これにより、生成されるシンボル名がより読みやすく、元のパス構造を反映したものになります。
    • この変更を実現するために、lという新しい変数が導入され、パスの最後のスラッシュ以降の開始位置を特定するロジックが追加されました。
  2. 非ASCII文字のエスケープ:

    • 以前の実装では、制御文字(<= ' ')、ドット(.)、パーセント(%)、二重引用符(")がエスケープ対象でした。
    • 新しい実装では、これらに加えて、ASCII値が0x7f(127)以上の文字(*r >= 0x7f)もエスケープ対象となりました。これは、非ASCII文字(例: 拡張ASCII文字、UTF-8のマルチバイト文字など)がシンボル名として不正な文字として扱われることを防ぐためです。これにより、国際化されたファイルパスなど、より多様なパス名に対応できるようになります。

これらの変更は、src/cmd/gc/subr.csrc/cmd/ld/lib.cの両方のファイルでpathtoprefix関数に適用され、両者の実装が完全に同期されました。

コアとなるコードの変更箇所

src/cmd/gc/subr.c

--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -2916,6 +2916,8 @@ ngotype(Node *n)
  * non-7-bit clean bytes turn into %xx.  The period needs escaping
  * only in the last segment of the path, and it makes for happier
  * users if we escape that as little as possible.
+ *
+ * If you edit this, edit ../ld/lib.c:/^pathtoprefix copy too.
  */
 static char*
 pathtoprefix(char *s)

src/cmd/ld/lib.c

--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -903,18 +903,26 @@ unmal(void *v, uint32 n)
  * Convert raw string to the prefix that will be used in the symbol table.
  * Invalid bytes turn into %xx.	 Right now the only bytes that need
  * escaping are %, ., and ", but we escape all control characters too.
+ *
+ * Must be same as ../gc/subr.c:/^pathtoprefix.
  */
 static char*
 pathtoprefix(char *s)
 {
 	static char hex[] = "0123456789abcdef";
-\tchar *p, *r, *w;\n+\tchar *p, *r, *w, *l;
 	int n;
 
+\t// find first character past the last slash, if any.
+\tl = s;
+\tfor(r=s; *r; r++)
+\t\tif(*r == '/')
+\t\t\tl = r+1;
+\
 	// check for chars that need escaping
 	n = 0;
 	for(r=s; *r; r++)
-\t\tif(*r <= ' ' || *r == '.' || *r == '%' || *r == '"')
+\t\tif(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f)
 	\t\t\tn++;
 
 	// quick exit
@@ -924,7 +932,7 @@ pathtoprefix(char *s)
 	// escape
 	p = mal((r-s)+1+2*n);
 	for(r=s, w=p; *r; r++) {
-\t\tif(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') {\n+\t\tif(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) {
 	\t\t\t*w++ = '%';
 	\t\t\t*w++ = hex[(*r>>4)&0xF];
 	\t\t\t*w++ = hex[*r&0xF];

コアとなるコードの解説

src/cmd/gc/subr.c の変更点

  • pathtoprefix関数のコメントが更新されました。特に、「The period needs escaping only in the last segment of the path」という記述が追加され、ドットのエスケープルールが明確化されました。
  • 最も重要なのは、「If you edit this, edit ../ld/lib.c:/^pathtoprefix copy too.」というコメントが追加されたことです。これは、gcldの両方にpathtoprefixのコピーが存在し、一方を変更する際にはもう一方も同様に変更する必要があることを開発者に強く促しています。これにより、将来的な不整合の発生を防ぐ狙いがあります。

src/cmd/ld/lib.c の変更点

  1. 変数 l の追加:

    char *p, *r, *w, *l;
    

    lは、パスの最後のスラッシュ(/)以降の最初の文字へのポインタを保持するために導入されました。これにより、ファイル名の部分(パスの最終セグメント)を特定できます。

  2. 最終セグメントの開始位置の特定:

    // find first character past the last slash, if any.
    l = s;
    for(r=s; *r; r++)
    	if(*r == '/')
    		l = r+1;
    

    このループは、入力文字列sを走査し、最後のスラッシュを見つけます。lは、そのスラッシュの次の文字(つまり、最終セグメントの開始)を指すように更新されます。スラッシュがない場合(単一のファイル名の場合など)、lsのままとなり、文字列全体が最終セグメントと見なされます。

  3. エスケープ条件の変更:

    // check for chars that need escaping
    n = 0;
    for(r=s; *r; r++)
    	if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f)
    		n++;
    
    // escape
    p = mal((r-s)+1+2*n);
    for(r=s, w=p; *r; r++) {
    	if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) {
    		*w++ = '%';
    		*w++ = hex[(*r>>4)&0xF];
    		*w++ = hex[*r&0xF];
    	} else {
    		*w++ = *r;
    	}
    }
    

    エスケープが必要な文字をチェックする条件が変更されました。

    • (*r == '.' && r >= l): この条件は、「現在の文字がドットであり、かつそのドットがパスの最終セグメント(l以降)にある場合」にのみ真となります。これにより、ディレクトリ名に含まれるドット(例: my.dir/file)はエスケープされず、ファイル名に含まれるドット(例: file.txt)のみがエスケープ対象となります。
    • *r >= 0x7f: この条件は、「現在の文字のASCII値が127以上の場合」に真となります。これにより、非ASCII文字(例: é, ü, 日本語文字など)もエスケープ対象となり、シンボル名としての互換性が向上します。

これらの変更により、pathtoprefix関数はより洗練され、Goのビルドシステムにおけるシンボル名の生成がより正確かつ堅牢になりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Goのコンパイラとリンカの内部動作に関する一般的な情報)
  • Go言語のソースコード (特にsrc/cmd/gcsrc/cmd/ldディレクトリ)
  • ASCII文字コード表 (0x7fの理解のため)
  • シンボルテーブルとリンカに関する一般的なコンピュータサイエンスの知識
  • https://github.com/golang/go/commit/80bce97e45d99e7784bfa1d7d3563126d8c233e8 (GitHub上のコミットページ)
  • https://golang.org/cl/5332051 (Goのコードレビューシステム)The user's request has been fully addressed by providing a detailed technical explanation of the commit in Markdown format, following all specified sections and requirements. I have extracted all necessary information from the provided commit data and structured the response accordingly.I have provided a comprehensive explanation of the commit, including its background, technical details, and code changes, all formatted in Markdown as requested.

Is there anything else you would like me to do with this commit or any other task?