5

問題は、共有ライブラリ (UNIX/LINUX) から関数アドレスを取得する方法です。

C でいくつかのテストケースを作成し (以下を参照)、コンパイルして Ubuntu 10.04 (amd64) および FreeBSD-8.2 (amd64) で実行しました。違いは感じませんでしたが、考えられるトラブルについてもっと知りたいです。

どうぞ:

  1. テスト 1

lib.c

char* f0(void) {
    return "Hello, World!";
}

main.c

#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>

void *hlib, *addr;
char* (*foo)(void);
char* s;

int main(int argc, char** argv) {
    if ( !(hlib = dlopen("./lib.so", RTLD_LAZY)) )
      return 1;
    if ( !(addr = foo = dlsym(hlib, "f0")) )
      return 2;
    s = foo();
    printf("%p => %s\n", addr, s);
    return 0;
}

今それを構築します:

gcc -o lib.o -c lib.c -Wall -Werror -O3 -fPIC
gcc -o lib.so -shared -nostartfiles lib.o
gcc -o main.o -c main.c -Wall -Werror -O3
gcc -o prog main.o -ldl

これにより、ライブラリ関数 f0() のアドレスと実行結果が出力されます。

  1. テスト 2

lib.h (動的リンク ライブラリの標準インターフェイスを定義します)

#ifndef __LIB_H__
#define __LIB_H__

typedef struct __syminfo {
  char* name; // function name
  void* addr; // function address
} syminfo_t;

typedef struct __libinfo {
  int       num;    // number of exported functions
  syminfo_t sym[1]; // vector of exported function information
} libinfo_t;

extern int (*__getinfo)(libinfo_t**);

#endif
/* __LIB_H__
*/

lib.c (ライブラリ自体)

#include <stdlib.h>
#include <lib.h>

static libinfo_t* li;

char* foo(void);

__attribute__((constructor)) void __init() {
  if ( (li = calloc(1, sizeof(libinfo_t))) ) {
    li->num = 1;
    li->sym[0].name = "foo";
    li->sym[0].addr = &foo;
  }
}

__attribute__((destructor)) void __free() {
  if (li)
    free(li);
}

int getinfo(libinfo_t** inf) {
  if (!inf)
    return -1;
  *inf = li;
  return 0;
}

char* foo(void) {
  return "Hello, World!";
}

main.c

#include <stdio.h>
#include <dlfcn.h>
#include <lib.h>

libinfo_t* inf;

void* hlib;

int (*__getinfo)(libinfo_t**);

char* (*foo)(void);

char* s;

int main(int argc, char** argv) {
  if ( !(hlib = dlopen("./lib.so", RTLD_LAZY)) ) 
    return 1;
  if ( !(__getinfo = dlsym(hlib, "getinfo")) )
    return 2;
  if (__getinfo(&inf))
    return 3;
  if ( !(foo = inf->sym[0].addr) )
    return 4;
  s = foo();
  printf("%p => %s\n", inf->sym[0].addr, s);      
  return 0;
}    

これをコンパイルします (-nostartfiles なし):

gcc -I. -o lib.o -c lib.c -Wall -Werror -O3 -fPIC
gcc -o lib.so lib.o -shared
gcc -I. -o main.o -c main.c -Wall -Werror -O3
gcc -o prog main.o -ldl

これは、テスト 1 と同じように出力されます。ライブラリ関数 foo() のアドレスとその実行結果です。

共有ライブラリ関数のアドレスを取得する方法を示そうとしましたが、2 番目のテストで正しいでしょうか? 私はそれでいくつかの問題を抱えていますか?

注: FreeBSD-8.2 では、-ldl 引数を使用する必要はありません。すべての dlfcn.h ルーチンは libc ライブラリにあります。

それぞれ説明を待っています。

4

1 に答える 1

1

それは私にはかなり標準的に見えます。いくつかの問題を引き起こす可能性のある唯一のものは、gcc 属性を使用して共有ライブラリのコンストラクタとデストラクタを作成していることです。それは完全に移植可能ではないかもしれません。関心のあるプラットフォームによって異なります。

この特定のケースでは、これほど複雑なことをする必要がないことに注意してください。2 番目の例で共有ライブラリから返される情報はすべてコンパイル時にわかっているため、その情報を使用して静的構造体を作成し、dlsym で構造体のアドレスを取得して、 main プログラムを呼び出すか、既知の関数を呼び出して構造体を返します。(後者はいくつかの特殊なケースでは少し柔軟性がありますが、どちらもかなり柔軟です。)

于 2013-03-17T02:15:29.567 に答える