web-dev-qa-db-ja.com

共有ライブラリ(.so)は、ロードプログラムに実装されている関数をどのように呼び出すことができますか?

私が実装した共有ライブラリがあり、ライブラリをロードするメインプログラムに実装されている関数を.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から呼び出したいのですが、その逆はありません

20
0x90

後で実行するために実行可能ファイルが.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();
}
15
user746527

次の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
30
  1. メイン関数のプロトタイプを.hファイルに入れ、メインライブラリコードとダイナミックライブラリコードの両方に含めます。

  2. GCCでは、-rdynamicフラグを使用してメインプログラムをコンパイルするだけです。

  3. ロードされると、ライブラリはメインプログラムから関数を呼び出すことができます。

もう少し説明すると、コンパイルすると、ダイナミックライブラリにはメインコード内の関数の未定義のシンボルが含まれます。メインアプリにライブラリを読み込ませると、シンボルはメインプログラムのシンボルテーブルによって解決されます。上記のパターンを何度も使用しましたが、魅力的に機能します。

5
mshildt