[インデックス 18919] ファイルの概要
このコミットは、Goリンカー (cmd/ld
) におけるPlan 9環境でのコンパイル時の警告を修正するものです。具体的には、src/cmd/ld/macho.c
と src/cmd/ld/symtab.c
の2つのファイルが変更されています。これらのファイルは、それぞれMach-O実行可能ファイル形式とシンボルテーブルの処理に関連しています。
コミット
- Author: David du Colombier 0intro@gmail.com
- Date: Fri Mar 21 19:26:47 2014 +0100
- Commit Hash: dc008af94df00e90be9a22bb7e321f9677f12639
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dc008af94df00e90be9a22bb7e321f9677f12639
元コミット内容
cmd/ld: fix warnings on Plan 9
warning: src/cmd/ld/macho.c:595 sign-extended character constant
warning: src/cmd/ld/macho.c:595 sign-extended character constant
warning: src/cmd/ld/symtab.c:63 sign-extended character constant
warning: src/cmd/ld/symtab.c:63 sign-extended character constant
LGTM=iant
R=golang-codereviews, iant
CC=golang-codereviews
https://golang.org/cl/76580046
変更の背景
このコミットは、Goのリンカー (cmd/ld
) をPlan 9オペレーティングシステム上でコンパイルする際に発生していた「sign-extended character constant」という警告を修正するために行われました。
この警告は、C言語のコンパイラが、文字定数(特に\xc2
や\xb7
のような127を超えるASCII値を持つ文字)を符号付き整数型に変換する際に発生します。C言語では、char
型がデフォルトでsigned char
として扱われるかunsigned char
として扱われるかは、コンパイラやアーキテクチャに依存します。もしchar
がsigned char
として扱われる環境で、127を超える値を持つ文字定数がint
型に昇格されると、その最上位ビットが符号ビットとして解釈され、値が負の数として符号拡張されてしまうことがあります。これは意図しない動作を引き起こす可能性があり、コンパイラが警告を発します。
Plan 9環境でのコンパイルにおいて、この挙動が問題となり警告が発生していたため、Goのリンカーのビルドプロセスをクリーンに保つために修正が必要とされました。
前提知識の解説
Plan 9
Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの後継として設計され、ネットワーク透過性、リソースのファイルシステムとしての表現、UTF-8の採用など、多くの革新的な概念を導入しました。Go言語の設計者の一部はPlan 9の開発にも携わっており、Go言語にはPlan 9の思想が色濃く反映されています。GoのツールチェインがPlan 9をサポートしているのは、このような歴史的背景があるためです。
C言語におけるchar
型と符号拡張
C言語において、char
型は通常1バイトの整数型として扱われます。しかし、その値が符号付き(signed char
)として解釈されるか、符号なし(unsigned char
)として解釈されるかは、C標準では実装定義(implementation-defined)とされています。多くのシステムではsigned char
がデフォルトですが、一部のシステム(特に組み込みシステムなど)ではunsigned char
がデフォルトの場合もあります。
「符号拡張(sign extension)」とは、より小さい整数型からより大きい整数型に値を変換する際に、元の型の最上位ビット(符号ビット)の値を、新しい型の残りの上位ビットにコピーするプロセスです。例えば、signed char
で-1
(バイナリで11111111
)をint
に変換すると、int
のビット幅に合わせて11111111111111111111111111111111
(-1
)となります。
「sign-extended character constant」警告
この警告は、文字定数がint
型に昇格される際に、その文字のASCII値が127を超えている(つまり、最上位ビットが1である)場合に発生します。もしchar
型が符号付きとして扱われる環境であれば、この文字定数は負の値として符号拡張されてしまいます。
例: char
がsigned char
の場合
'\xc2'
(194) は、int
に昇格されると-62
として解釈される可能性があります。'\xb7'
(183) は、int
に昇格されると-73
として解釈される可能性があります。
これは、開発者が文字を単なるバイト値として扱いたい場合に、意図しない負の値として解釈されることを防ぐためにコンパイラが発する警告です。
技術的詳細
このコミットの技術的な解決策は、非常にシンプルかつ効果的です。警告が発生していた箇所で、文字定数を比較する前に明示的にunsigned char
型にキャストしています。
元のコードでは、*p
や*q
といったchar
型のポインタが指す値を直接文字定数と比較していました。
// 修正前
if(*p == '\xc2' && *(p+1) == '\xb7') {
// ...
if(*q == '\xc2' && *(q+1) == '\xb7') {
ここで、'\xc2'
や'\xb7'
といった文字定数は、C言語の規則によりint
型として扱われます。もしコンパイラがchar
をsigned char
として扱う場合、*p
や*q
の値が127を超えると負の値として解釈され、また文字定数も符号拡張されて負の値になります。この比較が意図通りに機能しない可能性や、コンパイラが警告を発する原因となっていました。
修正後のコードでは、比較対象の*p
や*q
をunsigned char
型にキャストしています。
// 修正後
if((uchar)*p == 0xc2 && (uchar)*(p+1) == 0xb7) {
// ...
if((uchar)*q == 0xc2 && (uchar)*(q+1) == 0xb7) {
ここで、uchar
は通常unsigned char
のtypedefです。*p
や*q
をunsigned char
にキャストすることで、その値は常に0から255の範囲の符号なし整数として扱われます。これにより、符号拡張の問題が回避され、0xc2
や0xb7
といった16進数で表現されたバイト値との比較が意図通りに行われるようになります。
この修正は、コードのセマンティクスを変えることなく、コンパイラの警告を解消し、異なる環境(特にPlan 9のようなchar
の符号が異なる可能性のある環境)での移植性を向上させます。
コアとなるコードの変更箇所
diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c
index 0f9b0d2d2d..61306bb7ca 100644
--- a/src/cmd/ld/macho.c
+++ b/src/cmd/ld/macho.c
@@ -592,7 +592,7 @@ machosymtab(void)
} else {
p = s->extname;
while (*p++ != '\0') {
- if(*p == '\xc2' && *(p+1) == '\xb7') {
+ if((uchar)*p == 0xc2 && (uchar)*(p+1) == 0xb7) {
adduint8(ctxt, symstr, '.');
p++;
} else {
diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c
index 22e5bb5d95..c87d0f089c 100644
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -60,7 +60,7 @@ putelfstr(char *s)
if(p != nil) {
p = q = elfstrdat+off;
while (*q != '\0') {
- if(*q == '\xc2' && *(q+1) == '\xb7') {
+ if((uchar)*q == 0xc2 && (uchar)*(q+1) == 0xb7) {
q += 2;
*p++ = '.';
elfstrsize--;
コアとなるコードの解説
変更はsrc/cmd/ld/macho.c
のmachosymtab
関数内と、src/cmd/ld/symtab.c
のputelfstr
関数内で行われています。
src/cmd/ld/macho.c
(Mach-Oシンボルテーブル処理)
// 修正前
if(*p == '\xc2' && *(p+1) == '\xb7') {
// 修正後
if((uchar)*p == 0xc2 && (uchar)*(p+1) == 0xb7) {
このコードは、Mach-O形式のシンボルテーブルを処理する際に、シンボル名に含まれる特定のバイトシーケンス(\xc2\xb7
)を検出して、それを.
に変換するロジックの一部です。\xc2\xb7
はUTF-8でミドルドット(·)を表すシーケンスです。Goのリンカーは、シンボル名に特定の文字が含まれている場合に、それを正規化する処理を行っていると考えられます。
修正では、ポインタp
が指す文字(*p
)と次の文字(*(p+1)
)をuchar
(unsigned char
)にキャストしています。これにより、これらの文字が符号なしのバイト値として扱われ、0xc2
や0xb7
といった16進数リテラルとの比較が、符号拡張の影響を受けずに行われるようになります。
src/cmd/ld/symtab.c
(ELFシンボルテーブル処理)
// 修正前
if(*q == '\xc2' && *(q+1) == '\xb7') {
// 修正後
if((uchar)*q == 0xc2 && (uchar)*(q+1) == 0xb7) {
同様に、このコードはELF形式のシンボルテーブルの文字列データを処理する際に、同じバイトシーケンス(\xc2\xb7
)を検出して.
に変換するロジックです。
ここでも、ポインタq
が指す文字(*q
)と次の文字(*(q+1)
)をuchar
にキャストすることで、符号拡張による警告を回避し、バイト値としての正確な比較を保証しています。
これらの変更は、Goリンカーがシンボル名を処理する際のバイトレベルの比較において、Cコンパイラの挙動に依存しない堅牢なコードにするためのものです。特に、Plan 9のような特定の環境で発生するコンパイラの警告を解消し、ビルドの安定性を向上させる効果があります。
関連リンク
- Go CL 76580046: https://golang.org/cl/76580046
参考にした情報源リンク
- C言語における符号拡張と文字定数に関する情報源 (Web検索結果より)
- Plan 9とGo言語の関連性に関する情報源 (Web検索結果より)