问题 在C(gcc)中获取指向当前函数的指针?


gcc中有一个魔术变量,它持有一个指向当前函数的指针吗?

我想有一种表包含每个函数指针的一组信息。

我知道有一个__func__变量包含当前函数的名称作为字符串但不作为函数指针。

这不是为了调用函数而是仅仅用作索引。

编辑 基本上我想做的是能够在执行当前函数之前运行嵌套函数(并且还捕获返回以执行某些操作。) 基本上,这就像__cyg_profile_func_enter和__cyg_profile_func_exit(检测功能)......但问题是这些检测功能是全局的而不是功能专用的。

编辑 在linux内核中,您可以使用 unsigned long kallsyms_lookup_name(const char *name) 从 include/linux/kallsyms.h ...注意 CONFIG_KALLSYMS 必须激活选项。


7574
2018-01-28 13:41


起源

请问您为什么要这样做,可能有更合适的选择? - Hassan Syed
我希望能够在此函数内注册和重放(以任何顺序)嵌套函数,而无需创建实际代码。问题是我正在生成嵌套函数,但我真的不知道我生成嵌套函数的函数的名称。我想避免修改解析器。 - LB40
设计中的函数量是否有限?或者你会添加功能吗? 特别指定 ? - Hassan Syed
不,我会以临时方式添加功能 - LB40
我建议使用C ++仿函数习惯用法 - 如果可以的话,与普通C相比,它不会有太大的开销。你不必使用boost - 仿函数很容易实现。 loki-lib.sourceforge.net/index.php?n=Pattern.Command 我的答案中有更多链接:D - Hassan Syed


答案:


这是一个获取调用者地址的技巧,它可能会被清理一下。 依赖于 用于获取标签价值的GCC扩展

#include <stdio.h>

#define MKLABEL2(x) label ## x
#define MKLABEL(x) MKLABEL2(x)
#define CALLFOO do { MKLABEL(__LINE__): foo(&&MKLABEL(__LINE__));} while(0)

void foo(void *addr)
{
    printf("Caller address %p\n", addr);
}

int main(int argc, char **argv)
{
    CALLFOO;
    return 0;
}

1
2018-01-28 17:30



运用 __builtin_return_address(0) 内 foo()而不是传递标签的地址,是一种更简单的做同样事情的方式。 (这给出了调用后指令的地址 foo()而不是在通话之前的恒定负载的地址;但这些都不是函数的实际地址......) - Matthew Slattery
@Matthew:这比这个黑客好多了。 - Hasturkun


void f() {
   void (*fpointer)() = &f;
}

6
2018-01-28 13:45



是的,这就是你如何硬编码函数内部的信息:真正的D摆动和环形交叉。这个解决方案并不像在函数外部执行此工作那样优雅 - 而是可能会生成代码。 - Hassan Syed
:-D ......我想要更通用的东西......不知道f的名字。你可以在任何函数中使用的一种泛型线... - LB40
@LB是的,这个解决方案很乱,产生一个框架可能是你最好的选择(但请进一步解释问题)。 - Hassan Syed
随着有关您实际尝试实现的内容的更多信息,您可以获得替代方案。 - David Rodríguez - dribeas
@LB在您的问题中发布一些代码,说明您希望使用此类功能(如果存在)的样子。


#define FUNC_ADDR (dlsym(dlopen(NULL, RTLD_NOW), __func__))

并编译你的程序

gcc -rdynamic -o foo foo.c -ldl

3
2018-01-28 15:48



: - )...偷偷摸摸,但我担心更改编译标志以包含所有符号+包含dlfcn.h是不可能的。 - LB40


我认为你可以使用字符串(函数名称)作为键来构建表,然后通过比较来查找 __func__ 内置变量。

要强制使用有效的函数名,可以使用获取函数指针的宏,对其执行一些虚拟操作(例如,将其分配给兼容的函数类型临时变量)以检查它是否确实是有效的函数标识符,然后进行字符串化(用#)作为键之前的函数名。

更新:

我的意思是:

typedef struct {
  char[MAX_FUNC_NAME_LENGTH] func_name;
  //rest of the info here
} func_info;

func_info table[N_FUNCS];

#define CHECK_AND_GET_FUNC_NAME(f) ({void (*tmp)(int); tmp = f; #f})

void fill_it()
{
  int i = -1;
  strcpy(table[++i].func_name, CHECK_AND_GET_FUNC_NAME(foo));
  strcpy(table[++i].func_name, CHECK_AND_GET_FUNC_NAME(bar));
  //fill the rest
}

void lookup(char *name) {
  int i = -1;
  while(strcmp(name, table[++i]));
  //now i points to your entry, do whatever you need
}

void foo(int arg) {
  lookup(__func__);
  //do something
}

void bar(int arg) {
  lookup(__func__);
  //do something
}

(代码可能需要一些修复,我还没有尝试编译它,它只是为了说明这个想法)


2
2018-01-28 15:16



我不太明白你的意思。我可能错了但是 FUNC 是一个字符串(AFAIK这不是预处理器变量,而是C标准的一部分)和节点标识符,这等于带引号的函数的名称。获取函数指针的宏是什么? - LB40
@LB预处理器 是 C标准的一部分。据我所知 __FUNC__ 是一个预处理器变量/宏,就像 __FILE__ 和 __LINE__。关于获取指针,这是我的解决方案试图避免的。我会更新答案,使其更清晰。 - fortran
实际上 __func__,而且不像 __FILE__ 和 __LINE__ 它是预定义的标识符(不是宏)。它必须在编译阶段完成,而不是预处理,因为函数的范围(以及你在哪个内部,如果有的话)取决于预处理的结果。 - caf
很高兴知道,但实际上有一个叫做的宏 __FUNCTION__ 这就是我的意思......我对阅读感到困惑 __func__ 在问题中。我会更新答案。 - fortran
我不明白为什么,但我以前的一条评论被删除了。要了解更多_功能_ 去 : gcc.gnu.org/onlinedocs/gcc/Function-Names.html - LB40


如果您使用C ++,以下信息可能对您有所帮助:

对象是打字的, 函子 被包装为对象的函数, RTTI 允许在运行时识别类型。

Functors带有运行时开销,如果这对你来说是一个问题我会建议使用代码生成来硬编码知识或利用仿函数的OO-heirarchy。


1
2018-01-28 13:58





不,该功能不了解自己。您将不得不构建您自己正在讨论的表,然后如果您希望函数知道自己,则必须将索引作为参数传递给全局表(或函数的指针)。

注意:如果要执行此操作,则应该具有一致的参数命名方案。


0
2018-01-28 13:42





如果你想以'通用'方式做到这一点,那么你应该使用你已经提到的设施(__cyg_profile_func*)因为这是他们的目的。其他任何东西都必须像你的个人资料一样临时。

老实说,以通用方式(使用过滤器)执行操作可能比您将即时插入的任何新方法更不容易出错。


0
2018-01-28 15:00



当然,但没有办法为不同的行为选择不同的行为,仪器功能标志不是我想转向的地方...... - LB40


您可以使用捕获此信息 setjmp()。由于它可以保存足够的信息以返回当前功能,因此必须在提供的信息中包含该信息 jmp_buf

这种结构是非常不可移植的,但是你明确地提到了GCC,这可能不是阻塞问题。看到 这个GCC / x86 例子来了解它是如何工作的。


0
2018-01-28 15:43



setjmp()不是神奇的 - 它只知道当前的指令指针 - 而不是当前函数的地址...... - Hannes Landeholm


如果你想做代码生成,我会建议 GSLGen 来自Imatix。它使用XML来构建代码模型,然后使用简单的PHP(如自上而下的生成语言)来吐出代码 - 它已被用于生成C代码。

我个人一直在玩lua来生成代码。


0
2018-01-28 16:49