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

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

このコミットは、GoランタイムのAndroid向けビルドにおいて、Goプログラムの初期エントリポイントである _rt0 シンボルを外部にエクスポートするための変更です。具体的には、src/pkg/runtime/os_android.c という新しいファイルが追加され、Android環境でGoランタイムを共有ライブラリとして利用する際の初期化を可能にします。

コミット

commit c1c8c3c8c4194de357d1be925b2b70f6fc88b72e
Author: David Crawshaw <david.crawshaw@zentus.com>
Date:   Mon Jul 7 07:35:39 2014 -0400

    runtime: export _rt0 for android
    
    LGTM=iant, minux
    R=golang-codereviews, minux, iant
    CC=golang-codereviews
    https://golang.org/cl/109470043

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

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

元コミット内容

 src/pkg/runtime/os_android.c | 16 ++++++++++++++++\n 1 file changed, 16 insertions(+)\n\ndiff --git a/src/pkg/runtime/os_android.c b/src/pkg/runtime/os_android.c\nnew file mode 100644\nindex 0000000000..58e0dac939\n--- /dev/null\n+++ b/src/pkg/runtime/os_android.c\n@@ -0,0 +1,16 @@\n+// Copyright 2014 The Go Authors. All rights reserved.\n+// Use of this source code is governed by a BSD-style\n+// license that can be found in the LICENSE file.\n+\n+#include \"runtime.h\"\n+#include \"defs_GOOS_GOARCH.h\"\n+#include \"os_GOOS.h\"\n+\n+// Export the runtime entry point symbol.\n+//\n+// Used by the app package to start the Go runtime after loading\n+// a shared library via JNI. See code.google.com/p/go.mobile/app.\n+\n+void _rt0_arm_linux1();\n+#pragma cgo_export_static _rt0_arm_linux1\n+#pragma cgo_export_dynamic _rt0_arm_linux1\n```

## 変更の背景

この変更の背景には、Go言語で開発されたコードをAndroidアプリケーションに組み込むという、当時のGoモバイル開発の初期の取り組みがあります。通常、Goプログラムは単一の実行可能バイナリとしてコンパイルされ、`_rt0`という低レベルのエントリポイントから起動し、Goランタイムの初期化を経てユーザーの`main`関数が実行されます。

しかし、Androidアプリケーションは主にJava(またはKotlin)で開発され、ネイティブコード(C/C++など)を組み込む場合は、共有ライブラリ(`.so`ファイル)としてビルドし、Java Native Interface (JNI) を介してロード・実行するのが一般的です。

GoコードをAndroidアプリに組み込む際、Goランタイム自体を共有ライブラリとしてビルドし、AndroidアプリからJNI経由でそのランタイムを起動する必要が生じました。この起動プロセスにおいて、Goランタイムの初期エントリポイントである `_rt0` をAndroid側から呼び出せるようにエクスポートする必要がありました。

コミットメッセージにある「Used by the app package to start the Go runtime after loading a shared library via JNI. See code.google.com/p/go.mobile/app.」という記述は、この変更が `go.mobile/app` パッケージ(Goでモバイルアプリケーションを開発するための実験的なパッケージ)の要件を満たすために行われたことを明確に示しています。これにより、GoコードがAndroidアプリのネイティブ部分として機能し、Java/Kotlinコードと連携できるようになります。

## 前提知識の解説

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

*   **Goランタイム (Go Runtime)**: Goプログラムの実行環境全体を指します。これには、ガベージコレクタ、スケジューラ(ゴルーチンの管理)、メモリ管理、システムコールインターフェースなどが含まれます。Goプログラムが実行される前に、このランタイムが適切に初期化される必要があります。
*   **`_rt0`**: Goプログラムの最も低レベルなエントリポイントです。C言語の`main`関数に相当すると思われがちですが、`main`関数よりもさらに前に実行されるアセンブリコードのルーチンです。`_rt0`の主な役割は、Goランタイムが動作するために必要な初期設定(スタックのセットアップ、コマンドライン引数の処理、`runtime.rt0_go`の呼び出しなど)を行うことです。このルーチンが完了すると、Goランタイムの本格的な初期化が始まり、最終的にユーザーの`main`関数がゴルーチンとして起動されます。
*   **Android NDK (Native Development Kit)**: AndroidアプリケーションでC、C++、およびその他の言語で書かれたネイティブコードを使用できるようにするツールセットです。NDKを使用することで、開発者はパフォーマンスが重要な部分や既存のネイティブライブラリを再利用する際にネイティブコードを利用できます。
*   **JNI (Java Native Interface)**: Java仮想マシン (JVM) 上で動作するJavaコードが、ネイティブアプリケーションやライブラリ(C/C++など)と相互運用するためのプログラミングフレームワークです。JNIを使用すると、Javaコードからネイティブ関数を呼び出したり、ネイティブコードからJavaオブジェクトを操作したりできます。Androidアプリでは、Java/KotlinコードがJNIを介してネイティブな共有ライブラリをロードし、その中の関数を呼び出します。
*   **共有ライブラリ (Shared Library)**: 複数のプログラムから共有して利用できるライブラリの形式です。WindowsではDLL (Dynamic Link Library)、LinuxやAndroidでは`.so` (Shared Object) ファイルとして知られています。プログラムの実行時に動的にロードされ、メモリを節約し、モジュール性を高める利点があります。
*   **Cgo**: Go言語とC言語の相互運用を可能にするGoのツールです。Cgoを使用すると、GoコードからC関数を呼び出したり、CコードからGo関数を呼び出したりできます。Goのビルドプロセスの一部として機能し、GoとCの間のブリッジコードを生成します。
*   **`#pragma cgo_export_static` および `#pragma cgo_export_dynamic`**: これらはCgoの特殊なディレクティブで、Goランタイムの内部でGoのシンボル(関数など)をCコードから呼び出せるようにエクスポートするために使用されます。
    *   `#pragma cgo_export_static`: Goのシンボルを静的にエクスポートします。Goプログラムが静的ライブラリ (`-buildmode=c-archive`) や実行可能ファイルとしてコンパイルされる場合、このシンボルは最終的なバイナリに直接埋め込まれます。
    *   `#pragma cgo_export_dynamic`: Goのシンボルを動的にエクスポートします。Goプログラムが共有ライブラリ (`-buildmode=c-shared`) としてコンパイルされる場合、このシンボルは動的リンクのために利用可能になります。
    これらのディレクティブは、通常、ユーザーがGo関数をCにエクスポートする際に使用する `//export FunctionName` とは異なり、Goランタイムの内部的な連携のために使われます。

## 技術的詳細

このコミットの技術的な核心は、Goランタイムの初期化プロセスをAndroidの共有ライブラリのコンテキストに適合させる点にあります。

1.  **Goプログラムの起動シーケンス**:
    通常のGoプログラムは、OSによってロードされると、まず `_rt0` というアセンブリコードのエントリポイントに制御が渡されます。`_rt0` は、OSから渡された引数(`argc`, `argv`)を処理し、スタックを設定し、`runtime.rt0_go` 関数を呼び出します。`runtime.rt0_go` は、Goランタイムの主要な初期化(ガベージコレクタ、スケジューラ、メモリ割り当て器などの設定)を行い、最終的にユーザーが書いた `main` 関数を新しいゴルーチンとして起動します。

2.  **AndroidでのGoの課題**:
    Androidアプリケーションは、Java/Kotlinコードがメインであり、Goコードを組み込む場合は、Goコードを共有ライブラリ(`.so`ファイル)としてビルドし、JNIを介してJava/Kotlinコードからロード・実行する必要があります。このシナリオでは、OSが直接 `_rt0` を呼び出すわけではありません。代わりに、Java/KotlinコードがJNIを通じて共有ライブラリをロードし、その中の特定のエントリポイント関数を呼び出すことになります。

3.  **`_rt0` のエクスポートの必要性**:
    Goランタイムを適切に初期化するためには、`_rt0` が実行される必要があります。Androidの共有ライブラリとしてGoコードをビルドする場合、Java/Kotlin側からGoランタイムの初期化を開始するための「フック」が必要です。このフックとして、Goランタイムの `_rt0` を外部から呼び出せるようにエクスポートすることが選択されました。

4.  **`_rt0_arm_linux1` シンボル**:
    コミットでエクスポートされている `_rt0_arm_linux1` は、Goランタイムの `_rt0` の具体的な実装の一つです。`arm` はARMアーキテクチャ(Androidデバイスで一般的)、`linux` はLinuxカーネルベースのOS(Androidの基盤)、`1` は特定のバージョンやバリアントを示唆している可能性があります。このシンボルは、GoランタイムがAndroidのARM Linux環境で起動するための特定のエントリポイントとして機能します。

5.  **`#pragma cgo_export_static` と `#pragma cgo_export_dynamic` の利用**:
    `src/pkg/runtime/os_android.c` 内で `_rt0_arm_linux1` に対してこれらの `pragma` ディレクティブが使用されているのは、GoランタイムがAndroid向けにビルドされる際に、この `_rt0` シンボルが静的リンクと動的リンクの両方で外部から参照可能になるようにするためです。
    *   `#pragma cgo_export_static _rt0_arm_linux1`: Goランタイムが静的ライブラリとしてビルドされる場合でも、`_rt0_arm_linux1` シンボルがバイナリに含められ、外部から参照できるようにします。
    *   `#pragma cgo_export_dynamic _rt0_arm_linux1`: Goランタイムが共有ライブラリとしてビルドされる場合、`_rt0_arm_linux1` シンボルが動的リンクテーブルに登録され、実行時に外部からロード・呼び出しが可能になります。
    これにより、GoランタイムがAndroidアプリケーションに組み込まれる際の柔軟性が確保されます。Androidアプリは、JNIを通じてこのエクスポートされた `_rt0_arm_linux1` 関数を呼び出すことで、Goランタイムの初期化プロセスを開始し、Goコードの実行環境を確立できるようになります。

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

このコミットで追加されたファイル `src/pkg/runtime/os_android.c` の全内容がコアとなる変更箇所です。

```c
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"

// Export the runtime entry point symbol.
//
// Used by the app package to start the Go runtime after loading
// a shared library via JNI. See code.google.com/p/go.mobile/app.

void _rt0_arm_linux1();
#pragma cgo_export_static _rt0_arm_linux1
#pragma cgo_export_dynamic _rt0_arm_linux1

コアとなるコードの解説

  • // Copyright 2014 The Go Authors. All rights reserved.: 標準的なGoプロジェクトの著作権表示です。
  • #include "runtime.h": GoランタイムのC部分で利用される共通のヘッダファイルです。
  • #include "defs_GOOS_GOARCH.h": ビルドターゲットのOS (GOOS) とアーキテクチャ (GOARCH) に応じた定義を含むヘッダファイルです。この場合、GOOSandroidGOARCHarmに関連する定義が含まれます。
  • #include "os_GOOS.h": ターゲットOS (GOOS) に特化した定義を含むヘッダファイルです。この場合、os_android.h に相当する内容がインクルードされます。
  • コメントブロック:
    // Export the runtime entry point symbol.
    //
    // Used by the app package to start the Go runtime after loading
    // a shared library via JNI. See code.google.com/p/go.mobile/app.
    
    このコメントは、このファイルが _rt0 シンボルをエクスポートする目的を明確に説明しています。GoランタイムがJNIを介して共有ライブラリとしてロードされた後、go.mobile/app パッケージによってGoランタイムを起動するために使用されることを示しています。
  • void _rt0_arm_linux1();: これは、_rt0_arm_linux1 という名前の関数が宣言されています。void は戻り値がないことを示し、引数もありません。この宣言自体は関数のプロトタイプであり、実際の _rt0_arm_linux1 の実装はGoランタイムの他のアセンブリファイルに存在します。この行の目的は、このシンボルがCgoによって認識され、エクスポートの対象となるようにすることです。
  • #pragma cgo_export_static _rt0_arm_linux1: このCgoディレクティブは、_rt0_arm_linux1 シンボルを静的にエクスポートするようコンパイラに指示します。これにより、Goランタイムが静的ライブラリとしてビルドされる場合でも、このシンボルが外部から参照可能になります。
  • #pragma cgo_export_dynamic _rt0_arm_linux1: このCgoディレクティブは、_rt0_arm_linux1 シンボルを動的にエクスポートするようコンパイラに指示します。これにより、Goランタイムが共有ライブラリとしてビルドされる場合、このシンボルが動的リンクテーブルに登録され、実行時に外部からロード・呼び出しが可能になります。

これらの行が組み合わさることで、Goランタイムの初期エントリポイントがAndroid環境のネイティブコード(JNI経由でJava/Kotlinから呼び出される)に対して公開され、GoアプリケーションがAndroidプラットフォーム上で適切に起動・実行されるための基盤が提供されます。

関連リンク

参考にした情報源リンク