dlopen 和 dlsym 用法

參考

參考-1

參考-2

基本介紹

使用 dlopen 和 dlsym 可以使用 so 檔

將 so 檔裡面的函式取出來使用,達到動態替換函式的方法

1
2
3
4
#include <dlfcn.h>
void *dlopen (const char *filename, int flag);
void *dlsym (void *handle, const char *symbol);
int dlclose (void *handle);

首先 man dlopen 可以看到範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//Load the math library, and print the cosine of 2.0:

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

int
main(int argc, char **argv)
{
void *handle;

// 建立一個 function 名叫 cosine
// 需要傳入 double 類型參數並會回傳 double
double (*cosine)(double);
char *error;

// 打開 libm.so 動態函示庫
handle = dlopen("libm.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}

dlerror(); /* Clear any existing error */


/* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
would seem more natural, but the C99 standard leaves
casting from "void *" to a function pointer undefined.
The assignment used below is the POSIX.1-2003 (Technical
Corrigendum 1) workaround; see the Rationale for the
POSIX specification of dlsym(). */

// 將 libm.so 裡面的 cos 函式取出並指派給 cosine
//*(void **) (&cosine) = dlsym(handle, "cos");
// 可以改寫成以下這種寫法比較容易閱讀
cosine = dlsym(handle, "cos");

if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}

printf("%f\n", (*cosine)(2.0));
dlclose(handle);
exit(EXIT_SUCCESS);
}

編譯測試

-ldl 就是 link “dl module” 的意思,而 dl module = libdl.so

編譯出來的執行檔為 8908

1
gcc -o main main.c -ldl

如果把檔案改為直接呼叫函式,執行檔為 8552

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void)
{
printf ("%f\n", cos(2.0));
return 0;
}

# 編譯
gcc -o main main.c

實際製作測試

實際要製作兩個檔案

1
2
1. 動態函示庫檔案      (libTest.c)
2. 使用動態動態函示庫 (main.c)

首先是libTest.c,內容如下

1
2
3
4
5
6
#include <stdio.h>
int jasonAdd(int a, int b)
{
printf("%d add %d = %d\n", a, b, a+b );
return (a+b);
}

編譯步驟如下

1
2
1.編譯為目的檔            : gcc -fPIC -c libTest.c
2.目的檔轉為動態函示庫 : gcc -fPIC -shared -o libTest.so libTest.o

接著是main.c,內容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int *argc, char **argv)
{
void *fHandle;
int (*func)(int, int); // 注意傳入參數和回傳參數的型態

fHandle = dlopen("./libTest.so",RTLD_LAZY);
if (!fHandle) {
fprintf (stderr, "%s\n", dlerror());
exit(1);
}
dlerror();
func = dlsym(fHandle, "jasonAdd");

if (func) {
printf("Result = %d\n",func(10,20));
}
dlclose(fHandle);
return 0;
}

編譯步驟和執行結果如下

1
2
3
4
$ gcc -o main main.c -ldl
$ ./main
10 add 20 = 30
Result = 30

其他

動態函示庫名稱沒有限定要libXXX

例如你取名為jasonLIB.so也是可以的,只是習慣以libXXX命名

使用 .so 前提是你知道裡面函式的參數與回傳值

否則你再定義 function pointer 時也不知道怎麼定義

另外其實你可以使用objdump -T jasonLIB.so

來 dump 函示庫 symbol,可以看出有哪些函式可使用

但還是看不出參數型態數量以及回傳值型態