如何检查 dlsym 中函数指针的 return 值?

How to check the return value of a function pointer in dlsym?

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

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

typedef int (*function_handle)(int); 

Return一个函数类型在共享对象中无效。

 void some_function_ret_void(int b)

但是如果我将此指针分配给 fn,dlsym 不会抛出任何错误。

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

有没有办法检查从 dlsym 获得的函数指针的 return 值?

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

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

dlsym() 中没有 return 值智能 - dlsym() 甚至不知道您的符号是函数指针还是数据指针。除了依赖 dlsym().

的 return 值/类型之外,您可能应该看看其他实现设计的方法

The dlsym() function shall search for the named symbol in all objects loaded automatically as a result of loading the object referenced by handle.

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

潜在的问题——如何实现提供不同类型功能的插件——非常有趣。

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

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

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

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

其中*stackptr是指向当前栈的指针,*countptr是栈中double的个数,*maxcountptr告诉分配的大小(在doubles) 用于堆栈。如果操作成功执行,它将 return 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 '[=11=]'. */

    /* 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 开头,而不是八个空格。如果您不确定,运行 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