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

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

このコミットは、GoランタイムがDarwin (macOS) 環境でDYLD_INSERT_LIBRARIES環境変数が設定されている場合、またはbsdthread_registerシステムコールが失敗した場合に、より適切なエラーメッセージを提供するように改善するものです。これにより、ユーザーは問題の原因を特定しやすくなります。

コミット

commit 44fd1d1a6aee68023be292a6f856991af3f0d4c8
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Apr 30 15:55:07 2012 -0400

    runtime: give proper messages when user defined DYLD_INSERT_LIBRARIES on Darwin
           also gives clear error when bsdthread_register fails on Darwin
           Fixes #2992.
    
    R=rsc, krautz
    CC=golang-dev
    https://golang.org/cl/5966067

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

https://github.com/golang/go/commit/44fd1d1a6aee68023be292a6f856991af3f0d4c8

元コミット内容

runtime: give proper messages when user defined DYLD_INSERT_LIBRARIES on Darwin
       also gives clear error when bsdthread_register fails on Darwin
       Fixes #2992.

変更の背景

この変更の背景には、GoプログラムがmacOS上で実行される際に発生する可能性のある、特定の環境設定やシステムコール失敗時のデバッグの困難さがありました。

  1. DYLD_INSERT_LIBRARIESによる問題の明確化: DYLD_INSERT_LIBRARIESはmacOSのダイナミックリンカ(dyld)が、プログラム自身の依存関係がロードされる前に、指定されたダイナミックライブラリ(.dylibファイル)をプロセスにロードすることを可能にする環境変数です。これはデバッグ、テスト、またはカスタム機能の実装に便利ですが、悪意のあるコードインジェクションにも利用される可能性があります。Goプログラムがこの環境変数の影響を受けると、予期せぬ動作やクラッシュを引き起こすことがありましたが、その際のエラーメッセージが不明瞭でした。ユーザーがこの環境変数を設定している場合に、Goランタイムがより具体的なエラーメッセージを出すことで、問題の特定と解決を容易にすることが求められました。

  2. bsdthread_register失敗時のエラーハンドリング改善: bsdthread_registerはDarwin(macOS)のシステムコールであり、スレッドシステムの設定に使用されます。Goランタイムは、macOS上でスレッドを管理するためにこのシステムコールを利用します。このシステムコールが何らかの理由で失敗した場合、Goプログラムはクラッシュする可能性がありましたが、その際のエラーメッセージが不十分でした。特に、古いGoバージョンと新しいmacOSバージョンの間の互換性の問題や、セキュリティソフトウェアの干渉などが原因でこのエラーが発生することがありました。このコミットは、bsdthread_registerが失敗した場合に、より明確なエラーメッセージを提供することで、ユーザーが根本原因を理解し、対処できるようにすることを目的としています。

これらの改善は、GoプログラムのmacOS環境での堅牢性とデバッグ体験を向上させるために行われました。

前提知識の解説

このコミットを理解するためには、以下の概念について知っておく必要があります。

  1. Goランタイム (Go Runtime): Go言語で書かれたプログラムを実行するための環境です。メモリ管理(ガベージコレクション)、ゴルーチン(軽量スレッド)のスケジューリング、チャネルによる通信、システムコールへのインターフェースなど、プログラムの実行に必要な低レベルの機能を提供します。

  2. Darwin (macOS): AppleのオペレーティングシステムであるmacOSの基盤となるUNIX系OSです。Goランタイムは、このDarwin上で動作するために、Darwin固有のシステムコールやAPIを利用します。

  3. DYLD_INSERT_LIBRARIES: macOSの環境変数で、ダイナミックリンカ(dyld)に対して、指定された共有ライブラリ(.dylibファイル)を、アプリケーションの起動時にそのアプリケーションのプロセス空間に強制的にロードさせるためのものです。これは、既存のアプリケーションの動作を変更したり、デバッグフックを挿入したりする際に使用されます。しかし、セキュリティ上のリスクも伴い、悪用される可能性もあります。macOSのSystem Integrity Protection (SIP) やHardened Runtimeなどのセキュリティ機能は、このメカニズムの悪用を防ぐために導入されています。

  4. bsdthread_register: Darwinカーネルが提供するシステムコールの一つです。これは、ユーザー空間のスレッドライブラリ(例えば、Goランタイムが内部的に使用するスレッド管理メカニズム)が、カーネルに対して自身を登録するために使用されます。これにより、カーネルはユーザー空間のスレッドの状態を適切に管理し、シグナルハンドリングやデバッグなどの機能を提供できるようになります。GoランタイムがmacOS上でゴルーチンを効率的にスケジューリングし、システムリソースにアクセスするためには、このbsdthread_registerが正常に機能することが不可欠です。

  5. Cgo: Go言語の機能の一つで、C言語のコードをGoプログラムから呼び出すことを可能にします。Cgoを使用すると、Goの標準ライブラリでは提供されていないOS固有のAPIや、既存のCライブラリを利用できます。Cgoを使用する場合、GoランタイムはC言語のpthreadライブラリと連携してスレッドを管理することがあります。

技術的詳細

このコミットは、GoランタイムがmacOS上でスレッドを初期化する際の堅牢性を高めるためのものです。

Goランタイムは、macOS上で独自の軽量スレッド(ゴルーチン)を管理するために、基盤となるOSのスレッド機能を利用します。この際、bsdthread_registerシステムコールを呼び出して、Goランタイムのスレッド管理メカニズムをカーネルに登録します。

変更前は、bsdthread_registerの呼び出しがruntime·osinit関数内で行われていました。この関数は、環境変数を読み込む前に実行されるため、DYLD_INSERT_LIBRARIESのような環境変数の影響を考慮する機会がありませんでした。また、bsdthread_registerが失敗した場合、ランタイムは単にクラッシュするだけで、具体的なエラーメッセージを提供していませんでした。

このコミットでは、以下の技術的な変更が加えられています。

  1. bsdthread_registerの呼び出しタイミングの変更: runtime·bsdthread_registerの呼び出しが、runtime·osinitからruntime·goenvs関数内に移動されました。runtime·goenvsは、環境変数が読み込まれた後に実行されるため、ランタイムはDYLD_INSERT_LIBRARIESのような環境変数の存在をチェックできるようになります。

  2. bsdthread_registerの戻り値の利用: runtime·bsdthread_register関数のシグネチャが変更され、voidからint32を返すようになりました。これにより、システムコールの成功/失敗を示す戻り値を利用して、エラーハンドリングを行うことが可能になります。アセンブリコード(sys_darwin_386.ssys_darwin_amd64.s)も、この戻り値を適切に処理するように修正されています。具体的には、システムコールが失敗した場合(キャリーフラグがセットされない場合)、AXレジスタに格納されたエラーコードを否定(NEGL AX)して返し、成功した場合は0を返すように変更されています。

  3. エラーメッセージの改善: runtime·thread_darwin.c内のruntime·goenvs関数で、runtime·bsdthread_register()の戻り値をチェックし、非ゼロ(エラー)の場合にエラーメッセージを生成するロジックが追加されました。

    • もしDYLD_INSERT_LIBRARIES環境変数が設定されている場合、runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)という具体的なメッセージをスローします。これは、DYLD_INSERT_LIBRARIESがスレッド登録の失敗の原因である可能性が高いことを示唆し、ユーザーにその環境変数を解除するよう促します。
    • DYLD_INSERT_LIBRARIESが設定されていない場合でもbsdthread_registerが失敗した場合は、runtime: bsdthread_register errorという一般的なエラーメッセージをスローします。
  4. Cgoとの連携: Cgoを使用している場合は、GoランタイムがCのpthreadライブラリにスレッド作成のコールバックをインストールさせる必要があるため、runtime·iscgoフラグをチェックしてbsdthread_registerの呼び出しをスキップするロジックは維持されています。

これらの変更により、GoランタイムはmacOS上でのスレッド初期化に関する問題をより適切に診断し、ユーザーに具体的な解決策を提示できるようになりました。

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

このコミットで変更された主要なファイルとコードの変更箇所は以下の通りです。

  1. src/pkg/runtime/os_darwin.h:

    • runtime·bsdthread_register関数の宣言が、voidを返すものからint32を返すものに変更されました。
      --- a/src/pkg/runtime/os_darwin.h
      +++ b/src/pkg/runtime/os_darwin.h
      @@ -6,7 +6,7 @@
       #define SIG_IGN ((void*)1)
       
       int32	runtime·bsdthread_create(void*, M*, G*, void(*)(void));
      -void	runtime·bsdthread_register(void);
      +int32	runtime·bsdthread_register(void);
       int32	runtime·mach_msg_trap(MachHeader*, int32, uint32, uint32, uint32, uint32, uint32);
       uint32	runtime·mach_reply_port(void);
       int32	runtime·mach_semacquire(uint32, int64);
      
  2. src/pkg/runtime/sys_darwin_386.s (32-bit Intel アセンブリ):

    • runtime·bsdthread_registerのシステムコール呼び出し後のエラーハンドリングが変更されました。以前はエラー時にクラッシュしていましたが、エラーコードを返すように修正されました。
      --- a/src/pkg/runtime/sys_darwin_386.s
      +++ b/src/pkg/runtime/sys_darwin_386.s
      @@ -268,8 +268,10 @@ TEXT runtime·bsdthread_register(SB),7,$40
       	MOVL	$0, 20(SP)	// targetconc_ptr
       	MOVL	$0, 24(SP)	// dispatchqueue_offset
       	INT	$0x80
      -	JAE	2(PC)
      -	MOVL	$0xf1, 0xf1  // crash
      +	JAE	3(PC)
      +	NEGL	AX
      +	RET
      +	MOVL	$0, AX
       	RET
       
       // Invoke Mach system call.
      
  3. src/pkg/runtime/sys_darwin_amd64.s (64-bit Intel アセンブリ):

    • runtime·bsdthread_registerのシステムコール呼び出し後のエラーハンドリングが変更されました。32-bit版と同様に、エラー時にクラッシュする代わりにエラーコードを返すように修正されました。
      --- a/src/pkg/runtime/sys_darwin_amd64.s
      +++ b/src/pkg/runtime/sys_darwin_amd64.s
      @@ -265,8 +265,10 @@ TEXT runtime·bsdthread_register(SB),7,$0
       	MOVQ	$0, R9	// dispatchqueue_offset
       	MOVQ	$(0x2000000+366), AX	// bsdthread_register
       	SYSCALL
      -	JCC 2(PC)
      -	MOVL	$0xf1, 0xf1  // crash
      +	JCC 3(PC)
      +	NEGL	AX
      +	RET
      +	MOVL	$0, AX
       	RET
       
       // Mach system calls use 0x1000000 instead of the BSD's 0x2000000.
      
  4. src/pkg/runtime/thread_darwin.c:

    • runtime·osinitからruntime·bsdthread_registerの呼び出しが削除され、runtime·goenvs関数内に移動されました。
    • runtime·goenvs内で、runtime·bsdthread_register()の戻り値をチェックし、エラー時にDYLD_INSERT_LIBRARIESの有無に応じて異なるエラーメッセージをスローするロジックが追加されました。
      --- a/src/pkg/runtime/thread_darwin.c
      +++ b/src/pkg/runtime/thread_darwin.c
      @@ -50,11 +50,8 @@ runtime·semacreate(void)
       void
       runtime·osinit(void)
       {
      -	// Register our thread-creation callback (see sys_darwin_{amd64,386}.s)
      -	// but only if we're not using cgo.  If we are using cgo we need
      -	// to let the C pthread libary install its own thread-creation callback.
      -	if(!runtime·iscgo)
      -		runtime·bsdthread_register();
      +	// bsdthread_register delayed until end of goenvs so that we
      +	// can look at the environment first.
       
       	// Use sysctl to fetch hw.ncpu.
       	uint32 mib[2];
      @@ -75,6 +72,18 @@ void
       runtime·goenvs(void)
       {
       	runtime·goenvs_unix();
      +
      +	// Register our thread-creation callback (see sys_darwin_{amd64,386}.s)
      +	// but only if we're not using cgo.  If we are using cgo we need
      +	// to let the C pthread libary install its own thread-creation callback.
      +	if(!runtime·iscgo) {
      +		if(runtime·bsdthread_register() != 0) {
      +			if(runtime·getenv("DYLD_INSERT_LIBRARIES"))
      +				runtime·throw("runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)");
      +			runtime·throw("runtime: bsdthread_register error");
      +		}
      +	}
      +
       }
       
       void
      

コアとなるコードの解説

このコミットの核心は、GoランタイムがmacOS上でスレッドを初期化する際の挙動をより堅牢にし、デバッグを容易にすることにあります。

  1. os_darwin.hの変更:

    • runtime·bsdthread_register関数の戻り値の型をvoidからint32に変更したことは、この関数がシステムコールからの結果(成功/失敗を示すエラーコード)を返すことを可能にするための基盤です。これにより、呼び出し元はシステムコールの成否をプログラム的に判断できるようになります。
  2. sys_darwin_386.sおよびsys_darwin_amd64.sの変更:

    • これらのアセンブリファイルは、それぞれ32ビットおよび64ビットのmacOSアーキテクチャにおけるbsdthread_registerシステムコールの実際の呼び出しを定義しています。
    • 変更前は、システムコールが失敗した場合(JAEまたはJCC命令でキャリーフラグがセットされない場合)、MOVL $0xf1, 0xf1のような命令で意図的にクラッシュさせていました。これは、致命的なエラーが発生したことを示すための原始的な方法でした。
    • 変更後は、システムコールが失敗した場合に、NEGL AX命令を使用してAXレジスタに格納されたエラーコードを否定し、それを関数の戻り値として返します。成功した場合はMOVL $0, AX0を返します。これにより、Cコード側でシステムコールの結果を数値として受け取り、より詳細なエラーハンドリングを行うことが可能になります。
  3. thread_darwin.cの変更:

    • bsdthread_register呼び出しの遅延: 以前はruntime·osinitbsdthread_registerを呼び出していましたが、これをruntime·goenvsに移動しました。runtime·goenvsは環境変数が読み込まれた後に実行されるため、DYLD_INSERT_LIBRARIESのような環境変数の存在をチェックする機会が得られます。これは、環境変数がスレッド登録の失敗に影響を与える可能性があるため、非常に重要です。
    • エラーチェックとメッセージの改善:
      • if(runtime·bsdthread_register() != 0): bsdthread_registerが非ゼロの値を返した場合(つまりエラーが発生した場合)に、エラー処理ブロックに入ります。
      • if(runtime·getenv("DYLD_INSERT_LIBRARIES")): ここでDYLD_INSERT_LIBRARIES環境変数が設定されているかどうかをチェックします。runtime·getenvは、指定された環境変数の値を取得するGoランタイムの内部関数です。
      • もしDYLD_INSERT_LIBRARIESが設定されていてbsdthread_registerが失敗した場合、runtime·throw("runtime: bsdthread_register error (unset DYLD_INSERT_LIBRARIES)")というメッセージでパニックを発生させます。このメッセージは、DYLD_INSERT_LIBRARIESが問題の原因である可能性が高いことを明確に示し、ユーザーにその環境変数を解除するよう促します。
      • DYLD_INSERT_LIBRARIESが設定されていない場合でもbsdthread_registerが失敗した場合は、runtime·throw("runtime: bsdthread_register error")という一般的なエラーメッセージでパニックを発生させます。これは、他の未知の原因による失敗を示唆します。
    • Cgoの考慮: if(!runtime·iscgo)のチェックは引き続き行われます。これは、Cgoを使用しているGoプログラムでは、GoランタイムがCのpthreadライブラリにスレッド作成のコールバックをインストールさせる必要があるため、Goランタイム自身がbsdthread_registerを呼び出す必要がないためです。

これらの変更により、GoランタイムはmacOS上でのスレッド初期化に関する問題をより詳細に診断し、特にDYLD_INSERT_LIBRARIESが関与している場合に、ユーザーに具体的なデバッグのヒントを提供できるようになりました。

関連リンク

参考にした情報源リンク