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

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

このコミットは、GoランタイムのARMアーキテクチャ向けスタートアップコード(rt0_linux_arm.s)から、OABI (Old Application Binary Interface) のチェック処理を削除するものです。これにより、Thumb命令をサポートしない一部のARM EABI (Embedded Application Binary Interface) システム上でもGoバイナリが実行可能になります。

コミット

commit 6252b41981a5e5566b727de14cda5aece4bee98f
Author: Russ Cox <rsc@golang.org>
Date:   Mon Sep 9 15:06:05 2013 -0400

    runtime: remove OABI check from ARM startup
    
    The code in question is trying to print a nice error message
    when a Go EABI binary runs on an OABI machine.
    Unfortunately, the only way to do that is to use
    ARM Thumb instructions, which we otherwise don't use.
    
    There exist ARM EABI machines that do not support Thumb.
    We could run on them if not for this OABI check, so disable it.
    
    Fixes #5685.
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/13234050

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

https://github.com/golang/go/commit/6252b41981a5e5566b727de14cda5aece4bee98f

元コミット内容

このコミットは、GoランタイムのARMアーキテクチャ向けスタートアップコード(src/pkg/runtime/rt0_linux_arm.s)から、OABI (Old Application Binary Interface) のチェック処理を削除することを目的としています。

元々このコードは、GoのEABI (Embedded Application Binary Interface) バイナリがOABI環境で実行された際に、ユーザーに分かりやすいエラーメッセージを表示しようとしていました。しかし、そのエラーメッセージの表示にはARMのThumb命令セットを使用する必要がありました。Goのランタイムは通常、Thumb命令を使用しないため、このチェックのためだけにThumb命令が導入されていました。

問題は、一部のARM EABIマシンがThumb命令をサポートしていない点にありました。このようなマシンでは、GoのEABIバイナリは本来実行可能であるにもかかわらず、このOABIチェックが存在するために実行できませんでした。このコミットは、この互換性の問題を解決するため、OABIチェックを無効化することで、Thumb命令をサポートしないARM EABIマシン上でのGoバイナリの実行を可能にします。

この変更は、GoのIssue #5685を修正するものです。

変更の背景

この変更の背景には、ARMアーキテクチャにおけるABI(Application Binary Interface)の多様性と、Goランタイムの互換性に関する課題がありました。

Goのバイナリは、ARMアーキテクチャにおいてはEABI(Embedded Application Binary Interface)に準拠してコンパイルされます。EABIは、ARMプロセッサ向けの標準的なABIであり、関数呼び出し規約、データ型のアライメント、レジスタの使用方法などを定義しています。しかし、EABIが普及する以前には、OABI(Old Application Binary Interface)と呼ばれる古いABIが存在していました。

Goのランタイムは、EABIバイナリが誤ってOABI環境で実行された場合に、ユーザーに「このバイナリはOABIでは動作しません」といった親切なエラーメッセージを表示しようと試みていました。このエラーメッセージの表示ロジックは、ARMのThumb命令セットを利用していました。Thumb命令は、ARM命令セットよりもコード密度が高く、組み込みシステムなどで利用されることが多い命令セットです。

しかし、問題は、すべてのARM EABIシステムがThumb命令をサポートしているわけではないという点でした。特に、一部の古いARMv5TEJやARMv6アーキテクチャのプロセッサでは、EABIに準拠しているものの、Thumb命令の実行をサポートしていないか、あるいは特定のモードでのみサポートしていました。このような環境でGoのEABIバイナリを実行しようとすると、OABIチェックのためにThumb命令が実行され、その結果、不正な命令例外(SIGILL)が発生し、Goプログラムがクラッシュしてしまうという問題が発生していました。

Goチームは、Goバイナリが本来動作するはずのEABI環境で、単にOABIチェックのために動作しないという状況を避けるべきだと判断しました。ユーザーに親切なエラーメッセージを表示することよりも、より広範なEABIシステムでの互換性を優先するべきであるという結論に至りました。

この判断に基づき、OABIチェックのコードを削除することで、Thumb命令の実行に依存する部分をなくし、Thumb命令をサポートしないARM EABIシステム上でもGoバイナリが正常に起動できるように変更されました。これにより、GoのARMバイナリの互換性が向上し、より多くのデバイスで利用できるようになりました。

この変更は、具体的にはGoのIssue #5685で報告された問題に対応するものです。

前提知識の解説

このコミットを理解するためには、以下の技術的な概念を理解しておく必要があります。

ARMアーキテクチャとABI (Application Binary Interface)

ARM (Advanced RISC Machine) は、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。ARMプロセッサは、その設計の柔軟性から、様々な実装が存在します。

ABI (Application Binary Interface) は、オペレーティングシステムとアプリケーション、または異なるモジュール間でバイナリレベルでの互換性を保証するための規約のセットです。これには、関数呼び出し規約(引数の渡し方、戻り値の受け取り方)、レジスタの使用規約、データ型のアライメント、システムコール規約などが含まれます。

  • OABI (Old Application Binary Interface): ARMアーキテクチャの初期のLinuxシステムで使われていた古いABIです。特に、ARMv4T以前のプロセッサや、一部の古いディストリビューションで採用されていました。OABIは、EABIと比較して、浮動小数点演算の扱い方や、システムコールの呼び出し規約などに違いがあります。
  • EABI (Embedded Application Binary Interface): ARMアーキテクチャ向けの標準的なABIです。より効率的なコード生成、より良い浮動小数点演算のサポート、より厳密なアライメント規約などを特徴とします。現代のほとんどのARM LinuxシステムやAndroid、iOSなどで採用されています。GoのARMバイナリはEABIに準拠してコンパイルされます。

GoのEABIバイナリがOABI環境で実行されると、ABIの不一致により、システムコールが正しく呼び出せなかったり、データ構造が期待通りに解釈されなかったりして、正常に動作しません。

ARM Thumb命令セット

ARMプロセッサは、主に以下の2つの命令セットをサポートしています。

  • ARM命令セット: 32ビット幅の固定長命令で構成され、高い性能を発揮します。
  • Thumb命令セット: 16ビット幅の可変長命令で構成され、ARM命令セットよりもコード密度が高く、メモリ使用量を削減できます。組み込みシステムやメモリが限られた環境で特に有用です。

多くのARMプロセッサは、ARM命令とThumb命令の両方を実行できますが、プロセッサのモードを切り替える必要があります。また、一部の古いARMプロセッサ(特にARMv5TEJやARMv6の一部)では、Thumb命令の実行をサポートしていないか、特定のモードでのみサポートしている場合があります。

SIGILL (Illegal Instruction Signal)

SIGILL は、プロセスが不正な(または特権のない)CPU命令を実行しようとしたときに、オペレーティングシステムによってプロセスに送信されるシグナルです。このシグナルを受け取ったプロセスは、通常、クラッシュします。

このコミットの文脈では、GoのEABIバイナリがOABIチェックのためにThumb命令を実行しようとした際に、そのThumb命令をサポートしないARM EABIシステム上で SIGILL が発生し、プログラムが異常終了していました。

システムコールとアセンブリ言語

Goのランタイムは、プログラムの起動時やシステムリソースへのアクセス時に、OSの機能を利用するためにシステムコールを直接呼び出すことがあります。これらのシステムコールは、通常、アセンブリ言語で記述されたコードから呼び出されます。

  • rt0_linux_arm.s: GoランタイムのARM Linux向けのスタートアップコードが記述されているアセンブリファイルです。プログラムが起動した際に最初に実行されるコードであり、OSからの引数の受け取り、Goランタイムの初期化、そして最終的にGoのmain関数へのジャンプなどを行います。
  • sys_sigaction: Linuxのシステムコールの一つで、シグナルハンドラを設定するために使用されます。特定のシグナル(例: SIGILL)が発生したときに、どの関数を実行するかをOSに登録します。
  • sys_getpid: Linuxのシステムコールの一つで、現在のプロセスのプロセスID (PID) を取得するために使用されます。このコミットでは、OABIチェックのために意図的にシステムコールを呼び出し、その結果として発生する SIGILL を利用していました。
  • SWI (Software Interrupt): ARMアーキテクチャにおけるソフトウェア割り込み命令です。Linuxでは、システムコールを呼び出すために使用されます。SWI $0 は、通常、システムコール番号を特定のレジスタ(ARMではR7)に設定した後に実行され、OSに制御を渡します。

GoのIssueトラッカー

Goプロジェクトは、GitHubのIssueトラッカー(golang/goリポジトリのIssuesセクション)を使用して、バグ報告、機能リクエスト、議論などを管理しています。Fixes #5685という記述は、このコミットがGoのIssue #5685で報告された問題を解決したことを意味します。

技術的詳細

このコミットの技術的詳細は、GoランタイムのARM Linux向けスタートアップコードである src/pkg/runtime/rt0_linux_arm.s におけるアセンブリコードの変更に集約されます。

変更前のコードは、GoのEABIバイナリがOABI環境で実行された場合に、それを検出し、エラーメッセージを表示するためのロジックを含んでいました。この検出方法は、意図的に不正なシステムコール(OABI環境では不正となるようなシステムコール)を呼び出し、それによって発生する SIGILL シグナルを捕捉するというものでした。SIGILL が発生した場合、事前に設定されたシグナルハンドラ(bad_abi)が実行され、エラーメッセージが表示される仕組みでした。

しかし、この SIGILL を捕捉するためのシグナルハンドラの設定や、その後のシグナルハンドラの復元といった一連の処理が、ARMのThumb命令セットに依存していました。具体的には、BL oabi_syscall<>(SB) のような関数呼び出しが、内部的にThumb命令を使用する可能性がありました。

変更の核心は、このOABIチェックに関連する一連の命令をコメントアウト(削除)した点にあります。

変更前後のコードを比較すると、以下の部分が削除されていることがわかります。

--- a/src/pkg/runtime/rt0_linux_arm.s
+++ b/src/pkg/runtime/rt0_linux_arm.s
@@ -22,31 +22,36 @@ TEXT _rt0_arm_linux1(SB),NOSPLIT,$-4
 
  	// Save argc and argv
  	MOVM.DB.W [R0-R1], (R13)
- 	// set up sa_handler
- 	MOVW	$bad_abi<>(SB), R0 // sa_handler
- 	MOVW	$0, R1 // sa_flags
- 	MOVW	$0, R2 // sa_restorer
- 	MOVW	$0, R3 // sa_mask
- 	MOVM.DB.W [R0-R3], (R13)
- 	MOVW	$4, R0 // SIGILL
- 	MOVW	R13, R1 // sa
- 	SUB	$16, R13
- 	MOVW	R13, R2 // old_sa
- 	MOVW	$8, R3 // c
- 	MOVW	$174, R7 // sys_sigaction
- 	BL	oabi_syscall<>(SB)
+
+ 	// Thumb mode OABI check disabled because there are some
+ 	// EABI systems that do not support Thumb execution.
+ 	// We can run on them except for this check!
+
+ 	// // set up sa_handler
+ 	// MOVW	$bad_abi<>(SB), R0 // sa_handler
+ 	// MOVW	$0, R1 // sa_flags
+ 	// MOVW	$0, R2 // sa_restorer
+ 	// MOVW	$0, R3 // sa_mask
+ 	// MOVM.DB.W [R0-R3], (R13)
+ 	// MOVW	$4, R0 // SIGILL
+ 	// MOVW	R13, R1 // sa
+ 	// SUB	$16, R13
+ 	// MOVW	R13, R2 // old_sa
+ 	// MOVW	$8, R3 // c
+ 	// MOVW	$174, R7 // sys_sigaction
+ 	// BL	oabi_syscall<>(SB)
  
  	// do an EABI syscall
  	MOVW	$20, R7 // sys_getpid
  	SWI	$0 // this will trigger SIGILL on OABI systems
  	
- 	MOVW	$4, R0  // SIGILL
- 	MOVW	R13, R1 // sa
- 	MOVW	$0, R2 // old_sa
- 	MOVW	$8, R3 // c
- 	MOVW	$174, R7 // sys_sigaction
- 	SWI	$0 // restore signal handler
- 	ADD	$32, R13
+ 	// MOVW	$4, R0  // SIGILL
+ 	// MOVW	R13, R1 // sa
+ 	// MOVW	$0, R2 // old_sa
+ 	// MOVW	$8, R3 // c
+ 	// MOVW	$174, R7 // sys_sigaction
+ 	// SWI	$0 // restore signal handler
+ 	// ADD	$32, R13
  
  	SUB	$4, R13 // fake a stack frame for runtime·setup_auxv
  	BL	runtime·setup_auxv(SB)

具体的に削除されたのは以下の処理です。

  1. SIGILL シグナルハンドラの設定:

    • bad_abi<>(SB) をシグナルハンドラとして設定するための sa_handlersa_flagssa_restorersa_mask などの値をスタックにプッシュし、sys_sigaction システムコール(システムコール番号 174)を呼び出して SIGILL (シグナル番号 4) のハンドラを設定する部分。
    • BL oabi_syscall<>(SB) は、このシグナルハンドラ設定の一部として呼び出されていた関数です。
  2. 意図的な SIGILL の発生とシグナルハンドラの復元:

    • sys_getpid システムコール(システムコール番号 20)を SWI $0 で呼び出すことで、OABI環境では不正な命令となり SIGILL を発生させることを期待していました。
    • その後の SIGILL シグナルハンドラを元の状態に戻すための sys_sigaction システムコール呼び出しと、スタックポインタの調整 (ADD $32, R13)。

これらのコードブロックが完全にコメントアウトされたことで、Goランタイムの起動時にThumb命令の実行を伴うOABIチェックが完全にスキップされるようになりました。

結果として、GoのEABIバイナリは、Thumb命令をサポートしないARM EABIシステム上でも、OABIチェックによるクラッシュを回避し、正常に起動できるようになります。OABI環境で実行された場合のエラーメッセージは表示されなくなりますが、GoバイナリがOABI環境で動作しないという根本的な事実は変わらないため、このトレードオフは互換性向上を優先した結果と言えます。

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

--- a/src/pkg/runtime/rt0_linux_arm.s
+++ b/src/pkg/runtime/rt0_linux_arm.s
@@ -22,31 +22,36 @@ TEXT _rt0_arm_linux1(SB),NOSPLIT,$-4
 
  	// Save argc and argv
  	MOVM.DB.W [R0-R1], (R13)
- 	// set up sa_handler
- 	MOVW	$bad_abi<>(SB), R0 // sa_handler
- 	MOVW	$0, R1 // sa_flags
- 	MOVW	$0, R2 // sa_restorer
- 	MOVW	$0, R3 // sa_mask
- 	MOVM.DB.W [R0-R3], (R13)
- 	MOVW	$4, R0 // SIGILL
- 	MOVW	R13, R1 // sa
- 	SUB	$16, R13
- 	MOVW	R13, R2 // old_sa
- 	MOVW	$8, R3 // c
- 	MOVW	$174, R7 // sys_sigaction
- 	BL	oabi_syscall<>(SB)
+
+ 	// Thumb mode OABI check disabled because there are some
+ 	// EABI systems that do not support Thumb execution.
+ 	// We can run on them except for this check!
+
+ 	// // set up sa_handler
+ 	// MOVW	$bad_abi<>(SB), R0 // sa_handler
+ 	// MOVW	$0, R1 // sa_flags
+ 	// MOVW	$0, R2 // sa_restorer
+ 	// MOVW	$0, R3 // sa_mask
+ 	// MOVM.DB.W [R0-R3], (R13)
+ 	// MOVW	$4, R0 // SIGILL
+ 	// MOVW	R13, R1 // sa
+ 	// SUB	$16, R13
+ 	// MOVW	R13, R2 // old_sa
+ 	// MOVW	$8, R3 // c
+ 	// MOVW	$174, R7 // sys_sigaction
+ 	// BL	oabi_syscall<>(SB)
  
  	// do an EABI syscall
  	MOVW	$20, R7 // sys_getpid
  	SWI	$0 // this will trigger SIGILL on OABI systems
  	
- 	MOVW	$4, R0  // SIGILL
- 	MOVW	R13, R1 // sa
- 	MOVW	$0, R2 // old_sa
- 	MOVW	$8, R3 // c
- 	MOVW	$174, R7 // sys_sigaction
- 	SWI	$0 // restore signal handler
- 	ADD	$32, R13
+ 	// MOVW	$4, R0  // SIGILL
+ 	// MOVW	R13, R1 // sa
+ 	// MOVW	$0, R2 // old_sa
+ 	// MOVW	$8, R3 // c
+ 	// MOVW	$174, R7 // sys_sigaction
+ 	// SWI	$0 // restore signal handler
+ 	// ADD	$32, R13
  
  	SUB	$4, R13 // fake a stack frame for runtime·setup_auxv
  	BL	runtime·setup_auxv(SB)

コアとなるコードの解説

このコミットでは、src/pkg/runtime/rt0_linux_arm.s ファイル内のARMアセンブリコードが変更されています。このファイルは、GoプログラムがLinux ARMシステム上で起動する際に最初に実行されるランタイムのスタートアップコードです。

変更の核心は、OABI (Old Application Binary Interface) チェックに関連する一連の命令がコメントアウト(実質的に削除)されたことです。

以下に、変更前後の主要なコードブロックとその役割を解説します。

変更前のコード(削除された部分):

 	// set up sa_handler
 	MOVW	$bad_abi<>(SB), R0 // sa_handler
 	MOVW	$0, R1 // sa_flags
 	MOVW	$0, R2 // sa_restorer
 	MOVW	$0, R3 // sa_mask
 	MOVM.DB.W [R0-R3], (R13)
 	MOVW	$4, R0 // SIGILL
 	MOVW	R13, R1 // sa
 	SUB	$16, R13
 	MOVW	R13, R2 // old_sa
 	MOVW	$8, R3 // c
 	MOVW	$174, R7 // sys_sigaction
 	BL	oabi_syscall<>(SB)
  • このブロックは、SIGILL (シグナル番号 4) のシグナルハンドラを設定するためのものです。
  • MOVW $bad_abi<>(SB), R0: bad_abi というラベル(おそらくOABIエラーメッセージを表示する関数)のアドレスをレジスタ R0 にロードし、シグナルハンドラとして設定します。
  • MOVW $0, R1 / MOVW $0, R2 / MOVW $0, R3: sa_flagssa_restorersa_mask をそれぞれ0に設定します。これらは sigaction システムコールの引数です。
  • MOVM.DB.W [R0-R3], (R13): レジスタ R0 から R3 の内容をスタックポインタ R13 が指すアドレスに書き込みます。これは sigaction 構造体の一部をスタックに準備しています。
  • MOVW $4, R0 // SIGILL: シグナル番号 4 (SIGILL) を R0 に設定します。
  • MOVW R13, R1 // sa: スタックポインタ R13R1 に設定します。これは sigaction 構造体へのポインタとして渡されます。
  • SUB $16, R13: スタックポインタを16バイト減らします。これは old_sa 引数のためのスペースを確保しています。
  • MOVW R13, R2 // old_sa: 新しいスタックポインタ R13R2 に設定します。これは古い sigaction 構造体を保存するためのポインタです。
  • MOVW $8, R3 // c: sigaction システムコールの第4引数(サイズなど)を設定します。
  • MOVW $174, R7 // sys_sigaction: システムコール番号 174 (Linuxの sys_sigaction) を R7 に設定します。
  • BL oabi_syscall<>(SB): oabi_syscall という関数を呼び出します。この関数は、OABI環境で不正な命令となるようなシステムコールを意図的に実行し、SIGILL を発生させることを目的としていました。この呼び出し自体がThumb命令を使用する可能性がありました。
 	MOVW	$4, R0  // SIGILL
 	MOVW	R13, R1 // sa
 	MOVW	$0, R2 // old_sa
 	MOVW	$8, R3 // c
 	MOVW	$174, R7 // sys_sigaction
 	SWI	$0 // restore signal handler
 	ADD	$32, R13
  • このブロックは、SIGILL シグナルハンドラを元の状態に戻すためのものです。
  • SWI $0: システムコールを実行します。ここでは sys_sigaction を再度呼び出し、シグナルハンドラをデフォルトに戻すか、あるいは無効化します。
  • ADD $32, R13: スタックポインタを32バイト増やし、シグナルハンドラ設定のために使用したスタック領域を解放します。

変更後のコード:

変更後のコードでは、上記の2つのブロックが完全にコメントアウトされています。

 	// Thumb mode OABI check disabled because there are some
 	// EABI systems that do not support Thumb execution.
 	// We can run on them except for this check!

 	// // set up sa_handler
 	// MOVW	$bad_abi<>(SB), R0 // sa_handler
 	// MOVW	$0, R1 // sa_flags
 	// MOVW	$0, R2 // sa_restorer
 	// MOVW	$0, R3 // sa_mask
 	// MOVM.DB.W [R0-R3], (R13)
 	// MOVW	$4, R0 // SIGILL
 	// MOVW	R13, R1 // sa
 	// SUB	$16, R13
 	// MOVW	R13, R2 // old_sa
 	// MOVW	$8, R3 // c
 	// MOVW	$174, R7 // sys_sigaction
 	// BL	oabi_syscall<>(SB)
  • この部分全体がコメントアウトされ、SIGILL シグナルハンドラの設定処理が完全にスキップされます。これにより、Thumb命令の実行に依存する部分がなくなります。
 	// MOVW	$4, R0  // SIGILL
 	// MOVW	R13, R1 // sa
 	// MOVW	$0, R2 // old_sa
 	// MOVW	$8, R3 // c
 	// MOVW	$174, R7 // sys_sigaction
 	// SWI	$0 // restore signal handler
 	// ADD	$32, R13
  • この部分もコメントアウトされ、シグナルハンドラの復元処理とスタックポインタの調整がスキップされます。

残されたコード:

 	// do an EABI syscall
 	MOVW	$20, R7 // sys_getpid
 	SWI	$0 // this will trigger SIGILL on OABI systems
  • この部分は残されています。sys_getpid (システムコール番号 20) を呼び出すEABIシステムコールです。コメントには「this will trigger SIGILL on OABI systems」とありますが、これはOABI環境で実行された場合に、このEABIシステムコールが不正な命令として解釈され、SIGILL を発生させることを示唆しています。しかし、シグナルハンドラが設定されていないため、この SIGILL は捕捉されず、プログラムはクラッシュします。これは、OABI環境での実行を意図的にサポートしないというGoの姿勢を反映しています。

結論として、この変更は、Goランタイムが起動時にThumb命令を使用するOABIチェックを完全に削除することで、Thumb命令をサポートしないARM EABIシステム上でのGoバイナリの互換性を向上させるものです。 OABI環境での「親切な」エラーメッセージは失われますが、より広範なEABIシステムでの動作が保証されることになります。

関連リンク

参考にした情報源リンク