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

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

このコミットは、Goリンカー (cmd/ld) におけるPlan 9環境でのコンパイル時の警告を修正するものです。具体的には、src/cmd/ld/macho.csrc/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として扱われるかは、コンパイラやアーキテクチャに依存します。もしcharsigned 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型が符号付きとして扱われる環境であれば、この文字定数は負の値として符号拡張されてしまいます。

例: charsigned 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型として扱われます。もしコンパイラがcharsigned charとして扱う場合、*p*qの値が127を超えると負の値として解釈され、また文字定数も符号拡張されて負の値になります。この比較が意図通りに機能しない可能性や、コンパイラが警告を発する原因となっていました。

修正後のコードでは、比較対象の*p*qunsigned char型にキャストしています。

// 修正後
if((uchar)*p == 0xc2 && (uchar)*(p+1) == 0xb7) {
// ...
if((uchar)*q == 0xc2 && (uchar)*(q+1) == 0xb7) {

ここで、ucharは通常unsigned charのtypedefです。*p*qunsigned charにキャストすることで、その値は常に0から255の範囲の符号なし整数として扱われます。これにより、符号拡張の問題が回避され、0xc20xb7といった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.cmachosymtab関数内と、src/cmd/ld/symtab.cputelfstr関数内で行われています。

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))をucharunsigned char)にキャストしています。これにより、これらの文字が符号なしのバイト値として扱われ、0xc20xb7といった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のような特定の環境で発生するコンパイラの警告を解消し、ビルドの安定性を向上させる効果があります。

関連リンク

参考にした情報源リンク

  • C言語における符号拡張と文字定数に関する情報源 (Web検索結果より)
  • Plan 9とGo言語の関連性に関する情報源 (Web検索結果より)