5

私は次のようなことをしようとしています

  enum types {None, Bool, Short, Char, Integer, Double, Long, Ptr};
  int main(int argc, char ** args) {
     enum types params[10] = {0};
     void* triangle = dlopen("./foo.so", RTLD_LAZY);
     void * fun = dlsym(triangle, ars[1]);

     <<pseudo code>>
  }

擬似コードは次のようなものです

fun = {}
for param in params:
      if param == None:
         fun += void
      if param == Bool:
          fun += Boolean
      if param == Integer:
          fun += int
      ...
 returnVal = fun.pop()
 funSignature = returnval + " " + funName + "(" + Riffle(fun, ",") + ")"
 exec funSignature

ありがとうございました

4

3 に答える 3

23

実際、あなたはあなたが望むほとんどすべてをすることができます。C言語(たとえばC ++とは異なり)では、共有オブジェクトの関数は名前だけで参照されます。したがって、適切な関数を見つけるために、そして最も重要なことを呼び出すために、完全な署名は必要ありません。その名前だけが必要です!それは長所と短所の両方ですが、それはあなたが選んだ言語の性質です。

それがどのように機能するかを示しましょう。

#include <dlfcn.h>

typedef void* (*arbitrary)();
// do not mix this with   typedef void* (*arbitrary)(void); !!!

int main()
{
    arbitrary my_function;
    // Introduce already loaded functions to runtime linker's space
    void* handle = dlopen(0,RTLD_NOW|RTLD_GLOBAL);
    // Load the function to our pointer, which doesn't know how many arguments there sould be
    *(void**)(&my_function) = dlsym(handle,"something");
    // Call something via my_function
    (void)  my_function("I accept a string and an integer!\n",(int)(2*2));
    return 0;
}

実際、その方法で任意の関数を呼び出すことができます。ただし、1つの欠点があります。実際には、コンパイル時に関数の戻り型を知る必要があります。デフォルトでは、そのtypedefでvoid *を省略すると、intが戻り型と見なされます。そうです。これは正しいCコードです。重要なのは、コンパイラがスタックを適切に操作するために、戻り型のサイズを知る必要があるということです。

たとえば、リターン型のサイズが異なるいくつかの関数型を事前に宣言してから、実際に呼び出す関数型を選択するなどのトリックで回避できます。しかし、より簡単な解決策は、プラグインの関数にvoid*またはintを常に返すように要求することです。引数として指定されたポインタを介して返される実際の結果。

必ず確認する必要があるのは、受け入れるはずの引数の正確な数とタイプを使用して関数を常に呼び出すことです。異なる整数型の違いに細心の注意を払ってください(最良のオプションは、それらに引数を明示的にキャストすることです)。

何人かのコメント提供者は、上記のコードが可変個引数関数(など)で機能することが保証されていないことをprintf報告しました。

于 2009-08-30T19:12:52.660 に答える
19

返されるのは通常、関数ポインタですdlsym()-に偽装しますvoid *。(グローバル変数の名前を尋ねると、そのグローバル変数へのポインターも返されます。)

次に、他のポインタを使用して関数を実行する場合と同じように、その関数を呼び出します。

int (*fun)(int, char *) = (int (*)(int, char *))dlsym(triangle, "function");

(*fun)(1, "abc");    # Old school - pre-C89 standard, but explicit
fun(1, "abc");       # New school - C89/C99 standard, but implicit

私は古い学校です。'fun'が関数へのポインタであり、その宣言を確認する必要がないことを読者が理解できるように、明示的な表記を好みます。fun新しい学校表記では、' 'という関数を見つける前に、変数''を探すことを忘れないでくださいfun()

あなたがしているように関数呼び出しを動的に構築することはできないことに注意してください-または、一般的にはそうではありません。これを行うには、さらに多くの作業が必要です。関数ポインタが引数として何を期待し、何を返すのか、そしてそれをすべて解釈する方法を事前に知っておく必要があります。

Perlなどのより動的な関数呼び出しを管理するシステムには、関数の呼び出し方法と引数の受け渡し方法に関する特別な規則があり、任意のシグニチャーを持つ関数を呼び出さない(おそらく呼び出すことができない)。事前にわかっているシグニチャを持つ関数のみを呼び出すことができます。1つのメカニズム(Perlでは使用されません)は、引数をスタックにプッシュしてから、スタックから値を収集する方法を知っている関数を呼び出すことです。ただし、呼び出された関数がそれらの値を操作してから任意の他の関数を呼び出した場合でも、その呼び出された関数は任意の他の関数の正しい呼び出しシーケンスを提供します。

Cでの反射は難しいです-非常に難しいです。元に戻すことはできませんが、それをサポートするインフラストラクチャとそれを使用するための規律が必要であり、インフラストラクチャのルールをサポートする関数のみを呼び出すことができます。</ p>

于 2009-08-30T18:40:10.587 に答える
0

適切なソリューション

共有ライブラリを作成していると仮定します。この問題に対して私が見つけた最善の解決策は、次の方法で動的にリンクされる関数を厳密に定義および制御することです。

  1. すべての記号を非表示に設定
    • たとえばclang -dynamiclib Person.c -fvisibility=hidden -o libPerson.dylib 、clangでコンパイルする場合
  2. 次に、とを使用__attribute__((visibility("default")))extern "C"て、関数を選択的に再表示およびインクルードします
  3. 利益!関数のシグネチャが何であるかを知っています。あなたはそれを書いた!

これは、 Appleのダイナミックライブラリ設計ガイドラインで見つかりました。これらのドキュメントには、上記の問題に対する他の解決策も含まれています。

あなたの質問への答え

以前の回答で述べたように、定義に含まれるCおよびC ++関数extern "C"はマングルされていないため、関数のシンボルには完全な関数シグネチャが含まれていません。ただし、関数をマングルせずにC ++でコンパイルしている場合はextern "C"、関数をデマングルして完全な関数のシグネチャを取得できます(demangler.comc ++ライブラリなどのツールを使用)。マングリングとは何かについて詳しくは、こちらをご覧ください。

一般的に、dlopenを使用して関数をインポートしようとしている場合は、最初のオプションを使用するのが最適です。

于 2020-12-10T01:14:26.210 に答える