GraalVMとネイティブコードの連携

GraalVM LLVMランタイムを使用すると、従来はネイティブコードに直接コンパイルされる言語で記述されたコードを実行できます。これらの言語は通常、実行にマネージドランタイムやVMを必要としません。そのため、このコードとGraalVMのマネージドランタイムの連携を考慮する際には、特に低レベル機能を使用している場合は、特別な注意が必要です。

低レベルシステムコールへのアクセス制限 #

  • シグナル処理は、以下の前提に基づいて実行されます。
    • マネージドランタイムは、すべてのシグナルの処理を完全に制御していることを前提としています。
    • インストールされたシグナルハンドラは、ネイティブ実行時とは異なる動作をする可能性があります。
  • プロセス制御とスレッド処理は、以下の側面に基づいて実行されます。
    • GraalVMは、スレッド処理を完全に制御していることを前提としています。
    • マルチスレッドは、pthreadsライブラリ(例:pthread_create)を介してサポートされています。
    • cloneforkvforkなど、プロセス関連のシステムコールを直接使用することはサポートされていません。
    • exec関数ファミリはサポートされていません。

メモリレイアウト #

GraalVMで実行されるプロセスのメモリとスタックのレイアウトは、直接ネイティブ実行時とは異なります。特に、グローバル変数、スタック変数などの相対的な位置については、何も仮定できません。

スタックのウォークは、GraalVM APIのみを使用して可能です。コードとデータの間には厳格な分離があります。自己修正コードは機能しません。コードへのポインタに対する読み取り、書き込み、ポインタ演算はサポートされていません。

ネイティブモードでのシステムライブラリとの連携 #

ネイティブ実行モード(デフォルトモード)では、GraalVM LLVMランタイムで実行されるコードは、実際のネイティブライブラリ(たとえば、システムライブラリ)を呼び出すことができます。これらの呼び出しは、JavaでのJNI呼び出しと同様に動作します。つまり、一時的にマネージド実行環境を離れます。

これらのライブラリで実行されるコードはGraalVMの制御下にはないため、基本的にあらゆる操作を実行できます。特に、マルチコンテキストの分離は適用されず、仮想ファイルシステムなどのGraalVM APIはバイパスされます。

これは、標準Cライブラリのほとんどに特に当てはまります。

マネージド実行モード #

マネージドモード(--llvm.managedオプションで有効化)は、LLVMランタイムが他のGraalVM対応言語と同様に純粋にマネージドモードで実行される特殊な実行モードです。

注:マネージドモードはOracle GraalVMでのみ利用可能です。

このモードでは、設計上、ネイティブコードを呼び出してネイティブメモリにアクセスすることは許可されていません。すべてのメモリはガベージコレクタによって管理され、実行する必要があるすべてのコードはビットコードにコンパイルする必要があります。

ポインタ演算は、C標準で許可されている範囲でのみ可能です。特に、オーバーフローは防止され、境界外アクセスによって異なる割り当てにアクセスすることはできません。このような無効なアクセスはすべて、未定義の動作ではなく、ランタイム例外になります。

マネージドモードでは、GraalVMは仮想Linux/AMD64オペレーティングシステムをシミュレートし、musl libclibc++をC/C++標準ライブラリとして使用します。すべてのコードはそのシステム用にコンパイルする必要があり、その後、GraalVMでサポートされている任意のアーキテクチャまたはオペレーティングシステムで実行できます。システムコールは仮想化され、適切なGraalVM APIを介してルーティングされます。

ネイティブコードとマネージド言語間のポリグロット連携 #

LLVM言語(例:C/C++)とマネージド言語(例:JavaScript、Python、Ruby)間のポリグロット相互運用性を使用する場合は、手動メモリ管理に注意する必要があります。このセクションは、ネイティブ実行モード(デフォルト)にのみ適用されます。マネージドモード(--llvm.managedオプションで有効化され、Oracle GraalVMでのみ利用可能)では、LLVMランタイム自体はマネージド言語のように動作し、ポリグロットの相互作用は他のマネージド言語間の相互作用と同じです。

  • 考慮すべきガベージコレクションポリシー
    • マネージド言語のオブジェクトへのポインタはガベージコレクタによって管理されるため、手動で解放する必要はありません。
    • 一方、LLVMコードからの割り当てへのポインタ(例:malloc)はガベージコレクタの制御下にはないため、手動で解放する必要があります。
  • 考慮すべきアンマネージドヒープポリシー
    • ネイティブメモリ(例:malloc、データセクション、スレッドローカル)は、ガベージコレクタの制御下にはありません。
    • ガベージコレクタによって制御される外部オブジェクトへのポインタは、ネイティブメモリに直接格納することはできません。
    • この制限を回避するために、ハンドル関数が用意されています(graalvm/llvm/handles.hを参照)。

お問い合わせ