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

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

このコミットは、GoランタイムのARMアーキテクチャ向けアセンブリコードにおけるビルド問題を修正するものです。具体的には、古いARMプロセッサでの互換性を維持するため、BLX命令をBL命令に置き換える変更が行われました。

コミット

  • コミットハッシュ: dc48e9516c3b46f061af16f595a7c7dcb46434d1
  • Author: Elias Naur elias.naur@gmail.com
  • Date: Wed Aug 14 13:50:12 2013 -0400
  • 変更ファイル: src/pkg/runtime/cgo/gcc_arm.S

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

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

元コミット内容

    runtime: Fix build on older ARM
    
            The ARM external linking CL used BLX instructions in gcc assembler. Replace with BL to retain support on older ARM processors.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/12938043

変更の背景

このコミットは、Goランタイムが古いARMプロセッサ上で正しくビルドできない問題を解決するために導入されました。問題の原因は、以前の「ARM external linking CL (Change List)」で導入されたBLX命令の使用にありました。BLX命令は、一部の古いARMプロセッサではサポートされていないか、あるいは異なる動作をする可能性があり、これがビルドエラーや予期せぬ動作を引き起こしていました。

Go言語は、幅広いアーキテクチャとプラットフォームをサポートすることを目指しており、古いハードウェアに対する互換性も重要な考慮事項です。この修正は、Goがより多くのARMベースのデバイスで動作できるようにするための互換性維持の一環として行われました。

前提知識の解説

ARMアーキテクチャとアセンブリ言語

ARM (Advanced RISC Machine) は、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。Go言語のランタイムは、パフォーマンスが重要な部分やハードウェアに直接アクセスする必要がある部分でアセンブリ言語を使用します。アセンブリ言語は、CPUが直接実行できる機械語に非常に近い低レベルのプログラミング言語であり、特定のCPUアーキテクチャに特化しています。

ARM命令セット: ARMとThumb

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

  1. ARM命令セット: 32ビット幅の命令で構成され、高いパフォーマンスを提供します。
  2. Thumb命令セット: 16ビット幅の命令で構成され、コードサイズを削減し、メモリ効率を高めます。一部のThumb-2命令は32ビット幅です。

ARMプロセッサは、これらの命令セット間を切り替えて実行することができます。この切り替えは「インターワーキング (interworking)」と呼ばれます。

BL命令は、サブルーチン呼び出しに使用される命令です。

  • 機能: 指定されたアドレスにジャンプ(分岐)し、現在のプログラムカウンタ (PC) の次の命令のアドレスをリンクレジスタ (LR, R14) に保存します。これにより、サブルーチンが終了した後に元の実行フローに戻ることができます。
  • 命令セットの変更: BL命令は、命令セットの状態(ARMモードかThumbモードか)を変更しません。ARMモードでBLを実行すればARMモードのまま、ThumbモードでBLを実行すればThumbモードのままです。
  • 用途: 主に、呼び出し元と呼び出し先が同じ命令セットを使用している場合に用いられます。

BLX命令もサブルーチン呼び出しに使用されますが、BL命令に加えて命令セットの切り替え機能を持っています。

  • 機能: BLと同様に、指定されたアドレスにジャンプし、戻りアドレスをLRに保存します。
  • 命令セットの変更: BLXの最大の特徴は、命令セットの状態を切り替えることができる点です。
    • ラベル付きBLX: BLX命令が直接ラベル(アドレス)を指定する場合、常に命令セットを切り替えます(ARMからThumbへ、またはThumbからARMへ)。
    • レジスタ付きBLX: BLX命令がレジスタを指定する場合(例: BLX Rm)、レジスタの下位ビット(通常はビット0)によって、ターゲットの命令セット(ARMまたはThumb)が決定されます。これにより、ARMコードからThumbコードを呼び出したり、その逆を行ったりする「インターワーキング」が可能になります。
  • 用途: ARMとThumb命令セット間で頻繁に切り替える必要がある場合に特に有用です。

古いARMプロセッサとBLX

一部の古いARMプロセッサや特定のARMv5以前のアーキテクチャでは、BLX命令が完全にサポートされていないか、あるいはその動作が期待通りではない場合があります。特に、BLX命令が導入される前のARMv4Tアーキテクチャなどでは、BLX命令が存在しないため、これを使用するとビルドエラーや実行時エラーが発生します。

技術的詳細

このコミットの核心は、GoランタイムのCgo(C言語との連携)部分で使用されているARMアセンブリコードの修正です。具体的には、src/pkg/runtime/cgo/gcc_arm.Sファイル内のcrosscall_arm2という関数内でBLX命令が使用されていました。

crosscall_arm2は、GoとCの間の呼び出し(クロス呼び出し)を処理するためのアセンブリルーチンです。このルーチンは、GoのランタイムがC関数を呼び出す際、またはC関数がGoの関数を呼び出す際に、レジスタの状態を適切に設定し、呼び出しを行う役割を担っています。

元のコードでは、setmg(m, g)(GoのMとG、つまりOSスレッドとゴルーチンをセットする関数)とfn()(実際のCまたはGoの関数)の呼び出しにBLX命令が使われていました。これは、GoのランタイムがCのコードと連携する際に、命令セットの切り替えが必要になる可能性があるため、BLXが選択されたと考えられます。

しかし、前述の通り、BLX命令はすべてのARMプロセッサで完全にサポートされているわけではありません。特に古いARMプロセッサでは、この命令が存在しないか、あるいは異なる動作をするため、ビルド時にアセンブラがBLX命令を認識できずにエラーを発生させたり、実行時に不正な命令として扱われたりする問題が発生していました。

この修正では、BLX命令をBL命令に置き換えることで、この互換性の問題を解決しています。BL命令は、より基本的な分岐命令であり、ほとんどすべてのARMプロセッサでサポートされています。この変更により、Goランタイムは古いARMプロセッサでも正しくビルドされ、実行できるようになります。

この修正が問題なく機能するのは、crosscall_arm2ルーチンが呼び出すsetmgfnが、命令セットの切り替えを必要としない、あるいは呼び出し元と呼び出し先が常に同じ命令セットで動作することを前提としているためと考えられます。もし命令セットの切り替えが必須であれば、BLへの変更は機能的な問題を引き起こす可能性がありますが、このケースではそうではないと判断されたのでしょう。

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

diff --git a/src/pkg/runtime/cgo/gcc_arm.S b/src/pkg/runtime/cgo/gcc_arm.S
index e380a0f6db..7cf91f9ffa 100644
--- a/src/pkg/runtime/cgo/gcc_arm.S
+++ b/src/pkg/runtime/cgo/gcc_arm.S
@@ -25,8 +25,8 @@ EXT(crosscall_arm2):\n 	mov r5, r1\n 	mov r0, r2\n 	mov r1, r3\n-\tblx r5 // setmg(m, g)\n-\tblx r4 // fn()\n+\tbl r5 // setmg(m, g)\n+\tbl r4 // fn()\n 	pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}\n \n .globl EXT(__stack_chk_fail_local)\n```

## コアとなるコードの解説

変更は`src/pkg/runtime/cgo/gcc_arm.S`ファイル内の`crosscall_arm2`アセンブリルーチンにあります。

元のコード:
```assembly
-	blx r5 // setmg(m, g)
-	blx r4 // fn()

修正後のコード:

+	bl r5 // setmg(m, g)
+	bl r4 // fn()

この変更は非常にシンプルで、blx命令がbl命令に置き換えられています。

  • mov r5, r1mov r0, r2mov r1, r3 は、関数呼び出しの引数をレジスタに設定しています。ARMのAAPCS (ARM Architecture Procedure Call Standard) に従い、最初の4つの引数はr0からr3に渡されます。ここでは、r5r4に格納されたアドレス(それぞれsetmgfnのアドレス)を呼び出す準備をしています。
  • bl r5 // setmg(m, g): r5レジスタに格納されているアドレス(setmg関数のエントリポイント)に分岐し、戻りアドレスをLRに保存します。コメントが示すように、これはGoのM(OSスレッド)とG(ゴルーチン)を現在のコンテキストに設定する関数を呼び出しています。
  • bl r4 // fn(): r4レジスタに格納されているアドレス(実際のCまたはGoの関数)に分岐し、戻りアドレスをLRに保存します。コメントが示すように、これは実際のターゲット関数を呼び出しています。
  • pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}: 呼び出し規約に従って、サブルーチンが使用したレジスタをスタックから復元し、最後にpc(プログラムカウンタ)をポップすることで、呼び出し元に戻ります。

この変更により、BLX命令がサポートされていない古いARMプロセッサでも、これらの関数呼び出しが正しく行われるようになり、Goランタイムのビルドと実行が可能になります。この修正は、命令セットの切り替えがこの特定のコンテキストでは不要であるか、あるいは他の方法で処理されていることを示唆しています。

関連リンク

  • Go CL 12938043: https://golang.org/cl/12938043

参考にした情報源リンク