2013-06-13 15 views
15

Tôi có một thư viện được chia sẻ mà tôi triển khai và tôi muốn .so để gọi một hàm trong chương trình chính tải thư viện.Làm cách nào để thư viện được chia sẻ (.so) gọi một hàm được triển khai trong chương trình bộ tải của nó?

Hãy nói rằng tôi có main.c (thực thi), trong đó có:

void inmain_function(void*); 
dlopen("libmy.so"); 

Trong my.c (mã cho libmy.so) Tôi muốn gọi inmain_function:

inmain_function(NULL); 

Làm thế nào để thư viện được chia sẻ gọi inmain_function bất kể sự kiện inmain_function được xác định trong chương trình chính.

Lưu ý: Tôi muốn gọi một biểu tượng trong main.c từ my.c chứ không phải ngược lại là cách sử dụng phổ biến.

Trả lời

15

Bạn sẽ cần thực hiện chức năng đăng ký trong .so để tệp thực thi có thể cung cấp một con trỏ hàm cho .so của bạn để sử dụng sau này.

Như thế này:

void in_main_func() { 
// this is the function that need to be called from a .so 
} 

void (*register_function)(void(*)()); 
void *handle = dlopen("libmylib.so"); 

register_function = dlsym(handle, "register_function"); 

register_function(in_main_func); 

các register_function cần để lưu trữ các con trỏ hàm trong một biến trong .so nơi các chức năng khác trong .so thể tìm thấy nó.

mylib.c của bạn sẽ cần phải nhìn một cái gì đó như thế này:

void (*callback)() = NULL; 

void register_function(void (*in_main_func)()) 
{ 
    callback = in_main_func(); 
} 

void function_needing_callback() 
{ 
    callback(); 
} 
+2

Tôi muốn làm 'if (callback) {callback(); trả về 0; } else {return -1; } 'để chỉ ra một lỗi và để tránh gọi' NULL' (mà sẽ gây tử vong). – glglgl

+0

Phải, đã xóa đường nét. – user746527

0

Sau đây có thể được sử dụng để tải một thư viện động trong mã của bạn (trong trường hợp ai đó đến đây sau khi xem xét làm thế nào để làm điều đó):

void* func_handle = dlopen ("my.so", RTLD_LAZY); /* open a handle to your library */ 

void (*ptr)() = dlsym (func_handle, "my_function"); /* get the address of the function you want to call */ 

ptr(); /* call it */ 

dlclose (func_handle); /* close the handle */ 

Đừng quên đặt #include <dlfcn.h> và liên kết với tùy chọn –ldl.

Bạn cũng có thể muốn thêm một số logic kiểm tra nếu NULL được trả về. Nếu đó là trường hợp bạn có thể gọi dlerror và nó sẽ cung cấp cho bạn một số thông điệp có ý nghĩa mô tả vấn đề.

Các áp phích khác vẫn cung cấp câu trả lời phù hợp hơn cho vấn đề của bạn.

+0

Tôi muốn gọi từ .so đến một hàm trong một tệp sẽ tải .so – 0x90

+0

Có, điều này sẽ gọi hàm trong thư viện '.so' của bạn từ một tệp khác (chẳng hạn như vị trí chính của bạn). Đây có phải là những gì bạn có nghĩa là xin lỗi? – Nobilis

+0

Tôi muốn gọi hàm chính từ .so. – 0x90

22

Bạn có hai lựa chọn, từ đó bạn có thể chọn:

Lựa chọn 1: xuất khẩu tất cả các biểu tượng từ thực thi của bạn. Đây là tùy chọn đơn giản, chỉ khi xây dựng tệp thực thi, hãy thêm cờ -Wl,--export-dynamic. Điều này sẽ làm cho tất cả các chức năng có sẵn cho các cuộc gọi thư viện.

Tùy chọn 2: tạo tệp biểu tượng xuất với danh sách hàm và sử dụng -Wl,--dynamic-list=exported.txt. Điều này đòi hỏi một số bảo trì, nhưng chính xác hơn.

Để minh họa: thư viện thực thi và tải động đơn giản.

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

void exported_callback() /*< Function we want to export */ 
{ 
    printf("Hello from callback!\n"); 
} 

viud unexported_callback() /*< Function we don't want to export */ 
{ 
    printf("Hello from unexported callback!\n"); 
} 

typedef void (*lib_func)(); 

int call_library() 
{ 
    void  *handle = NULL; 
    lib_func func = NULL; 
    handle = dlopen("./libprog.so", RTLD_NOW | RTLD_GLOBAL); 
    if (handle == NULL) 
    { 
     fprintf(stderr, "Unable to open lib: %s\n", dlerror()); 
     return -1; 
    } 
    func = dlsym(handle, "library_function"); 

    if (func == NULL) { 
     fprintf(stderr, "Unable to get symbol\n"); 
     return -1; 
    } 

    func(); 
    return 0; 
} 

int main(int argc, const char *argv[]) 
{ 
    printf("Hello from main!\n"); 
    call_library(); 
    return 0; 
} 

Mã thư viện (lib.c):

#include <stdio.h> 
int exported_callback(); 

int library_function() 
{ 
    printf("Hello from library!\n"); 
    exported_callback(); 
    /* unexported_callback(); */ /*< This one will not be exported in the second case */ 
    return 0; 
} 

Vì vậy, lần đầu tiên xây dựng thư viện (bước này không khác nhau):

gcc -shared -fPIC lib.c -o libprog.so 

Bây giờ xây dựng thực thi với tất cả những biểu tượng xuất khẩu:

gcc -Wl,--export-dynamic main.c -o prog.exe -ldl 

Run dụ:

$ ./prog.exe 
Hello from main! 
Hello from library! 
Hello from callback! 

Biểu tượng hiển thị rted:

$ objdump -e prog.exe -T | grep callback 
00000000004009f4 g DF .text 0000000000000015 Base  exported_callback 
0000000000400a09 g DF .text 0000000000000015 Base  unexported_callback 

Bây giờ với danh sách xuất khẩu (exported.txt):

{ 
    extern "C" 
    { 
     exported_callback; 
    }; 
}; 

Build & kiểm tra những biểu tượng có thể nhìn thấy:

$ gcc -Wl,--dynamic-list=./exported.txt main.c -o prog.exe -ldl 
$ objdump -e prog.exe -T | grep callback 
0000000000400774 g DF .text 0000000000000015 Base  exported_callback 
+0

khi bạn biên dịch libprog.so như thế này, nó sẽ thấy rằng exports_callback là một biểu tượng bị thiếu. – kritzikratzi

+1

ps. để trả lời câu hỏi của riêng tôi: người ta có thể vô hiệu hóa lỗi "undefined symbol" và nó sẽ hoạt động, ví dụ như trong clang với '-undefined dynamic_lookup'. trên Linux với gcc này hoạt động kỳ diệu như mô tả trong bài viết, nhưng tôi không có ý tưởng tại sao. – kritzikratzi

4
  1. Đặt prototype hàm chính của bạn trong một .h và đưa nó vào cả mã thư viện chính và động của bạn.

  2. Với GCC, chỉ cần biên dịch chương trình chính của bạn với cờ -rdynamic.

  3. Khi được tải, thư viện của bạn sẽ có thể gọi hàm từ chương trình chính.

Một chút giải thích thêm nữa là khi được biên dịch, thư viện động của bạn sẽ có biểu tượng không xác định trong chức năng nằm trong mã chính. Khi ứng dụng chính của bạn tải thư viện, biểu tượng sẽ được giải quyết bằng bảng biểu tượng của chương trình chính. Tôi đã sử dụng các mô hình trên nhiều lần và nó hoạt động như một say mê.

+1

Chỉ cần tò mò, điều gì sẽ xảy ra nếu một lib được chia sẻ khác có biểu tượng? – user746527

+0

Biểu tượng của ứng dụng chính sẽ thay thế/ghi đè lên thư viện, trừ khi hàm của thư viện được định nghĩa bằng từ khóa 'static'. – mshildt

Các vấn đề liên quan