SOLID未分類 ローダー機能ー2(連載18)

ローダー機能ー2(連載18)

(2023/10/18)

[連載18] ローダー機能ー2

前回から、SOLID-OSの持つ「ローダー機能」についてご紹介しています。

今回は、DLL(Dynamic Link Library)方式のローダー機能を動かしてみていきます。

 

 

1.DLL方式のローダー機能おさらい

OSや基幹部となるアプリ等とは別に、アプリ入れ替え可能な領域が存在します。

下の図で、紫色の部分です。

ローダー機能により、例えばアプリAをロードするとします。
以降はアプリAの関数をコールしたり、アプリAが基幹部分の関数をコールしたりすることができます。

さらに、複数アプリをロードすることも可能です。

両アプリとも、先ほどと同様、相互呼び出しが可能です。

DLL方式の場合、入れ替え対象のアプリは、ビルドの際にはアドレスが決定しません。
ロード時にアドレスが確定します。

今回は、そのDLL方式のアプリをロードしてみます。

ロードする対象のアプリは、メモリファイルシステムに置くことにします。
わざわざファイルシステムを導入する必要がないため、簡単に使えます。

 

 

2.ローダブルアプリケーション開発環境の準備

では早速試してみましょう。
以下URLに沿って試してみます。

https://solid.kmckk.com/SOLID/doc/latest/tutorial/create_loadable_app_project.html#id10

上記URLの説明で、開発会社と協力会社という言葉が出てきます。
・開発会社:基幹となる部分を開発する担当
・協力会社:入れ替え対象のアプリを開発する担当
と解釈して進めます。

今回も、Cortex-A9搭載ボード×PARTNER Jet2の組み合わせを使用します。

 

2.1 コーディング&ビルドのための準備

準備すること:
① メモリマップの設定
② ロードを行う方のプログラムとファイルシステムを用意
③ ローダブルアプリケーションのソリューションを生成

順番に行っていきましょう。

① メモリマップの設定

アプリをロードする場所を決めます。
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.”と表示されるはずです。

ビルドします。

正常終了しました。
これで、コーディング&ビルドのための準備は終わりました。

 

2.2 ロードするための準備

準備すること:

①ロードされる側アプリケーションのデバッグ設定変更
②メインアプリをビルドしバイナリを適当なフォルダに格納
③ロードされる側アプリケーションの 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の下にコピーしています。

これで、準備は整いました。

 

2.3 ロードする側の動作確認

いよいよ動作確認です。

動作確認すること:
・ひな型だけの「ローダブルアプリケーション」がロード&実行されることを確認

では、確認していきましょう。

SOLID-IDEを起動し、ロードする側である基幹部分のアプリを立ち上げ、実行します。
DLLがロードされ、DLL内の関数がコールされることを確認します。

 

無事、DLLの実行開始アドレス、グローバル変数countのシンボルアドレスを取得できています。

では、継続実行しましょう。

 

 

DllFunc.と3回表示されました。

グローバル変数countが書き換えられた上にDLLの関数がコールされ、実行されることがわかりました。

 

2.4 ロードされる側の動作確認

次はロードされる側です。

動作確認すること:
・ローダブルアプリケーションのロードと実行

見ていきましょう。

SOLID-IDEを起動し、先ほど作成したロードされる側のアプリケーションを立ち上げ、実行してみます。

 

実行されました。

 

 

ちなみに、SOLID_LOG_printf()によるターミナル出力内容を書き換えると、ちゃんとそれが実行されていることがわかります。
ソースコード上で
SOLID_LOG_printf("Hello! DllFunc.\n");
とし、ビルドして実行してみます。

 

変更した通りに実行されました。

 

2.5 メインアプリからDllへのシンボルEXPORT

一通り動作したところで、次に基幹部分となるメインアプリから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();
}

では、ビルドして実行してみましょう。

コールされてますね!

 

 

3.まとめ

今回は、DLL方式のローダー機能についてご紹介しました。
本来は、この先、ブレークポイントを設定してデバッグもできる、というところまでに踏み込みたかったのですが、とても長くなりそうだったので今回はここまで、です。

Dllを簡単に扱えるようにするため、ウィザードがほぼ設定してくれるのですね。
後は、組み込みなので当然と言えばそうなのですが、そのファイルの置き場を決めてあげる。
この2点がポイントかと思いました。

次回は、SOLID独自方式のローダ機能についてご紹介する予定です。