如何检查 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
告诉分配的大小(在double
s) 用于堆栈。如果操作成功执行,它将 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;
}
提供sin
和cos
功能。
因为只有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
。
我正在编写一个库,它将使用 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()
.
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
告诉分配的大小(在double
s) 用于堆栈。如果操作成功执行,它将 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;
}
提供sin
和cos
功能。
因为只有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
。