如何在dlsym中检查函数指针的返回值?

时间:2017-04-11 09:05:59

标签: c shared-libraries

我正在编写一个库,它将使用dlopen加载动态库并调用给定的函数。

我的函数指针需要一个返回类型为int的函数指针。

typedef int (*function_handle)(int); 

共享对象中一个函数的返回类型为void。

 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;
}

2 个答案:

答案 0 :(得分:1)

不,无法检查dlsym()的返回值。

dlsym()中没有返回值智能 - dlsym()甚至不知道您的符号是函数指针还是数据指针。您应该查看实现设计的其他方法,而不是取决于dlsym()的返回值/类型。

  

dlsym()函数将在加载handle引用的对象时自动加载的所有对象中搜索命名符号。

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

答案 1 :(得分:1)

基本问题 - 如何实现提供不同类型功能的插件 - 非常有趣。

例如,考虑一个简单的Reverse Polish calculator,带有一个添加新运算符的插件接口。

不要让主程序使用dlsym()来查找每个符号,而是让插件只导出一个 - 比如plugin_init() - 将注册函数作为函数指针参数。然后,每个插件会根据其希望添加的每个功能调用一次注册函数。

RPN计算器基于堆栈。如果我们假设每个操作符都可以调整堆栈大小,那么操作符函数原型基本上就是

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

其中*stackptr是指向当前堆栈的指针,*countptr是堆栈中double的数量,*maxcountptr告诉分配的大小(double 1)} s)用于堆栈。如果操作成功执行,它将返回0,否则返回非零errno错误代码。

考虑这个 application.c

#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 $@

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

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

请注意,预期的行必须以 Tab 开头,而不是以8个空格开头。如果您不确定,请运行sed -e 's|^ *|\t|' -i Makefile进行修复。

编译计算器及其插件:

make clean all

如果你跑

./rpcalc list

它会告诉您唯一支持的运算符是list本身。但是,如果你运行例如。

./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,正如您所期望的那样。