私が実装した共有ライブラリがあり、ライブラリをロードするメインプログラムに実装されている関数を.soに呼び出させたい。
以下を含むmain.c(実行可能ファイル)があるとします。
void inmain_function(void*);
dlopen("libmy.so");
My.c(libmy.soのコード)で、inmain_function
を呼び出します。
inmain_function(NULL);
メインプログラムでinmain_function
が定義されているという事実に関係なく、共有ライブラリはどのようにinmain_function
を呼び出すことができますか。
注:main.cのシンボルをmy.cから呼び出したいのですが、その逆はありません
後で実行するために実行可能ファイルが.soへの関数ポインターを提供できるように、.soにレジスタ関数を作成する必要があります。
このような:
void in_main_func () {
// this is the function that need to be called from a .so
}
void (*register_function)(void(*)());
void *handle = dlopen("libmylib.so");
register_function = dlsym(handle, "register_function");
register_function(in_main_func);
register_functionは、関数ポインタを.so内の変数に格納する必要があります。ここで、.so内の他の関数がそれを見つけることができます。
Mylib.cは次のようにする必要があります。
void (*callback)() = NULL;
void register_function( void (*in_main_func)())
{
callback = in_main_func();
}
void function_needing_callback()
{
callback();
}
次の2つのオプションから選択できます。
オプション1:すべてのシンボルを実行可能ファイルからエクスポートします。これは単純なオプションで、実行可能ファイルをビルドするときにフラグ-Wl,--export-dynamic
を追加します。これにより、すべての関数をライブラリー呼び出しで使用できるようになります。
オプション2:関数のリストを含むエクスポートシンボルファイルを作成し、-Wl,--dynamic-list=exported.txt
を使用します。これにはある程度のメンテナンスが必要ですが、より正確です。
実例:単純な実行可能ファイルと動的にロードされるライブラリ。
#include <stdio.h>
#include <dlfcn.h>
void exported_callback() /*< Function we want to export */
{
printf("Hello from callback!\n");
}
viud unexported_callback() /*< Function we don't want to export */
{
printf("Hello from unexported callback!\n");
}
typedef void (*lib_func)();
int call_library()
{
void *handle = NULL;
lib_func func = NULL;
handle = dlopen("./libprog.so", RTLD_NOW | RTLD_GLOBAL);
if (handle == NULL)
{
fprintf(stderr, "Unable to open lib: %s\n", dlerror());
return -1;
}
func = dlsym(handle, "library_function");
if (func == NULL) {
fprintf(stderr, "Unable to get symbol\n");
return -1;
}
func();
return 0;
}
int main(int argc, const char *argv[])
{
printf("Hello from main!\n");
call_library();
return 0;
}
ライブラリコード(lib.c):
#include <stdio.h>
int exported_callback();
int library_function()
{
printf("Hello from library!\n");
exported_callback();
/* unexported_callback(); */ /*< This one will not be exported in the second case */
return 0;
}
したがって、最初にライブラリをビルドします(このステップは違いません):
gcc -shared -fPIC lib.c -o libprog.so
すべてのシンボルをエクスポートして実行ファイルをビルドします。
gcc -Wl,--export-dynamic main.c -o prog.exe -ldl
実行例:
$ ./prog.exe
Hello from main!
Hello from library!
Hello from callback!
エクスポートされたシンボル:
$ objdump -e prog.exe -T | grep callback
00000000004009f4 g DF .text 0000000000000015 Base exported_callback
0000000000400a09 g DF .text 0000000000000015 Base unexported_callback
次に、エクスポートされたリスト(exported.txt
)を使用します。
{
extern "C"
{
exported_callback;
};
};
可視シンボルを作成して確認する:
$ gcc -Wl,--dynamic-list=./exported.txt main.c -o prog.exe -ldl
$ objdump -e prog.exe -T | grep callback
0000000000400774 g DF .text 0000000000000015 Base exported_callback
メイン関数のプロトタイプを.hファイルに入れ、メインライブラリコードとダイナミックライブラリコードの両方に含めます。
GCCでは、-rdynamic
フラグを使用してメインプログラムをコンパイルするだけです。
ロードされると、ライブラリはメインプログラムから関数を呼び出すことができます。
もう少し説明すると、コンパイルすると、ダイナミックライブラリにはメインコード内の関数の未定義のシンボルが含まれます。メインアプリにライブラリを読み込ませると、シンボルはメインプログラムのシンボルテーブルによって解決されます。上記のパターンを何度も使用しましたが、魅力的に機能します。