2017-04-11 134 views
0

我正在寫一個庫,它將使用dlopen加載動態庫並調用給定的函數。如何檢查函數指針在dlsym中的返回值?

我的函數指針期望一個返回類型爲int的函數指針。

typedef int (*function_handle)(int); 

一個函數的返回類型是在共享對象中的空隙。

void some_function_ret_void(int b) 

但是,如果我將此指針指定給fn,dlsym不會引發任何錯誤。

typedef int (*function_handle)(int); 
function_handle fn; 

有沒有辦法檢查從dlsym得到的函數指針的返回值?

#include <dlfcn.h> 
#include <stdio.h> 
typedef int (*function_handle)(int); 
int main() 
{ 
    void* handle=dlopen("./libtest.so",RTLD_LAZY); 
    function_handle fn; 
    int retval =0; 
    char *err; 
    /**** How to check the return type of fn *********/ 

    fn=dlsym(handle, "some_function_ret_void"); 
    if ((err = dlerror()) != NULL) { 
    printf("Could not invoke the handler %s",err); 
    dlclose(handle); 
    return 0; 
    } 

    /* 
    if(fn return type is void don't print anything) 

    if(fn return type is char* , print the charecter) 
     free(str); 
    */ 

    retval = fn(4); 
    printf("%d",retval); 
    dlclose(handle); 
    return 0; 
} 
在libtest.c

(libtest.so)

int some_function_ret_int(int a) { 
    return a+10; 
} 

char* some_function_ret_str(int b) { 
//allocate memory for string and return; 
} 

void some_function_ret_void(int b) { 
//allocate memory for string and return; 
} 
+0

只有編譯器能夠編譯跟蹤返回類型的當前翻譯單元。一旦編譯完成,就沒有關於參數或返回類型的信息。使用錯誤的返回類型會導致*未定義的行爲*。 –

+0

這同樣適用於參數。沒有辦法知道函數期望的參數。 C沒有定義任何反射數據。 – JeremyP

+2

'dlsym'只會返回函數(或其他共享對象)已加載到內存中的地址(作爲void *')。取決於你的代碼將返回的'void *'轉換爲所需的類型。在某些方面,您的問題與使用不正確原型聲明外部函數的問題類似。它會編譯和鏈接,但在運行時調用它時會生成_undefined behavior_。 –

回答

0

沒有,有沒有辦法來檢查的dlsym()返回值。

dlsym() - dlsym()甚至不知道你的符號是函數指針還是數據指針。您應該看看其他實現您的設計的方式,而不是根據dlsym()的返回值/類型。

dlsym()函數將搜索由於加載句柄引用的對象而自動加載的所有對象中的命名符號。

http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html

0

根本問題 - 如何實現提供不同類型的功能插件 - 很有趣。

例如,考慮一個簡單的Reverse Polish calculator,帶有添加新運算符的插件接口。

而不是讓主程序使用dlsym()找到每個符號,插件導出只有一個 - 比方說,plugin_init() - - 將註冊函數作爲函數指針參數。然後,每個插件會根據每個希望添加的功能調用一次註冊函數。

RPN計算器基於堆棧。如果我們假設每個運營商可以調整堆棧,操作函數原型爲基本

int operation(double **stackptr, int *countptr, int *maxcountptr); 

其中*stackptr是指向當前堆棧,*countptrdouble S IN堆的數量,*maxcountptr告訴尺寸分配(在double s)堆棧。如果操作成功執行,則會返回0,否則返回非零值errno錯誤代碼。

請考慮這個應用程序。Ç

#define _POSIX_C_SOURCE 200809L 
#include <stdlib.h> 
#include <dlfcn.h> 
#include <string.h> 
#include <stdio.h> 
#include <errno.h> 

struct operator { 
    struct operator *next; 
    int   (*func)(double **, int *, int *); 
    char    name[]; 
}; 

struct operator *operators = NULL; 

static int register_operator(const char *name, 
          int  (*func)(double **, int *, int *)) 
{ 
    const size_t  namelen = (name) ? strlen(name) : 0; 
    struct operator *curr; 

    /* Make sure name and func are valid. */ 
    if (!namelen || !func) 
     return EINVAL; 

    /* See if name is already used. */ 
    for (curr = operators; curr != NULL; curr = curr->next) 
     if (!strcmp(name, curr->name)) 
      return EEXIST; 

    /* Allocate memory for this operator. */ 
    curr = malloc(namelen + 1 + sizeof (struct operator)); 
    if (!curr) 
     return ENOMEM; 

    /* Copy function pointer and name. */ 
    curr->func = func; 
    memcpy(curr->name, name, namelen + 1); /* Include terminating '\0'. */ 

    /* Prepend to list. */ 
    curr->next = operators; 
    operators = curr; 

    /* Success. */ 
    return 0; 
} 

static int list_operators(double **stack, int *count, int *maxcount) 
{ 
    struct operator *curr; 

    fprintf(stderr, "Known operators:\n"); 
    for (curr = operators; curr != NULL; curr = curr->next) 
     fprintf(stderr, "\t'%s'\n", curr->name); 

    return 0; 
} 


int main(int argc, char *argv[]) 
{ 
    double *stack = NULL; 
    int  count = 0; 
    int  maxcount = 0; 
    int  arg; 

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { 
     fprintf(stderr, "\n"); 
     fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]); 
     fprintf(stderr, "  %s ./plugin ... NUMBER [ OPERATOR | NUMBER ] ...\n", argv[0]); 
     fprintf(stderr, "\n"); 
     return EXIT_SUCCESS; 
    } 

    if (register_operator("list", list_operators)) { 
     fprintf(stderr, "Failed to register built-in 'list' operator.\n"); 
     return EXIT_FAILURE; 
    } 

    for (arg = 1; arg < argc; arg++) { 
     struct operator *op; 
     double   val; 
     char    dummy; 

     /* Check if argument is a plugin path, starting with "./". */ 
     if (argv[arg][0] == '.' && argv[arg][1] == '/') { 
      void *handle = dlopen(argv[arg], RTLD_NOW); 
      if (handle) { 
       int (*func)(int (*)(const char *, int (*)(double **, int *, int *))) = dlsym(handle, "plugin_init"); 
       if (func) { 
        int failure = func(register_operator); 
        if (failure) { 
         fprintf(stderr, "%s: Operator registration failed: %s.\n", argv[arg], strerror(failure)); 
         return EXIT_FAILURE; 
        } 
       } else 
        dlclose(handle); 
       continue; 
      } 
     } 

     /* Check if argument is a known operator. */ 
     for (op = operators; op != NULL; op = op->next) 
      if (!strcmp(op->name, argv[arg])) 
       break; 
     if (op) { 
      int failure = op->func(&stack, &count, &maxcount); 
      if (failure) { 
       fprintf(stderr, "%s: Cannot apply operator: %s.\n", argv[arg], strerror(failure)); 
       return EXIT_FAILURE; 
      } 
      continue; 
     } 

     /* Parse as a number. */ 
     if (sscanf(argv[arg], " %lf %c", &val, &dummy) != 1) { 
      fprintf(stderr, "%s: Unknown operator.\n", argv[arg]); 
      return EXIT_FAILURE; 
     } 

     /* Make sure stack has enough room for an additional number. */ 
     if (count >= maxcount) { 
      double *temp; 

      maxcount = (count | 255) + 257; 
      temp = realloc(stack, maxcount * sizeof *stack); 
      if (!temp) { 
       fprintf(stderr, "%s.\n", strerror(ENOMEM)); 
       return EXIT_FAILURE; 
      } 
      stack = temp; 
     } 

     /* Push val to top of stack. */ 
     stack[count++] = val; 
    } 

    for (arg = 0; arg < count; arg++) 
     printf("[%d] = %g\n", arg + 1, stack[arg]); 

    return (count == 1) ? EXIT_SUCCESS : EXIT_FAILURE; 
} 

struct operator *operators是著名運營商的全球單鏈表。 register_operator()函數將新運算符添加到列表中,除非名稱已被使用。

唯一的內置運算符是list,因此您可以列出已知的運算符。

我們來看幾個不同的插件實現。首先,plugin_basic.c

#include <errno.h> 

static int op_add(double **stack, int *count, int *maxcount) 
{ 
    if (*count < 2) 
     return EINVAL; 

    (*stack)[*count - 2] = (*stack)[*count - 1] + (*stack)[*count - 2]; 
    (*count)--; 

    return 0; 
} 

static int op_sub(double **stack, int *count, int *maxcount) 
{ 
    if (*count < 2) 
     return EINVAL; 

    (*stack)[*count - 2] = (*stack)[*count - 1] - (*stack)[*count - 2]; 
    (*count)--; 

    return 0; 
} 

static int op_mul(double **stack, int *count, int *maxcount) 
{ 
    if (*count < 2) 
     return EINVAL; 

    (*stack)[*count - 2] = (*stack)[*count - 1] * (*stack)[*count - 2]; 
    (*count)--; 

    return 0; 
} 

static int op_div(double **stack, int *count, int *maxcount) 
{ 
    if (*count < 2) 
     return EINVAL; 

    (*stack)[*count - 2] = (*stack)[*count - 1]/(*stack)[*count - 2]; 
    (*count)--; 

    return 0; 
} 

int plugin_init(int (*register_operator)(const char *name, 
             int  (*func)(double **, int *, int *))) 
{ 
    int failure; 

    if ((failure = register_operator("+", op_add))) 
     return failure; 

    if ((failure = register_operator("-", op_sub))) 
     return failure; 

    if ((failure = register_operator("x", op_mul))) 
     return failure; 

    if ((failure = register_operator("/", op_div))) 
     return failure; 

    return 0; 
} 

它提供了四種基本的運營商+-x,和/;和plugin_sincos.c

#include <math.h> 
#include <errno.h> 

static int op_sin(double **stack, int *count, int *maxcount) 
{ 
    if (*count < 1) 
     return EINVAL; 

    (*stack)[*count - 1] = sin((*stack)[*count - 1]); 

    return 0; 
} 

static int op_cos(double **stack, int *count, int *maxcount) 
{ 
    if (*count < 1) 
     return EINVAL; 

    (*stack)[*count - 1] = sin((*stack)[*count - 1]); 

    return 0; 
} 


int plugin_init(int (*register_operator)(const char *name, 
             int  (*func)(double **, int *, int *))) 
{ 
    int failure; 

    if ((failure = register_operator("sin", op_sin))) 
     return failure; 

    if ((failure = register_operator("cos", op_cos))) 
     return failure; 

    return 0; 
} 

提供sincos功能。

因爲只有plugin_init()功能需要動態出口,讓我們添加一個共同的符號文件,plugin.syms

{ 
    plugin_init; 
}; 

請注意,我已經明確標明很多功能static。這是爲了避免命名空間污染:確保它們對其他編譯單元不可見,否則可能會導致衝突。 (儘管符號文件應確保只有plugin_init()動態出口的,static提醒我,這是不應該的功能在任何情況下要導出的程序員。)

最後,的Makefile綁定一起:

CC  := gcc 
CFLAGS := -Wall -O2 
LD  := $(CC) 
LDFLAGS := -lm -ldl 

.PHONY: clean all 

all: rpcalc basic.plugin sincos.plugin 

clean: 
     rm -f rpcalc basic.plugin sincos.plugin 

rpcalc: application.c 
     $(CC) $(CFLAGS) $^ $(LDFLAGS) -o [email protected] 

basic.plugin: plugin_basic.c 
     $(CC) $(CFLAGS) -fPIC -shared $^ $(LDFLAGS) -Wl,-dynamic-list,plugin.syms -Wl,-soname,[email protected] -o [email protected] 

sincos.plugin: plugin_sincos.c 
     $(CC) $(CFLAGS) -fPIC -shared $^ $(LDFLAGS) -Wl,-dynamic-list,plugin.syms -Wl,-soname,[email protected] -o [email protected] 

注意,打算行必須以標籤,而不是八個空格開始。如果您不確定,請運行sed -e 's|^ *|\t|' -i Makefile進行修復。如果您運行

./rpcalc list 

它會告訴你,唯一支持的運營商是list本身

make clean all 

編譯計算器及其插件。但是,如果您運行例如

./rpcalc ./basic.plugin list 
./rpcalc ./*.plugin list 

它會顯示由插件實現的操作符。

它也是一個工作計算器。如果你想計算,也就是說,sin(0.785398) x cos(0.785398),運行

./rpcalc ./*.plugin 0.785398 sin 0.785398 cos x 

,並計劃將輸出[1] = 0.5,正如你所期望。