use*_*737 5 c shared-libraries
问题是如何从共享库(UNIX/LINUX)获取函数地址?
我在C中编写了一些测试用例(见下文),在Ubuntu 10.04(amd64)和FreeBSD-8.2(amd64)上编译和运行.我没有感觉到任何差异,但我想知道更多关于可能的麻烦.
他们来了:
lib.c
char* f0(void) {
return "Hello, World!";
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
现在构建它:
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
Run Code Online (Sandbox Code Playgroud)
这将打印库函数f0()的地址和执行结果.
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__
*/
Run Code Online (Sandbox Code Playgroud)
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!";
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
现在编译它(没有-nartartfiles):
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
Run Code Online (Sandbox Code Playgroud)
此printf与Test 1相同:库函数foo()的地址及其执行结果.
我试图展示如何获得共享库函数地址,但我是否正确进行第二次测试?我有麻烦吗?
注意:在FreeBSD-8.2中不需要使用-ldl参数,所有dlfcn.h例程都在libc库中.
分别在寻求任何解释.
这对我来说看起来相当标准。您使用的唯一可能引起问题的是您使用 gcc 属性为共享库创建构造函数和析构函数。这可能不完全是可移植的;这取决于您关心什么平台。
请注意,在这种特定情况下,无需执行如此复杂的操作。在第二个示例中从共享库返回的信息在编译时都是已知的,因此您可以使用该信息创建一个静态结构,然后使用 dlsym 检索该结构的地址并从主程序或调用已知函数来返回结构。(后者在某些极端情况下稍微灵活一些,但两者都相当灵活。)