SOLID未分類 ローダー機能ー3(連載19)

ローダー機能ー3(連載19)

(2023/10/24)

[連載19] ローダー機能ー3

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

今回は、SOLID独自形式のローダー機能を動かしてみていきます。

 

 

1.SOLID独自形式のローダー機能おさらい

DLL形式の場合と同様、OSや基幹部となるアプリ等とは別に、アプリ入れ替え可能な領域が存在します。

複数アプリをロードすることも可能で、両アプリとも相互呼び出しが可能です。

 

DLL形式の場合、入れ替え対象のアプリは、ビルドの際には実行アドレスが決定しませんでした。
今回ご紹介する、SOLID独自形式の場合は、ビルド時に実行アドレスを決定します。

この点が大きな違いです。

わざわざ実行アドレスをビルド時に決定するのは、一見面倒そうですが、メリットがあります。
組み込み用途向けとして、実行効率を最大にしつつ、アプリのローディングを行う事ができます。

 

 

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

では早速試してみましょう。
前回同様、以下URLに沿って試してみます。

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

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

なお、実際の開発現場では、同じチームがロードする側とされる側の両方を開発するスタイルであったり、別組織であったりします。
ですので、例えば、ロードするメインアプリのワークスペースで実行することはない、という係の人もおられることになります。

ですが、ここでは(前回もですが)「ロード機能」のご紹介をするという観点から、双方のワークスペースからプログラムを実行するところまでを試してみます。

 

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

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

準備することは、DLL形式を使う場合と同様ですが、内容が少し違います。

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

 

① メモリマップの設定

今回の場合、ビルド時にロードアドレスが決まっている必要があります。
ロードする側のアプリのメモリマップに、あらかじめ仮想アドレスとして登録しておく必要があります。

今回の場合は、このために特別な領域を確保する必要はありません。
なぜなら、ロードするアプリケーションを作成する際に、その開始アドレスを明示的に指定するからです。
指定するときに、「他に使われてれていない」という事が明確であれば、それでOKです。
ですが、ここでは、説明しやすいのでリザーブしておくようにしました。

[メモリマップデザイナ]ウインドウを見てみましょう。

そういえば前回、「DLLAREA」として登録していた部分がありました。

今回もこの領域を使いましょう。

以下のように変更します。

仮想アドレス:0x20000000
サイズ:32Mbyte
の領域を確保しました。

AttributeはSOLID_RAM(通常のRAM空間)とします。
名称は”LOADAREA”としました。

 

②  ロードを行う方のプログラムとファイルシステムを用意
ロードを行えるようにするためのソースコードは以下を参考にします。

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

ロード対象アプリのファイル名は、例と同じく“APP1”としました。
ロードされるファイルは、前回同様、メモリファイルシステムを採用します。

メモリファイルシステムとは:
https://solid.kmckk.com/SOLID/doc/latest/os/filesystem.html#id3

なので、ファイル名は、\ROM\<アドレス>, <サイズ> となります。

それから、これも前回同様、メモリファイルシステムとしてロードファイルを置く場所を作ってあげる必要がありますね。

前回の設定をそのまま使いましょう。
[メモリマップデザイナ]ウインドウを見てみます。

 

仮想アドレス:0x22000000
物理アドレス:0x4e000000
サイズ:4Mbyte
AttributeはSOLID_ROM

名前だけ、変えておきましょうか。

という事で、ロードを行えるようにするためのソースコードは以下のようになりました。
ロードするアプリの仕様は、前回同様以下とします。
・エントリ関数が存在する。
(ウィザードでロード対象のプロジェクト作成時に生成される関数です)
・グローバル変数 countが存在し、" APP <カウント数>."と表示する。

ローダー用のAPIを使用するためには、ヘッダーとして、
#include "solid_loader.h"
をインクルードします。


/* モジュールロード (ロード時に名前を付けます 例では “APP1”) */
ercd = SOLID_LDR_LoadFile ("APP1", "\\ROM\\0x22000000, 0x6000");

if (ercd == SOLID_ERR_OK) {
    /* ロードした モジュール が実行可能かチェック */
    ret = SOLID_LDR_CanExec("APP1", &addr);

    if (ret > 0) {
	/* 呼び出したい変数のアドレスをシンボル名から検索 (例では count) */
	ret = SOLID_LDR_GetAddr("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"));
	}

        void (*pFunc)() = (void (*)())addr;  /* エントリアドレスを関数ポインタに型変換 */
        pFunc();                             /* ロードしたコードを実行 */

    }
}

ここで一旦、SOLID-IDEをクローズします。

 

③ ローダブルアプリケーションのソリューションを生成

以下の手順に従って生成していきます。
https://solid.kmckk.com/SOLID/doc/latest/tutorial/create_loadable_app_project.html#id28

SOLID-IDEを起動します。
以降、書かれている手順通りなので、手順は割愛します。

プロジェクトは以下のように選択しました。

「ウィザードの選択」では、今回もSOLID-OSのソースツリーに含める設定にするため、以下のようにしました。

プラットフォームはARMv7-A、ソリューションプロパティファイルには先程ロードAPIコールを追加したプロジェクトのものを入力しました。
仮想アドレスには、先ほどロードする側のアプリで”LOADAREA”と名付けた空間の先頭仮想アドレスを入力しました。

OKボタン押下すると、ワークスペースが作成されました。
”APP STAT”、”APP END”と表示されるプログラムが既に入っていました。

プログラムを以下のように変更します。


#include "app.h"
#include "kernel.h"
#include "solid_log.h"

int count = 1;

/**
*   メイン関数
*/
int app_main(void)
{
    SOLID_LOG_printf("APP %d\r\n", count);

    return 0;
}

グローバル変数 “count”を、メインアプリからアクセスできるようにするため、APP_EXP.txtファイルに以下のように記載します。

ビルドします。

 

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

 

2.2 ロードするための準備

準備すること:
① ロードされる側アプリケーションのデバッグ設定変更
② メインアプリをビルドしバイナリを適当なフォルダに格納
③ ロードされる側アプリケーションの PARTNERデバッガ設定ファイル作成
④ ロードする側アプリケーションの PARTNERデバッガ設定ファイル作成

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

 

① ロードされる側アプリケーションのデバッグ設定変更
たった今作成した、ロードされるアプリケーションのデバッグ設定を変更します。

プロパティウインドウを開きます。

以下のように設定しました。

エントリポイント は __slo_start (先頭はアンダースコア2つ)です。

※設定の一部は、ロードの後のデバッグ時にブレークポイントを使うための設定も含みます。
一旦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\testloadpro.slo,0x4e000000

ここで、フォルダ構成を見てみましょう。

このようになりました。
前回と同じような感じです。

 

④ ロードする側アプリケーションの PARTNERデバッガ設定ファイル作成
前回、ロードする側、すなわち、基幹部分となるメインアプリ側についても、PARTNERデバッガ設定ファイルを一つ変更する必要がありました。
今回も同様です。

「デバッグのためにICE起動する直前に、ロードされるアプリをメモリに置く」
でしたね。
ロードされる側のアプリファイルを、物理アドレス指定で、メモリにダウンロードしておくための手順です。

このため、init.mcrに以下を一行、追加します。

rd ${SolutionDir}\testloadapp\testloadpro.slo,0x4e000000

ロードされる側のアプリファイルを置く場所は、以下を使います。

この、LOADBUFの物理アドレスを指定しました。

#なお、前回同様、ロード対象アプリファイル「testloadpro.slo」を、${SolutionDir}\testloadappの下にコピーしています。

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

 

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

いよいよ動作確認です。

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

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

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

SOLID_LDR_CanExec()関数が正業終了しました。
これは、ロードが成功し実行可能となった、かつ、ロードモジュール側のすべてのアドレスが解決し、&addrにエントリアドレスが返ってきているという意味です。
安心してロードモジュールを実行できます。

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

意図通り、”APP 3”と表示されました。
ロードモジュールのグローバル変数countの値を変更でき、エントリ関数から実行されていることがわかります。

 

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

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

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

見ていきましょう。

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

実行されました。

前回同様、SOLID_LOG_printf()によるターミナル出力内容を書き換えて試してみましょう。

ソースコード上で
SOLID_LOG_printf("APP count=%d\r\n", count);
としてみます。

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

 

 

3.APP_IMP.txtとAPP_EXP.txt

「コーディング&ビルドのための準備」のところで、一つ振り返るべき点があります。

グローバル変数 “count”を、メインアプリからアクセスできるようにするため、APP_EXP.txtファイルに記載をしました。
先程、しらっと記載してそのまま進んでしまいましたが、、、違和感ありましたよね?

その理由について、少しここでご説明します。

APP_IMP.txtとAPP_EXP.txtは、ロードする側のメインアプリや(自分以外の)他のロードモジュールと、ロードされる側のアプリの間で、シンボルをやり取りするために重要となるファイルです。

先程でてきたAPP_EXP.txtは、ロードされる側のシンボルをEXPORTする働きをします。
そして今回使用していませんが、APP_IMP.txtはロードされる側が、メインアプリや他のロードモジュールのシンボルをIMPORTするために働きます。

[APP_EXP.txt]
メインアプリは、ロードモジュールをロードし、シンボル情報をSOLID_LDR_GetAddr()を使って取り出します。
一方ロードされる側は、自分をビルドしてロード用バイナリファイルを作成する際に、APP_EXP.txtに登録されているシンボルをロード用バイナリファイルに出力しておきます。
後でメインアプリや他のロードモジュールの中で、SOLID_LDR_GetAddr()経由で取り出すことができるようにしておく、というワケです。

[APP_IMP.txt]
通常、ビルドの際、未解決シンボルが残っている場合、リンカーはエラー終了しますよね。
実は、SOLID独自形式のロード用バイナリファイルを生成する際に使用するリンカーには仕掛けが入っています。

APP_IMP.txtに書かれているシンボルについて、ロード用バイナリファイルに未解決シンボルである旨の情報を残したうえで、アドレス未解決のままリンカーは正常終了します。
そして、メインアプリによってロードされる際に、すべてのシンボルが解決されれば、実行可能状態となります。(CanExec()が成功)

未解決シンボルが残っている場合、もしかすると他にまだロードモジュールが居て、そちらのシンボルかもしれません。その場合は、対象のロードモジュールがロードされてすべてのシンボルが解決すれば実行可能状態となります。

ルールについては、以下のURLに記載されていますので、ご参照ください。
https://solid.kmckk.com/SOLID/doc/latest/tutorial/create_loadable_app_project.html#id23

 

 

4.まとめ

今回は、SOLID独自形式のローダー機能についてご紹介しました。
アドレス固定にする作業が必要だから少し面倒かも、と思いきや、ウィザードで行ってくれるので、操作性は前回のDLLと同じような感じでした。

次回は、ブレークポイントを設定してデバッグをしてみる予定です。