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

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

このコミットでは、GoランタイムのNetBSDにおけるシグナルハンドリングに関連するヘッダーファイルが変更されています。具体的には、以下の3つのファイルが修正されました。

  • src/pkg/runtime/signal_netbsd_386.h
  • src/pkg/runtime/signal_netbsd_amd64.h
  • src/pkg/runtime/signal_netbsd_arm.h

これらのファイルは、それぞれ386、amd64、ARMアーキテクチャにおけるNetBSDのシグナルコンテキスト構造体へのアクセス方法を定義しています。

コミット

  • コミットハッシュ: eb80431b6136663c7a1141a879c89b64fb8ba24f
  • Author: Russ Cox rsc@golang.org
  • Date: Thu Mar 14 17:59:45 2013 -0400
  • コミットメッセージ:
    runtime: fix netbsd after reorg (again)
    
    R=golang-dev
    CC=golang-dev
    https://golang.org/cl/7719046
    

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

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

元コミット内容

runtime: fix netbsd after reorg (again)

R=golang-dev
CC=golang-dev
https://golang.org/cl/7719046

変更の背景

このコミットの背景には、Goランタイムの内部的な再編成("reorg")があります。コミットメッセージにある「reorg (again)」という記述から、以前にも同様の再編成が行われ、その際にNetBSD固有のコードに問題が生じたか、あるいは今回の再編成で再び問題が発生したことが示唆されます。

Go言語のランタイムは、ガベージコレクション、スケジューリング、メモリ管理、そしてシグナルハンドリングといった低レベルな操作をOSに依存せずに効率的に行うための重要なコンポーネントです。OS固有のシグナルハンドリングは、プログラムの異常終了やデバッグ、プロファイリングなどにおいて不可欠な機能であり、OSが提供するシグナルコンテキスト構造体(ucontext_tなど)を正しく解釈する必要があります。

以前のランタイムの再編成により、NetBSDのシグナルハンドリングコードが期待通りに動作しなくなった可能性があります。このコミットは、その再編成によって生じたNetBSDのシグナルハンドリングの不具合を修正することを目的としています。特に、シグナルハンドラ内でレジスタ情報にアクセスするためのマクロの定義が、新しいランタイムの構造に合わせて調整されたと考えられます。

前提知識の解説

1. シグナルハンドリング

シグナルは、Unix系OSにおいてプロセスに非同期的に通知されるイベントです。例えば、Ctrl+Cによる割り込み(SIGINT)、不正なメモリアクセス(SIGSEGV)、子プロセスの終了(SIGCHLD)などがあります。プログラムはこれらのシグナルを捕捉し、特定の処理(シグナルハンドラ)を実行するように設定できます。

2. ucontext_t 構造体

ucontext_tは、POSIX.1-2001で定義されている構造体で、シグナルハンドラが呼び出された時点でのプロセスのコンテキスト(レジスタの状態、シグナルマスク、スタック情報など)を保存するために使用されます。これにより、シグナルハンドラは元の実行コンテキストを復元したり、デバッグ情報にアクセスしたりすることができます。

主要なメンバーは以下の通りです。

  • uc_mcontext: マシン依存のコンテキスト情報(レジスタの状態など)を保持します。これは通常、mcontext_t型です。
  • uc_sigmask: シグナルハンドラが呼び出された時点でのシグナルマスク。
  • uc_stack: シグナルハンドラが使用するスタック情報。
  • uc_link: このコンテキストから復帰する際に使用される次のコンテキストへのポインタ。

3. mcontext_t 構造体

mcontext_tは、ucontext_tの一部であり、CPUのレジスタの状態など、マシンに特化したコンテキスト情報を格納します。この構造体の具体的な定義はOSやアーキテクチャによって異なりますが、通常は汎用レジスタ、セグメントレジスタ、プログラムカウンタ、スタックポインタなどの値が含まれます。

4. Sigcontext (NetBSDにおけるシグナルコンテキスト)

NetBSDでは、mcontext_tの内部に、レジスタ情報を保持する構造体が含まれています。このコミットで言及されているSigcontextは、おそらくNetBSDの特定のバージョンやアーキテクチャにおけるmcontext_tの内部構造、あるいはその一部を指す概念的な名称、または古いバージョンのNetBSDで使われていた型名である可能性があります。重要なのは、uc_mcontextを通じてレジスタ情報にアクセスする際に、その内部構造を正しく解釈する必要があるという点です。

5. GoランタイムとOSシグナル

Goランタイムは、OSのシグナルハンドリング機構を利用して、Goルーチンのスケジューリング、デッドロック検出、プロファイリング、そしてクラッシュ時のスタックトレース出力などを行います。Goのシグナルハンドラは、OSから受け取ったシグナルをGoの内部的なシグナル処理ルーチンに渡し、必要に応じてGoルーチンを停止させたり、特定の処理を実行したりします。この際、OSが提供するucontext_t構造体から、Goランタイムが必要とするレジスタ情報を正確に抽出することが重要になります。

技術的詳細

このコミットの核心は、SIG_REGSマクロの定義変更です。このマクロは、ucontext_t構造体からレジスタ情報を含む部分(uc_mcontext)を抽出するために使用されます。

変更前と変更後の定義は以下の通りです。

変更前:

#define SIG_REGS(ctxt) (*((Sigcontext*)&((Ucontext*)(ctxt))->uc_mcontext))

変更後:

#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext)

この変更は、uc_mcontextの型解釈に関するものです。

  • 変更前: ((Ucontext*)(ctxt))->uc_mcontext の結果を Sigcontext* にキャストし、さらにそのポインタをデリファレンス(*)していました。これは、uc_mcontextが直接Sigcontext型ではないが、そのポインタがSigcontext型として扱える、あるいはSigcontext型へのポインタを返すような状況を想定していた可能性があります。つまり、uc_mcontextSigcontext構造体そのものではなく、その構造体へのポインタ、あるいはその構造体を含むより大きな構造体の一部であると解釈していた可能性があります。

  • 変更後: ((Ucontext*)(ctxt))->uc_mcontext の結果をそのまま使用しています。これは、uc_mcontextがすでにレジスタ情報を含む適切な型(例えば、mcontext_tが直接レジスタ情報にアクセスできる構造体である場合)であるか、あるいはその型がポインタであり、そのポインタが直接レジスタ情報へのアクセスを提供していることを示唆しています。つまり、余分なキャストやデリファレンスが不要になった、あるいは誤っていたことを意味します。

この変更は、NetBSDの特定のバージョンにおけるucontext_tおよびmcontext_tの構造体の定義が、Goランタイムの再編成によって期待されるアクセス方法と一致しなくなったために必要になったと考えられます。Goランタイムの再編成により、uc_mcontextの扱い方が変更されたか、あるいはNetBSDのヘッダーファイルが更新され、uc_mcontextの型が以前のGoランタイムの仮定と異なるようになった可能性があります。

この修正により、GoランタイムはNetBSD上でシグナルを受信した際に、uc_mcontextからレジスタ情報を正しく抽出し、Goルーチンの状態を正確に把握できるようになります。これは、クラッシュ時のスタックトレースの生成や、デバッガによるレジスタ値の検査など、Goプログラムの安定性とデバッグ可能性に直結する重要な修正です。

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

以下の差分は、src/pkg/runtime/signal_netbsd_386.hsrc/pkg/runtime/signal_netbsd_amd64.hsrc/pkg/runtime/signal_netbsd_arm.hの全てで共通です。

--- a/src/pkg/runtime/signal_netbsd_386.h
+++ b/src/pkg/runtime/signal_netbsd_386.h
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.\n
-#define SIG_REGS(ctxt) (*((Sigcontext*)&((Ucontext*)(ctxt))->uc_mcontext))
+#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext)
 
 #define SIG_EAX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EAX])
 #define SIG_EBX(info, ctxt) (SIG_REGS(ctxt).__gregs[REG_EBX])

コアとなるコードの解説

変更前の SIG_REGS マクロ

#define SIG_REGS(ctxt) (*((Sigcontext*)&((Ucontext*)(ctxt))->uc_mcontext))

この定義では、以下のステップでレジスタ情報にアクセスしようとしていました。

  1. ((Ucontext*)(ctxt)): ctxtucontext_tへのポインタ)をUcontext*型にキャストします。
  2. ((Ucontext*)(ctxt))->uc_mcontext: キャストされたポインタからuc_mcontextメンバーにアクセスします。このuc_mcontextmcontext_t型です。
  3. (Sigcontext*)&(...): uc_mcontextのアドレスをSigcontext*型にキャストします。これは、uc_mcontextが直接Sigcontext型ではないが、そのメモリレイアウトがSigcontextと互換性がある、あるいはSigcontextmcontext_tの内部構造の一部であるという仮定に基づいています。
  4. *((Sigcontext*)&(...)): キャストされたポインタをデリファレンスして、Sigcontext構造体そのものにアクセスします。

このアプローチは、uc_mcontextが直接Sigcontext型ではない場合に、型安全性を無視して特定のメモリレイアウトを強制的に解釈しようとするものです。

変更後の SIG_REGS マクロ

#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext)

この定義では、以下のステップでレジスタ情報にアクセスします。

  1. ((Ucontext*)(ctxt)): ctxtUcontext*型にキャストします。
  2. ((Ucontext*)(ctxt))->uc_mcontext: キャストされたポインタからuc_mcontextメンバーにアクセスします。

この変更は、uc_mcontextがすでにレジスタ情報に直接アクセスできる適切な型(例えば、mcontext_tが直接レジスタの配列や構造体を含んでいる場合)であることを示唆しています。つまり、以前のSigcontext*へのキャストとデリファレンスは不要であり、むしろ誤った型解釈を引き起こしていた可能性が高いです。

Goランタイムの再編成により、ucontext_tmcontext_tの扱い方に関する内部的な仮定が変更されたか、あるいはNetBSDのシステムヘッダーの定義が更新され、uc_mcontextの型が以前のGoランタイムの期待と異なるようになったため、このシンプルなアクセス方法が正しくなったと考えられます。この修正により、GoランタイムはNetBSDのシグナルコンテキストからレジスタ情報を正確に読み取れるようになり、シグナルハンドリングが正常に機能するようになりました。

関連リンク

参考にした情報源リンク