(2023/10/18)
[連載18] ローダー機能ー2
前回から、SOLID-OSの持つ「ローダー機能」についてご紹介しています。
今回は、DLL(Dynamic Link Library)方式のローダー機能を動かしてみていきます。
OSや基幹部となるアプリ等とは別に、アプリ入れ替え可能な領域が存在します。
下の図で、紫色の部分です。
ローダー機能により、例えばアプリAをロードするとします。
以降はアプリAの関数をコールしたり、アプリAが基幹部分の関数をコールしたりすることができます。
さらに、複数アプリをロードすることも可能です。
両アプリとも、先ほどと同様、相互呼び出しが可能です。
DLL方式の場合、入れ替え対象のアプリは、ビルドの際にはアドレスが決定しません。
ロード時にアドレスが確定します。
今回は、そのDLL方式のアプリをロードしてみます。
ロードする対象のアプリは、メモリファイルシステムに置くことにします。
わざわざファイルシステムを導入する必要がないため、簡単に使えます。
では早速試してみましょう。
以下URLに沿って試してみます。
https://solid.kmckk.com/SOLID/doc/latest/tutorial/create_loadable_app_project.html#id10
上記URLの説明で、開発会社と協力会社という言葉が出てきます。
・開発会社:基幹となる部分を開発する担当
・協力会社:入れ替え対象のアプリを開発する担当
と解釈して進めます。
今回も、Cortex-A9搭載ボード×PARTNER Jet2の組み合わせを使用します。
準備すること:
① メモリマップの設定
② ロードを行う方のプログラムとファイルシステムを用意
③ ローダブルアプリケーションのソリューションを生成
順番に行っていきましょう。
① メモリマップの設定
アプリをロードする場所を決めます。
DLL形式なので、ここで決めた場所にアプリがロードされるのですが、具体的なロード先のアドレスはSOLID-OSが決めます。
このため、ここでは、ロードに使っていいよ、という場所を抑えるだけでOKです。
[メモリマップデザイナ]ウインドウを見てみましょう。
以前スナップショット機能用に確保した領域がありましたね。
黄色い部分です。
今回はスナップショット使わないので、削除してここを開けましょう。
アプリをロードする場所「DLLAREA」を設定します。
仮想アドレス:0x20000000
サイズ:32Mbyte
の領域を確保しました。
AttributeはSOLID_RESERVE(予約済アドレス空間)とします。
② ロードを行う方のプログラムとファイルシステムを用意
ロードを行えるようにするためのソースコードは以下を参考にします。
https://solid.kmckk.com/SOLID/doc/latest/tutorial/create_loadable_app_project.html#id45
ロード対象アプリのファイル名が必要ですね。
ここで、メモリファイルシステムが大活躍!します。
メモリファイルシステムとは:
https://solid.kmckk.com/SOLID/doc/latest/os/filesystem.html#id3
抜粋します。
-----
SOLIDでのファイルシステムは、メモリの一部をファイルの内容としてアクセスする、 簡易的なファイルシステムが実装されています。
指定するファイルパスは、\ROM\<アドレス>,<サイズ> を指定します。 アドレスおよびサイズは16進数で指定してください。
-----
すなわち、
ファイル名は、\ROM\<アドレス>, <サイズ>でOK!
(ROMって書いてますが、RAMでも使えます)
ファイルシステムは、わざわざ導入する必要ありません。
もちろん、既にファイルシステムがあるよ、というシステムの場合、そのファイルシステムを使う事も可能です。
さらに、デバッグ時だけなんだけど?という場合、VLINKといってJTAGデバッガ PARTNER-Jetを介してPCを簡易的に外付けファイルシステムとすることもできます。
とにかく、今回は一番簡単な、メモリファイルシステムを使います。
という事で、メモリファイルシステムとしてDLLを置く場所を作ってあげる必要がありますね。
再度、[メモリマップデザイナ]ウインドウから設定します。
仮想アドレス:0x22000000
物理アドレス:0x4e000000
サイズ:4Mbyte
の領域を確保しました。
AttributeはSOLID_ROMとしました。
ロードするアプリは、
・DllFunc()という関数が存在する。
(ウィザードでDLLプロジェクト作成時に生成される関数です)
・グローバル変数 countが存在し、count数だけ"DllFunc."と表示する。
という仕様とします。
という事で、ロードを行えるようにするためのソースコードは以下のようになりました。
ローダー用のAPIを使用するためには、ヘッダーとして、
#include "solid_loader.h"
をインクルードします。
/* モジュールロード (ロード時に名前を付けます 例では “DLL1”), 領域はmemory_map.smm の DLLAREA */
ercd = SOLID_LDR_LoadDLL("DLL1", "\\ROM\\0x22000000, 0x24b00", SOLID_LDR_ADDR_DLLAREA);
if (ercd == SOLID_ERR_OK)
{
/* ロードした DLL が実行可能かチェック */
ret = SOLID_LDR_CanExec("DLL1", &addr);
if (ret > 0)
{
/* 呼び出したい変数のアドレスをシンボル名から検索 (例では count) */
ret = SOLID_LDR_GetDllAddr("DLL1", "count", &cnt);
if (ret == SOLID_ERR_OK)
{
SOLID_ADDRESS *p_count = (SOLID_ADDRESS *)cnt;
*p_count = 3;
}
else
{
syslog(LOG_INFO, ("count NOT found\n"));
}
/* 呼び出したい関数のアドレスをシンボル名から検索 (例では DllFunc) */
ret = SOLID_LDR_GetDllAddr("DLL1", "DllFunc", &addr);
if (ret == SOLID_ERR_OK)
{
void (*pFunc)() = (void (*)())addr; /* アドレスを関数ポインタに型変換 */
pFunc(); /* ロードしたコードを実行 */
}
else
{
syslog(LOG_INFO,("DllFunc() NOT found\n"));
}
}
}
Dllのグローバル変数countを3に変更した後、DllFunc()関数をコールします。
すなわち、シリアルターミナルには"DllFunc."が3回表示されるはずです。
ここで一旦、SOLID-IDEをクローズします。
③ ローダブルアプリケーションのソリューションを生成
以下の手順に従って生成していきます。
https://solid.kmckk.com/SOLID/doc/latest/tutorial/create_loadable_app_project.html#id28
SOLID-IDEを起動します。
以降、書かれている手順通りなので、手順は割愛します。
プロジェクトは以下のように選択しました。
「ウィザードの選択」では、今回はSOLID-OSのソースツリーに含める設定にするため、以下のようにしました。
プラットフォームはARMv7-A、ソリューションプロパティファイルには先程ロードAPIコールを追加したプロジェクトのものを入力しました。
プログラムを以下のように変更します。
#include "dll.h"
#include "kernel.h"
#include "solid_log.h"
DLL_EXPORT int count = 1;
/**
* DLL関数
*/
DLL_EXPORT void DllFunc(void)
{
for (int i = 0; i < count; i++)
SOLID_LOG_printf("DllFunc.\n");
}
実行されると、シリアルターミナルにcountの数だけ”DllFunc.”と表示する仕様です。
countの初期値は1ですので、誰もcountを書き換えない限りは一度だけ”DllFunc.”と表示されるはずです。
ビルドします。
正常終了しました。
これで、コーディング&ビルドのための準備は終わりました。
準備すること:
①ロードされる側アプリケーションのデバッグ設定変更
②メインアプリをビルドしバイナリを適当なフォルダに格納
③ロードされる側アプリケーションの PARTNERデバッガ設定ファイル作成
④ロードする側アプリケーションの PARTNERデバッガ設定ファイル作成
順番に行っていきましょう。
①ロードされる側アプリケーションのデバッグ設定変更
たった今作成した、ロードされるアプリケーションのデバッグ設定を変更します。
プロパティウインドウを開きます。
以下のように設定しました。
※設定の一部は、ロードの後のデバッグ時にブレークポイントを使うための設定も含みます。
一旦SOLID-IDEを閉じます。
② メインアプリをビルドしバイナリを適当なフォルダに格納
ここで、フォルダ構成を見てみると、こうなっています。
ここに、基幹部分のファイル(メインアプリ)のバイナリを入れます。
今回の場合、ビルド済のメインアプリのバイナリは、以下2つあります。
メインアプリ用フォルダ:main-app/を作成し、中に以下2ファイルを入れます。
sample1.out
solid_boot.out
③ ロードされる側アプリケーションの PARTNERデバッガ設定ファイル作成
さらに、PARTNERデバッガ設定ファイルを作成します。
PARTNERデバッガ設定ファイル用フォルダ:partner/を作成し、中に以下3ファイルを入れます。
init.mcr :新規作成します。
jetarm.cfg:メインアプリで使用しているファイルをコピーします。
JETARM.JPX:メインアプリで使用しているファイルをコピーします。
init.mcrファイルの中身は以下としました。
kanji utf8
l ${SolutionDir}\main-app\solid_boot.out
lsa ${SolutionDir}\main-app\sample1.out
rd ${SolutionDir}\Debug_clang\exeClang_SOLID\testdllpro.so,0x4e000000
ここで、フォルダ構成を見てみましょう。
このようになりました。
④ ロードする側アプリケーションの PARTNERデバッガ設定ファイル作成
ロードする側、すなわち、基幹部分となるメインアプリ側についても、PARTNERデバッガ設定ファイルを一つ変更する必要があります。
これは、DLLをメモリファイルシステムとして使うために必要になる手順です。
何かというと、
「デバッグのためにICE起動する直前に、DLLアプリをメモリに置く」
要するに、DLLアプリライブラリファイルを、物理アドレス指定で、メモリにダウンロードしておくための手順です。
このため、init.mcrに以下を一行、追加します。
rd ${SolutionDir}\testdllapp\testdllpro.so,0x4e000000
この、DLLBUFの物理アドレスを指定しました。
#なお、今回はDLLアプリライブラリファイル「testdllpro.so」を、${SolutionDir}\testdllappの下にコピーしています。
これで、準備は整いました。
いよいよ動作確認です。
動作確認すること:
・ひな型だけの「ローダブルアプリケーション」がロード&実行されることを確認
では、確認していきましょう。
SOLID-IDEを起動し、ロードする側である基幹部分のアプリを立ち上げ、実行します。
DLLがロードされ、DLL内の関数がコールされることを確認します。
無事、DLLの実行開始アドレス、グローバル変数countのシンボルアドレスを取得できています。
では、継続実行しましょう。
DllFunc.と3回表示されました。
グローバル変数countが書き換えられた上にDLLの関数がコールされ、実行されることがわかりました。
次はロードされる側です。
動作確認すること:
・ローダブルアプリケーションのロードと実行
見ていきましょう。
SOLID-IDEを起動し、先ほど作成したロードされる側のアプリケーションを立ち上げ、実行してみます。
実行されました。
ちなみに、SOLID_LOG_printf()によるターミナル出力内容を書き換えると、ちゃんとそれが実行されていることがわかります。
ソースコード上で
SOLID_LOG_printf("Hello! DllFunc.\n");
とし、ビルドして実行してみます。
変更した通りに実行されました。
一通り動作したところで、次に基幹部分となるメインアプリからDLLへ、シンボルのEXPORTを試してみます。
今までは、Dllの持つシンボルを基幹部分からアクセスしていました。
今回はその逆方向になります。
お作法は、以下URLに記載されています。
https://solid.kmckk.com/SOLID/doc/latest/tutorial/create_loadable_app_project.html#export-import
① Dllからコールされる関数とシンボルを設置
メインアプリ側にDllからコールされる関数 called_by_dll() とシンボル num を書きます。
以下のようなプログラムにしました。
int num= 0;
void called_by_dll()
{
syslog(LOG_INFO, "Called by Dll. Number = %d\n", num);
}
② EXPORTするシンボルの登録
関数 called_by_dll() とシンボル numを、SOLID_LDR_RegisterSymbol API で、ローダーに登録します。
DllでIMPORTできるようにするためです。
Dllをロードする前に、以下の行を書きます。
SOLID_LDR_RegisterSymbol("num", (SOLID_ADDRESS)&num);
SOLID_LDR_RegisterSymbol("called_by_dll", (SOLID_ADDRESS)&called_by_dll);
ビルドし、一旦SOLID-IDEを閉じます。
③ EXPORTしたシンボルをDllからコール
EXPORTしたシンボルをDllでIMPORTするために行うことはありません。
なぜなら、Dllの未解決シンボルはすべてIMPORT対象だから、です。
https://solid.kmckk.com/SOLID/doc/latest/tutorial/create_loadable_app_project.html#id40
ですので、普通にexternして関数 called_by_dll() とシンボル numを使いましょう。
SOLID-IDEを起動し、Dllのプロジェクトを開き、以下のプログラムを書きました。
#include "dll.h"
#include "kernel.h"
#include "solid_log.h"
DLL_EXPORT int count = 1;
extern int num;
extern void called_by_dll();
/**
* DLL関数
*/
DLL_EXPORT void DllFunc(void)
{
for (int i = 0; i < count; i++)
SOLID_LOG_printf("Hello! DllFunc.\n");
num = 15;
called_by_dll();
}
コールされてますね!
今回は、DLL方式のローダー機能についてご紹介しました。
本来は、この先、ブレークポイントを設定してデバッグもできる、というところまでに踏み込みたかったのですが、とても長くなりそうだったので今回はここまで、です。
Dllを簡単に扱えるようにするため、ウィザードがほぼ設定してくれるのですね。
後は、組み込みなので当然と言えばそうなのですが、そのファイルの置き場を決めてあげる。
この2点がポイントかと思いました。
次回は、SOLID独自方式のローダ機能についてご紹介する予定です。