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

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

このコミットは、GoランタイムがWindows/AMD64アーキテクチャ上で例外処理を行う方法を、従来のStructured Exception Handling (SEH)からVectored Exception Handling (VEH)へと移行するものです。これにより、Windows/AMD64ビルドにおける例外処理の信頼性と互換性が向上し、特定のビルド問題が修正されます。

コミット

commit a837347dd95d045e05bf0e6df9bf1c9b157c7c53
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Wed Mar 26 11:13:50 2014 +1100

    runtime: use VEH for windows/amd64 exception handling
    
    Fixes windows/amd64 build.
    
    LGTM=rsc
    R=golang-codereviews, rsc
    CC=golang-codereviews
    https://golang.org/cl/79470046

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

https://github.com/golang/go/commit/a837347dd95d045e05bf0e6df9bf1c9b157c7c53

元コミット内容

GoランタイムがWindows/AMD64環境での例外処理にVectored Exception Handling (VEH)を使用するように変更します。これにより、Windows/AMD64ビルドの問題が修正されます。

変更の背景

Go言語のランタイムは、プログラムの実行中に発生する様々な例外(例えば、ゼロ除算、無効なメモリアクセス、Goルーチンのパニックなど)を適切に処理する必要があります。Windows環境では、例外処理のためのメカニズムとして主にStructured Exception Handling (SEH)とVectored Exception Handling (VEH)の二つが存在します。

このコミット以前のGoランタイムのWindows/AMD64ビルドでは、例外処理に何らかの問題を抱えていたことが示唆されています。コミットメッセージの「Fixes windows/amd64 build.」という記述から、既存の例外処理メカニズムがWindows/AMD64環境で正しく機能していなかったか、あるいは特定の条件下でビルドが失敗する原因となっていたと考えられます。

SEHはコンパイラによって生成されるコードと密接に連携し、スタックアンワインド情報などを利用して例外を処理します。しかし、Goランタイムのように独自のスタック管理やスケジューリングを行うシステムでは、SEHの統合が複雑になる場合があります。特に、GoのgoroutineスタックとOSのスタックが異なる場合、SEHが期待通りに動作しない可能性があります。

VEHは、SEHよりも低レベルで、OSが例外をディスパッチする前にアプリケーションが独自のハンドラを登録できるメカニズムです。これにより、GoランタイムはOSの例外ディスパッチプロセスに早期に介入し、Go独自のパニック/リカバリメカニズムとOSの例外をより柔軟に連携させることが可能になります。この変更は、Windows/AMD64環境での例外処理の堅牢性を高め、既存のビルド問題を解決することを目的としています。

前提知識の解説

1. Windowsの例外処理メカニズム

  • Structured Exception Handling (SEH): Windowsのネイティブな例外処理メカニズムです。コンパイラによって生成される例外テーブル(PEファイルの.pdata.xdataセクションに格納されるアンワインド情報など)と連携し、例外発生時にスタックを巻き戻しながら適切な例外ハンドラを探します。C++のtry-catchブロックやWindows APIの__try/__exceptなどがSEHを利用しています。SEHはスレッドごとに例外ハンドラチェーンを持ちます。

  • Vectored Exception Handling (VEH): Windows XPで導入された、SEHよりも高レベルで、かつSEHよりも先に実行される例外処理メカニズムです。AddVectoredExceptionHandler関数を使って登録されたハンドラは、プロセス内のどのスレッドで例外が発生しても、SEHハンドラが呼び出される前に実行されます。VEHハンドラは、例外を処理するか、他のハンドラに処理を委ねるかを決定できます。Goランタイムのように、独自のスタック管理や実行モデルを持つシステムにとって、OSの例外ディスパッチに早期に介入できるVEHは非常に有用です。

2. Portable Executable (PE) ファイルフォーマット

Windowsの実行可能ファイル(.exe)やダイナミックリンクライブラリ(.dll)のフォーマットです。PEファイルは、コード、データ、リソース、そして例外処理情報など、プログラムの実行に必要な様々なセクションを含んでいます。

  • .pdataセクション: 関数ポインタとアンワインド情報のオフセットを格納するテーブルです。AMD64アーキテクチャでは、例外発生時のスタックアンワインドに不可欠な情報が含まれます。
  • .xdataセクション: アンワインド情報自体を格納します。

3. Goランタイムと例外処理

Go言語には、panicrecoverという独自のエラー処理メカニズムがあります。これは、Goルーチン内で発生した予期せぬエラー(ランタイムエラーや明示的なpanic呼び出し)を捕捉し、プログラムのクラッシュを防ぐためのものです。 OSレベルで発生する例外(例えば、セグメンテーション違反やゼロ除算)は、Goランタイムによって捕捉され、Goのpanicに変換される必要があります。この変換を行うのが、sigtrampsighandlerといったランタイム内のシグナル/例外ハンドラです。

4. sigtrampsighandler

  • sigtramp: OSから直接呼び出される、アセンブリ言語で書かれた低レベルのシグナル/例外トラップハンドラです。OSのコンテキスト(レジスタの状態など)を保存し、GoランタイムのCコードで書かれたsighandler関数を呼び出す準備をします。
  • sighandler: sigtrampから呼び出されるGoランタイムのCコード関数で、具体的な例外の種類を判断し、Goのpanicに変換したり、デバッガに情報を渡したりする役割を担います。

技術的詳細

このコミットの主要な変更点は、GoランタイムがWindows/AMD64環境で例外を処理するために、従来のSEH関連のメカニズムからVEHへと完全に移行したことです。

  1. PEファイルからの例外情報削除 (src/cmd/ld/pe.c):

    • addexcept関数が削除されました。この関数は、リンカがPEファイルに.pdata(関数テーブル)と.xdata(アンワインドデータ)セクションを追加し、SEHのための例外処理情報を埋め込む役割を担っていました。
    • この削除は、GoランタイムがもはやSEHのメカニズムに依存せず、VEHを通じて独自の例外処理を行うことを意味します。リンカがPEファイルにSEH関連の情報を埋め込む必要がなくなったため、このコードが不要になりました。
  2. 汎用Windowsランタイムからの例外ハンドラ登録/削除の削除 (src/pkg/runtime/os_windows.c):

    • runtime·install_exception_handler()runtime·remove_exception_handler()の呼び出しが、runtime·minit()runtime·unminit()から削除されました。
    • これは、Goランタイムが起動時や終了時に明示的に例外ハンドラを登録・解除する従来の方式を廃止したことを示しています。VEHはAddVectoredExceptionHandlerによって登録され、プロセス全体で有効になるため、GoルーチンやM(マシン)の初期化/終了とは直接関連付けられなくなります。
  3. AMD64固有の例外ハンドラロジックの変更 (src/pkg/runtime/os_windows_amd64.c):

    • runtime·sighandler関数が変更されました。この関数は、VEHハンドラであるsigtrampから呼び出されます。
    • DBG_PRINTEXCEPTION_Cという新しい例外コードが追加されました。これはデバッガが捕捉することを意図した例外で、Goランタイムはこれを無視するように変更されました。
    • EXCEPTION_BREAKPOINTの処理がコメントアウトされ、Goランタイムがアセンブリソース内のブレークポイント命令を直接処理しない方針が示されました。
    • runtime·sighandlerの戻り値が変更されました。以前は0を返すことで他のハンドラに処理を委ねる可能性がありましたが、多くのケースで-1(例外を処理済み)を返すようになりました。これにより、Goランタイムが例外を完全に捕捉し、Windowsの他のSEHハンドラに処理が渡されないように制御します。
    • デバッグ出力にr->Rip(命令ポインタ)が追加され、例外発生時のPC値がより詳細に表示されるようになりました。
  4. AMD64アセンブリレベルでのVEHハンドラの実装 (src/pkg/runtime/sys_windows_amd64.s):

    • runtime·sigtramp関数が大幅に書き換えられました。この関数は、WindowsによってVEHハンドラとして登録され、例外発生時に直接呼び出されます。
    • 新しいsigtrampは、Windowsのコールバック規約に従って、DI, SI, BP, BX, R12, R13, R14, R15レジスタとDFフラグを保存・復元するロジックが追加されました。これは、VEHハンドラが呼び出された後に、Goランタイムがこれらのレジスタの状態を維持する必要があるためです。
    • PEXCEPTION_POINTERS ExceptionInfoという引数を受け取るように変更され、例外レコードとコンテキストポインタを適切に取得するようになりました。
    • runtime·sighandlerを呼び出す際に、ExceptionRecord*, Context*, G*(現在のGoルーチン)の3つの引数を渡すようにスタックが設定されます。
    • sigtrampの戻り値(AXレジスタ)が、runtime·sighandlerの戻り値に基づいて設定されます。これにより、Windowsに対して例外が処理されたか(-1)、あるいは他のハンドラに委ねるべきか(0)を通知します。
    • runtime·install_exception_handler()runtime·remove_exception_handler()のアセンブリ実装が削除されました。これは、これらの関数がもはや使用されないためです。

この変更により、GoランタイムはWindowsの例外処理メカニズムに深く統合され、より堅牢な例外処理が可能になりました。特に、Goの独自のスタック管理とWindowsの例外処理の間のギャップをVEHが埋めることで、GoプログラムがWindows上でより安定して動作するようになります。

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

src/cmd/ld/pe.c

--- a/src/cmd/ld/pe.c
+++ b/src/cmd/ld/pe.c
@@ -529,49 +529,6 @@ addpersrc(void)
 	dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h->VirtualSize;
 }
 
-static void
-addexcept(IMAGE_SECTION_HEADER *text)
-{
-	IMAGE_SECTION_HEADER *pdata, *xdata;
-	vlong startoff;
-	uvlong n;
-	LSym *sym;
-
-	USED(text);
-	if(thechar != '6')
-		return;
-
-	// write unwind info
-	sym = linklookup(ctxt, "runtime.sigtramp", 0);
-	startoff = cpos();
-	lputl(9);	// version=1, flags=UNW_FLAG_EHANDLER, rest 0
-	lputl(sym->value - PEBASE);
-	lputl(0);
-
-	n = cpos() - startoff;
-	xdata = addpesection(".xdata", n, n);
-	xdata->Characteristics = IMAGE_SCN_MEM_READ|
-		IMAGE_SCN_CNT_INITIALIZED_DATA;
-	chksectoff(xdata, startoff);
-	strnput("", xdata->SizeOfRawData - n);
-
-	// write a function table entry for the whole text segment
-	startoff = cpos();
-	lputl(text->VirtualAddress);
-	lputl(text->VirtualAddress + text->VirtualSize);
-	lputl(xdata->VirtualAddress);
-
-	n = cpos() - startoff;
-	pdata = addpesection(".pdata", n, n);
-	pdata->Characteristics = IMAGE_SCN_MEM_READ|
-		IMAGE_SCN_CNT_INITIALIZED_DATA;
-	chksectoff(pdata, startoff);
-	strnput("", pdata->SizeOfRawData - n);
-
-	dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = pdata->VirtualAddress;
-	dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = pdata->VirtualSize;
-}
-
 void
 asmbpe(void)
 {
@@ -609,7 +566,6 @@ asmbpe(void)
 	addexports();
 	addsymtable();
 	addpersrc();
-	addexcept(t);
 
 	fh.NumberOfSections = nsect;
 	fh.TimeDateStamp = time(0);

src/pkg/runtime/os_windows.c

--- a/src/pkg/runtime/os_windows.c
+++ b/src/pkg/runtime/os_windows.c
@@ -248,15 +248,12 @@ runtime·minit(void)
 		(uintptr)-1, (uintptr)-2, (uintptr)-1, &thandle,
 		(uintptr)0, (uintptr)0, (uintptr)DUPLICATE_SAME_ACCESS);
 	runtime·atomicstorep(&m->thread, thandle);
-
-	runtime·install_exception_handler();
 }
 
 // Called from dropm to undo the effect of an minit.
 void
 runtime·unminit(void)
 {
-	runtime·remove_exception_handler();
 }
 
 #pragma textflag NOSPLIT

src/pkg/runtime/os_windows_amd64.c

--- a/src/pkg/runtime/os_windows_amd64.c
+++ b/src/pkg/runtime/os_windows_amd64.c
@@ -32,6 +32,11 @@ runtime·dumpregs(Context *r)
 	runtime·printf("gs      %X\n", (uint64)r->SegGs);
 }
 
+#define DBG_PRINTEXCEPTION_C 0x40010006
+
+// Called by sigtramp from Windows VEH handler.
+// Return value signals whether the exception has been handled (-1)
+// or should be made available to other handlers in the chain (0).
 uint32
 runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
 {
@@ -39,8 +44,25 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
 	uintptr *sp;
 
 	switch(info->ExceptionCode) {
+	case DBG_PRINTEXCEPTION_C:
+		// This exception is intended to be caught by debuggers.
+		// There is a not-very-informational message like
+		// "Invalid parameter passed to C runtime function"
+		// sitting at info->ExceptionInformation[0] (a wchar_t*),
+		// with length info->ExceptionInformation[1].
+		// The default behavior is to ignore this exception,
+		// but somehow returning 0 here (meaning keep going)
+		// makes the program crash instead. Maybe Windows has no
+		// other handler registered? In any event, ignore it.
+		return -1;
+
 	case EXCEPTION_BREAKPOINT:
-		return 1;
+		// It is unclear whether this is needed, unclear whether it
+		// would work, and unclear how to test it. Leave out for now.
+		// This only handles breakpoint instructions written in the
+		// assembly sources, not breakpoints set by a debugger, and
+		// there are very few of the former.
+		break;
 	}
 
 	if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
@@ -65,15 +87,16 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
 			r->Rsp = (uintptr)sp;
 		}
 		r->Rip = (uintptr)runtime·sigpanic;
-		return 0;
+		return -1;
 	}
 
 	if(runtime·panicking)	// traceback already printed
 		runtime·exit(2);
 	runtime·panicking = 1;
 
-	runtime·printf("Exception %x %p %p\n", info->ExceptionCode,
-		info->ExceptionInformation[0], info->ExceptionInformation[1]);
+	runtime·printf("Exception %x %p %p %p\n", info->ExceptionCode,
+		info->ExceptionInformation[0], info->ExceptionInformation[1], r->Rip);
+
 
 	runtime·printf("PC=%X\n", r->Rip);
 	if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
@@ -92,7 +115,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
 		runtime·crash();
 
 	runtime·exit(2);
-	return 0;
+	return -1; // not reached
 }
 
 void

src/pkg/runtime/sys_windows_386.s

--- a/src/pkg/runtime/sys_windows_386.s
+++ b/src/pkg/runtime/sys_windows_386.s
@@ -313,14 +313,6 @@ TEXT runtime·setldt(SB),NOSPLIT,$0
 	MOVL	CX, 0x14(FS)
 	RET
 
-// void install_exception_handler()
-TEXT runtime·install_exception_handler(SB),NOSPLIT,$0
-	RET
-
-// void remove_exception_handler()
-TEXT runtime·remove_exception_handler(SB),NOSPLIT,$0
-	RET
-
 // Sleep duration is in 100ns units.
 TEXT runtime·usleep1(SB),NOSPLIT,$0
 	MOVL	duration+0(FP), BX

src/pkg/runtime/sys_windows_amd64.s

--- a/src/pkg/runtime/sys_windows_amd64.s
+++ b/src/pkg/runtime/sys_windows_amd64.s
@@ -95,49 +95,55 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0
 	MOVL	AX, 0x68(CX)
 	RET
 
-TEXT runtime·sigtramp(SB),NOSPLIT,$0
-	// CX: exception record
-	// R8: context
+// Called by Windows as a Vectored Exception Handler (VEH).
+// First argument is pointer to struct containing
+// exception record and context pointers.
+// Return 0 for 'not handled', -1 for handled.
+TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
+	// CX: PEXCEPTION_POINTERS ExceptionInfo
 
-	// unwinding?
-	TESTL	$6, 4(CX)		// exception flags
-	MOVL	$1, AX
-	JNZ	sigdone
-
-	// copy arguments for call to sighandler.
-
-	// Stack adjustment is here to hide from 6l,
-	// which doesn't understand that sigtramp
-	// runs on essentially unlimited stack.
-	SUBQ	$56, SP
-	MOVQ	CX, 0(SP)
-	MOVQ	R8, 8(SP)
-
-	get_tls(CX)
-
-	// check that m exists
-	MOVQ	m(CX), AX
+	// DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
+	// as required by windows callback convention.
+	PUSHFQ
+	SUBQ	$88, SP
+	MOVQ	DI, 80(SP)
+	MOVQ	SI, 72(SP)
+	MOVQ	BP, 64(SP)
+	MOVQ	BX, 56(SP)
+	MOVQ	R12, 48(SP)
+	MOVQ	R13, 40(SP)
+	MOVQ	R14, 32(SP)
+	MOVQ	R15, 24(SP)
+
+	MOVQ	0(CX), BX // ExceptionRecord*
+	MOVQ	8(CX), CX // Context*
+
+	// fetch g
+	get_tls(DX)
+	MOVQ	m(DX), AX
 	CMPQ	AX, $0
 	JNE	2(PC)
 	CALL	runtime·badsignal2(SB)
-
-	MOVQ	g(CX), CX
-	MOVQ	CX, 16(SP)
-
-	MOVQ	BX, 24(SP)
-	MOVQ	BP, 32(SP)
-	MOVQ	SI, 40(SP)
-	MOVQ	DI, 48(SP)
-
+	MOVQ	g(DX), DX
+	// call sighandler(ExceptionRecord*, Context*, G*)
+	MOVQ	BX, 0(SP)
+	MOVQ	CX, 8(SP)
+	MOVQ	DX, 16(SP)
 	CALL	runtime·sighandler(SB)
+	// AX is set to report result back to Windows
 
-	MOVQ	24(SP), BX
-	MOVQ	32(SP), BP
-	MOVQ	40(SP), SI
-	MOVQ	48(SP), DI
-	ADDQ	$56, SP
+	// restore registers as required for windows callback
+	MOVQ	24(SP), R15
+	MOVQ	32(SP), R14
+	MOVQ	40(SP), R13
+	MOVQ	48(SP), R12
+	MOVQ	56(SP), BX
+	MOVQ	64(SP), BP
+	MOVQ	72(SP), SI
+	MOVQ	80(SP), DI
+	ADDQ	$88, SP
+	POPFQ
 
-sigdone:
 	RET
 
 TEXT runtime·ctrlhandler(SB),NOSPLIT,$8
@@ -277,13 +283,6 @@ TEXT runtime·callbackasm1(SB),NOSPLIT,$0
 	POPQ	-8(CX)(DX*1)      // restore bytes just after the args
 	RET
 
-TEXT runtime·setstacklimits(SB),NOSPLIT,$0
-	MOVQ	0x30(GS), CX
-	MOVQ	$0, 0x10(CX)
-	MOVQ	$0xffffffffffff, AX
-	MOVQ	AX, 0x08(CX)
-	RET
-
 // uint32 tstart_stdcall(M *newm);\n TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0
 	// CX contains first arg newm
 	// R8: context
@@ -315,14 +314,6 @@ TEXT runtime·settls(SB),NOSPLIT,$0
 	MOVQ	DI, 0x28(GS)
 	RET
 
-// void install_exception_handler()
-TEXT runtime·install_exception_handler(SB),NOSPLIT,$0
-	CALL	runtime·setstacklimits(SB)
-	RET
-
-TEXT runtime·remove_exception_handler(SB),NOSPLIT,$0
-	RET
-
 // Sleep duration is in 100ns units.
 TEXT runtime·usleep1(SB),NOSPLIT,$0
 	MOVL	duration+0(FP), BX

コアとなるコードの解説

src/cmd/ld/pe.c の変更

  • addexcept 関数の削除: この関数は、WindowsのPEファイルフォーマットにおいて、SEH(Structured Exception Handling)に必要な例外処理情報(アンワインド情報)を.pdataおよび.xdataセクションとして追加する役割を担っていました。GoランタイムがVEH(Vectored Exception Handling)に移行することで、リンカがSEH関連の情報をPEファイルに埋め込む必要がなくなりました。これにより、Goの実行ファイルはSEHのフレームワークに依存せず、VEHを通じて独自の例外処理フローを確立できるようになります。

src/pkg/runtime/os_windows.c の変更

  • runtime·install_exception_handler()runtime·remove_exception_handler() の呼び出し削除: これらの関数は、Goランタイムが初期化される際(runtime·minit)と終了する際(runtime·unminit)に、カスタムの例外ハンドラを登録・解除するために使用されていました。VEHはAddVectoredExceptionHandlerによってプロセス全体に登録されるため、GoのM(マシン)の初期化/終了とは直接関連付ける必要がなくなりました。この変更は、例外ハンドラの管理がよりグローバルなVEHメカニズムに委ねられたことを示しています。

src/pkg/runtime/os_windows_amd64.c の変更

  • DBG_PRINTEXCEPTION_C の追加と処理: 0x40010006という例外コードがDBG_PRINTEXCEPTION_Cとして定義され、runtime·sighandler内でこの例外が捕捉された場合に-1を返すように変更されました。この例外はデバッガが捕捉することを意図しており、Goランタイムはこれを無視することで、デバッグ時の不要なクラッシュを防ぎます。
  • EXCEPTION_BREAKPOINT の処理変更: ブレークポイント例外の処理がコメントアウトされました。これは、Goランタイムがアセンブリソースコードに埋め込まれたブレークポイント命令を直接処理するのではなく、デバッガにその処理を委ねる方針を示唆しています。
  • runtime·sighandler の戻り値の変更: runtime·sighandlerが、多くのケースで-1(例外を処理済み)を返すように変更されました。これにより、Goランタイムが例外を完全に捕捉し、Windowsの他のSEHハンドラに処理が渡されないように制御します。以前は0を返すことで他のハンドラに処理を委ねる可能性がありましたが、VEHを主軸とすることで、Goランタイムが例外処理の主導権を握るようになりました。
  • デバッグ出力の強化: runtime·printfによる例外情報の出力にr->Rip(命令ポインタ)が追加されました。これにより、例外発生時のプログラムカウンタの値がより詳細に表示され、デバッグが容易になります。

src/pkg/runtime/sys_windows_amd64.s の変更

  • runtime·sigtramp の大幅な書き換え: このアセンブリ関数は、WindowsによってVEHハンドラとして登録され、例外発生時にOSから直接呼び出されるエントリポイントとなります。
    • レジスタの保存と復元: Windowsのコールバック規約に従い、DI, SI, BP, BX, R12, R13, R14, R15レジスタとDFフラグをスタックに保存し、runtime·sighandlerの呼び出し後に復元するロジックが追加されました。これにより、Goランタイムの実行コンテキストがVEHハンドラの呼び出しによって破壊されないように保護されます。
    • 引数の処理: PEXCEPTION_POINTERS ExceptionInfoという引数(CXレジスタで渡される)から、ExceptionRecord*Context*のポインタを適切に抽出し、runtime·sighandlerに渡すための準備を行います。
    • runtime·sighandler の呼び出し: 抽出したExceptionRecord*Context*、そして現在のGoルーチン(G*)のポインタを引数としてruntime·sighandlerを呼び出します。
    • 戻り値の設定: runtime·sighandlerの戻り値(AXレジスタ)を、sigtrampの戻り値として設定します。これにより、Windowsに対して例外がGoランタイムによって処理されたか(-1)、あるいは他のハンドラに処理を委ねるべきか(0)を通知します。
  • runtime·install_exception_handler()runtime·remove_exception_handler() のアセンブリ実装削除: これらの関数はもはや使用されないため、アセンブリコードからも削除されました。

これらの変更は、GoランタイムがWindowsの例外処理をより直接的かつ効率的に制御できるようにするための重要なステップです。VEHの導入により、Goのパニック/リカバリメカニズムとOSレベルの例外との連携が強化され、Windows/AMD64環境でのGoプログラムの安定性が向上します。

関連リンク

参考にした情報源リンク