POSIX标准明确规定,你可以“运行”作为指针传递的地址(强调是我的):
Note that conversion from a void *
pointer to a function pointer as in:
fptr = (int (*)(int))dlsym(handle, "my_function");
is not defined by the ISO C standard. This standard requires this conversion to work correctly on conforming implementations.
为此,您需要首先“动画化”对象指针。
#define ANIMATED_POINTER(FUNC_TYPE, OBJECT_POINTER) \
(*((FUNC_TYPE *)(&(OBJECT_POINTER))))
然后您可以直接调用对象变量,
#include <dlfcn.h>
#define ANIMATED_POINTER(FUNC_TYPE, OBJECT_POINTER) \
(*((FUNC_TYPE *)(&(OBJECT_POINTER))))
typedef void (* MyFuncType) ();
int main () {
void * handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "func");
ANIMATED_POINTER(MyFuncType, func_obj_ptr)();
}
return 0;
}
或者你可以将它转换为适当函数类型的另一个变量。
#include <dlfcn.h>
#define ANIMATED_POINTER(FUNC_TYPE, OBJECT_POINTER) \
(*((FUNC_TYPE *)(&(OBJECT_POINTER))))
typedef void (* MyFuncType) ();
int main () {
void * handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "func");
MyFuncType func = ANIMATED_POINTER(MyFuncType, func_obj_ptr);
func();
}
return 0;
}
加法
尽管从对象指针到函数指针的转换可能会有问题,但在标准C中允许将不同类型的函数指针相互转换。您可以利用这个权限,并将void (*) (void)
作为宏的固定目标类型。
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
因此,假设有一个仅包含以下内容的
addition.c
库:
int add_numbers (int first, int second) {
return first + second;
}
以下是同一段代码的四个变体。
#1
#include <dlfcn.h>
#include <stdio.h>
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
int main () {
void * handle = dlopen("./addition.so", RTLD_NOW);
if (handle) {
int (* add_numbers) (int, int);
void * func_obj_ptr = dlsym(handle, "add_numbers");
add_numbers = (int (*) (int, int)) UNTYPED_ANIMATED_POINTER(func_obj_ptr);
int result = add_numbers(19, 23);
printf("The result is: %d\n", result);
}
return 0;
}
#2
#include <dlfcn.h>
#include <stdio.h>
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
int main () {
void * handle = dlopen("./addition.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "add_numbers");
int result = ((int (*) (int, int)) UNTYPED_ANIMATED_POINTER(func_obj_ptr))(19, 23);
printf("The result is: %d\n", result);
}
return 0;
}
#3
#include <dlfcn.h>
#include <stdio.h>
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
typedef int (* AdditionFunc) (int, int);
int main () {
void * handle = dlopen("./addition.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "add_numbers");
AdditionFunc add_numbers = (AdditionFunc) UNTYPED_ANIMATED_POINTER(func_obj_ptr);
int result = add_numbers(19, 23);
printf("The result is: %d\n", result);
}
return 0;
}
#4
#include <dlfcn.h>
#include <stdio.h>
#define UNTYPED_ANIMATED_POINTER(OBJECT_POINTER) \
(*((void (**) (void))(&(OBJECT_POINTER))))
typedef int (* AdditionFunc) (int, int);
int main () {
void * handle = dlopen("./addition.so", RTLD_NOW);
if (handle) {
void * func_obj_ptr = dlsym(handle, "add_numbers");
int result = ((AdditionFunc) UNTYPED_ANIMATED_POINTER(func_obj_ptr))(19, 23);
printf("The result is: %d\n", result);
}
return 0;
}
-pedantic
选项,可能会失去其他有用的琐碎警告,这些警告可能指出了我的代码中真正存在的问题。为什么建议去掉-pedantic
选项? - Lone Learnerdlsym()
是POSIX的。如果您使用符合POSIX标准的系统,则可以将void*
转换为函数指针。 - EOF-pedantic
,但是关闭特定的警告。#pragma GCC diagnostic
可以完成这项工作。在其他编译器中,它是#pragma warning(disable: <warn number>)
。 - Frankie_Cvoid*
,然后从中进行memcpy
将无法读取代码字节。 - JDługosz