从共享库获取函数指针之间的区别

时间:2013-03-14 09:36:31

标签: c shared-libraries

问题是如何从共享库(UNIX / LINUX)获取函数地址?

我在C中编写了一些测试用例(见下文),在Ubuntu 10.04(amd64)和FreeBSD-8.2(amd64)上编译和运行。我没有感觉到任何不同,但我想更多地了解可能的麻烦。

他们是:

  1. 测试1
  2. 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
    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
      

      此printf与Test 1相同:库函数foo()的地址及其执行结果。

      我试图展示如何获得共享库函数地址,但我是否正确进行第二次测试?我有麻烦吗?

      注意:在FreeBSD-8.2中不需要使用-ldl参数,所有dlfcn.h例程都在libc库中。

      分别寻求任何解释。

1 个答案:

答案 0 :(得分:1)

这对我来说看起来很标准。您正在使用的唯一可能会产生一些问题的是您正在使用gcc属性为共享库创建构造函数和析构函数。这可能不完全是便携式的;这取决于你关心的平台。

请注意,在这种特定情况下,不需要做这么复杂的事情。您在第二个示例中从共享库返回的信息在编译时都是已知的,因此您可以使用该信息创建一个静态结构,并使用dlsym检索结构的地址并从中查找主程序或调用已知函数返回结构。 (后者在某些极端情况下稍微灵活一些,但两者都相当灵活。)