Linux動態鏈接之五:運行時顯式加載共享文件.so

動態鏈接更多是從.lib等模塊分離組裝的角度來看待問題,實際上,如果動態鏈接可以實現,那麼在運行過程中動態加載.so對象也是可行的,這種共享對象被稱爲DLL,它其實和普通.so對象並無本質區別,只是程序的視角不同。

運行時鏈接的技術手段常用來做插件、驅動加載等處理,因爲不需要開始就全部加載進入進程,可以有效地減少程序啓動時間和內存使用。對於Web服務器需要長期連續運行的場景,如果某個模塊更新,顯然不能重啓再加載,故而這種情況下使用DLL手段是合理。

動態鏈接是由動態鏈接器.ld.so全程負責,對於程序是透明的,而運行時加載,同樣是由程序藉助.ld.so封裝提供的4個API來完成操作的:
1. dlopen()
void * dlopen(const char* filename, int flag);
打開一個動態庫,並將其加載到進程的地址空間,完成初始化過程。第一個參數是動態庫的路徑,如果是相對路徑比如只提供文件名,那麼則按照LD_LIBRARY_PATH,/etc/ld.so.cache, /lib, /usr/lib等系統路徑順序查找匹配的共享庫。如果filename=0則會返回當前可執行文件的全局符號表的句柄,則可以在運行時定義任何符號的位置,並且可以執行它們,類似高級語言的“反射”機制。

flag可選RTLD_LAZY(PLT延遲綁定機制);RTLD_NOW表示模塊被加載時立即完成所有符號的綁定工作,如果有任何未定義的符號引用,則返回錯誤。在調試程序時應該使用RTLD_NOW作爲參數,因爲有符號未定義引用的情況可以立即定位,而使用RTLD_LAZY延遲綁定則會導致綁定工作推遲,並且難以捕獲。

dlopen()返回的相應模塊的句柄,這個句柄用來在後續dlsym()等函數中用來定位模塊內部內容,如果加載模塊失敗,則返回NULL。

2.dlsym()

void* dlsym(void* handle, char* symbol)

運行時裝載查找指定符號,如果查到則返回符號的地址或者常量值,如果沒有查到,則返回NULL,同時dlerror()返回相應錯誤信息。

3.dlerror()
每次調用dlopen()\dlsym()等函數後,可以通過dlerror(0函數來判斷上次調用是否成功。dlerror()函數返回值類型爲char*, 如果dlerror()返回的是NULL則標識成功,如果不是,則返回相應錯誤信息。

4.dlclose()
dlclose()函數作用和dlopen()相反,輸入相應模塊的句柄,用來從虛擬進程空間中卸載相應的模塊。並在卸載之前,先執行.finit段的代碼,然後將各模塊的符號從全局符號表中去除掉,取消進程空間跟模塊的映射關係,然後纔是關閉模塊文件的讀取通道。

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

/*調用規範:
$RunSo /lib/foobar.so  function arg1 arg2 ... return_type
$./runso /lib/i386-linux-gnu/libm-2.xx.so sin d2.0 d
*/

#define SETUP_STACK \
i = 2;              \
while(++i < argc-1) {                             \
    switch(argv[i][0]) {                      \
        case 'i':                         \
            asm volatile("push %0" :: "r"(atoi (&argv[i][1]))); \
            esp += 4;  \
            break;     \
        case 'd':\
                        atof(&argv[i][1]); \
            asm volatile("subl $8, %esp\n" "fstpl (%esp)" ); \
            esp += 8; \
            break;    \
        case 's':  \
                        asm volatile("push %0" :: "r"(&argv[i][1]) ); \
            esp += 4; \ break;    \
        default:          \
            printf("error argument type"); \
            goto exit_runso;  \
        } }                    

#define RESTORE_STACK   asm volatile("add %0, %%esp"::"r"(esp))

int main(int argc, char* argv[]) {
    void* handle;
    char* error;
    int i;
    int esp = 0;
    void* func;

    handle = dlopen(argv[1], RTLD_NOW);
    if(handle == 0) {
        printf("Can't find library:%s\n", argv[1]);
        return -1;
    }

    func = dlsym(handle, argv[2]);
    if ( (error = dlerror()) != NULL){
        printf("Find symbol %s error: %s\n", argv[2], error);
        goto exit_runso;
    }

    switch (argv[argc-1][0]){
        case 'i':{
            int (*func_int)() = func;
            SETUP_STACK;
            int ret = func_int();
            RESTORE_STACK;
            printf("ret = %d\n", ret);
            break;
            }
        case 'd':{
            double (*func_double) () = func;
            SETUP_STACK;
            double ret = func_double();
            RESTORE_STACK;
            printf("ret = %f\n", ret);
            break;
            }
        case 's':{
            char* (*func_str)() = func;
            SETUP_STACK;
            char* ret = func_str();
            RESTORE_STACK;
            printf("ret = %s\n", ret);
            break;
            }
        case 'v':{
            void (*func_void)() = func;
            SETUP_STACK;
            func_void();
            RESTORE_STACK;
            printf("ret = void");
            break;
            }
        } //end of switch

        exit_runso:
        dlclose(handle);
}

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章