如何动态分配功能代码?

时间:2019-01-14 13:14:33

标签: c pointers callback function-pointers

在C语言中,将函数指针用作来自某些库的回调的常用方法是为用户上下文添加void*指针:

void (*fp)(void* ctx);

这允许库使用上下文ctx调用回调。

假设我使用的库在回调中不包含上下文指针。我需要为每个上下文提供一个回调。在C中动态分配函数指针以提供回调的最便捷方法是什么?我如何malloc()可以从库中调用的功能代码?

例如:

typedef void (*my_fp_t)(char);

my_fp_t fp = (my_fp_t) malloc(sizeof(...));
init_function_ptr(fp, "Hello, there");
my_library_callback(fp);
...

void my_library_callback(my_fp_t fp) {
  fp('a'); // prints "Hello, there"
}

2 个答案:

答案 0 :(得分:2)

假设您希望分配实际的功能代码,而不是功能指针。编写引导加载程序和类似内容时的完成方式:

  • 对编译器和ABI使用的基础调用约定不确定。您需要确切地知道在堆栈框架中哪些寄存器和/或位置在函数调用和返回时一切正常。
  • 通过在C编译器中编写函数并仔细复制/粘贴程序集,或通过在汇编程序中手动编写函数,来生成汇编程序代码。
  • 将汇编程序转换为操作码。
  • 确保有一块内存可以存储数据和执行代码。这也取决于ABI和MMU的设置。
  • 了解如何在此自定义段中分配内存。通常涉及一些链接描述文件摆弄和各种非标准关键字。 #pragma __declspec __attribute__等等,上面放糖。高度特定于编译器。
  • 将内存作为原始uint8_t func [n]数组分配给该区域,以十六进制形式包含原始OP代码。
  • 关闭严格的别名和其他此类邪恶的指针转换之类的东西。从对象指针到函数指针时,请确保您的编译器具有一些定义良好的非标准扩展。
  • 通过((func_ptr)func) ()调用代码。

如您所愿,根据系统的不同,这可能是一项艰巨的任务。对于使用嵌入式系统编译器的小型微控制器,这在某种程度上很容易实现。对于诸如x86或PowerPC之类的更复杂的系统,使用诸如gcc之类的编译器,要少得多。您将依靠各种错误指定的行为,并且代码将完全是特定于系统的。

答案 1 :(得分:0)

我想我得到了答案。

真正的问题是在一个没有收到回调的回调中获取void*上下文指针。而且我不能使用(原则上)全局变量,因为可能会调用许多回调,而且我无法确定是哪一个。

我可以采取与JIT VM相同的方法:为每个可能的void * ctx值动态生成机器代码,例如:

void do_stuff(void* ctx) {
  // Finally have the ctx!
}

void* my_ctx;
my_fn_ptr p = allocate_function_that_calls_f_with_ctx(do_stuff, ctx);
library_register_callback_fn(p);

这里p是带有签名void (*)(void)的函数的函数指针,当依次调用时,它将调用f(ctx)

但是,这仅用于库的回调,并且回调的数量很少。然后,我可以编写一堆函数,每个函数取一个表中的void *,每个函数取自不同的索引:

void do_stuff_0(void) { do_stuff(do_stuff_TABLE[0]); }
void do_stuff_1(void) { do_stuff(do_stuff_TABLE[1]); }
void do_stuff_2(void) { do_stuff(do_stuff_TABLE[2]); }
...

我可以编写一些简单的脚本,在新的.c文件中写入从0到成千上万的所有函数。然后,我需要跟踪已注册的功能和尚未注册的功能,并提供某种机制以选择下一个可用功能并释放不再需要的功能。