【问题标题】:variable name, function arguments at run time in CC中运行时的变量名,函数参数
【发布时间】:2017-01-23 03:48:35
【问题描述】:

是否可以在 C 程序中在运行时知道函数参数和变量的名称类型?例如,如果我有一个函数:

int abc(int x, float y , somestruct z ){
    char a;
    int b ;
}

我能知道在这个函数abc()里面,参数和变量的名称是什么ie在这种情况下是xyza,@ 987654328@,它们的类型为intfloatsomestructcharint

如果有别的功能说:

float some_func(specialstruct my_struct, int index){

} 

我应该知道参数名称是my_structindex,类型是specialstructint

我在运行时需要这些信息?

我可以访问基指针和返回地址,我可以使用上面的指针获取所需的信息吗?

我能够使用返回地址和dladdr() 函数提取函数名。

我看到GDB 这样做了,所以应该可以提取此信息吗?

【问题讨论】:

  • 为什么在运行时需要这些名称?你能用它们做什么?
  • C 是一种编译语言。一旦程序被编译(或者换句话说,一旦它被转换成机器代码),所有的变量名、函数名等都消失了。所以答案是:不。另一方面,GDB 知道变量和函数名称,因为程序已经用特殊选项编译,但这仅对调试有用。
  • gdb 使用编译器生成的调试信息。见eli.thegreenplace.net/2011/02/07/…
  • @MichaelWalz:是否编译不是问题。编译语言还可以将属性“反映”回代码本身。反射是 c++17 的一部分,但据我所知已被删除。其他语言有此功能。
  • @Klaus 正确,但在 C 中没有任何反映。

标签: c linux gdb bfd dladdr


【解决方案1】:

正如其他人所指出的,反射并未内置于 C 或 C++ 语言中。各种想法here

但是,反射可以在 C/C++ 中使用第三方库和可执行文件或外部文件中的调试符号进行。

dwarfdump 可执行文件或多或少可以满足您的期望。使用 DWARF 信息,可以获取有关函数、变量、类型等的详细信息。以类似的方式,进程可以使用 libdwarfdump 功能来检查自身。

这是一个简单的手动示例:

typedef struct somestruct 
{
   int i;
   int j;
} somestruct ;

int abc(int x, float y , struct somestruct z ){
    char a;
    int b ;
}


int main(int argc, char* argv[])
{

   struct somestruct z;
   abc(1,1.0f,z);
   return 0;
}

以及 dwarfdump 的部分输出

< 1><0x00000055>    DW_TAG_subprogram
                      DW_AT_external              yes(1)
                      DW_AT_name                  "abc"
                      DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                      DW_AT_decl_line             0x00000009
                      DW_AT_prototyped            yes(1)
                      DW_AT_type                  <0x0000004e>
                      DW_AT_low_pc                0x004004ed
                      DW_AT_high_pc               <offset-from-lowpc>18
                      DW_AT_frame_base            len 0x0001: 9c: DW_OP_call_frame_cfa
                      DW_AT_GNU_all_call_sites    yes(1)
                      DW_AT_sibling               <0x000000ad>
< 2><0x00000076>      DW_TAG_formal_parameter
                        DW_AT_name                  "x"
                        DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                        DW_AT_decl_line             0x00000009
                        DW_AT_type                  <0x0000004e>
                        DW_AT_location              len 0x0002: 916c: DW_OP_fbreg -20
< 2><0x00000082>      DW_TAG_formal_parameter
                        DW_AT_name                  "y"
                        DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                        DW_AT_decl_line             0x00000009
                        DW_AT_type                  <0x000000ad>
                        DW_AT_location              len 0x0002: 9168: DW_OP_fbreg -24
< 2><0x0000008e>      DW_TAG_formal_parameter
                        DW_AT_name                  "z"
                        DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                        DW_AT_decl_line             0x00000009
                        DW_AT_type                  <0x0000002d>
                        DW_AT_location              len 0x0002: 9160:        DW_OP_fbreg -32

仔细研究,我们可以看到片段定义了函数'abc',参数为x、y和z。

参数 x 的类型是对键为 0x4e 的类型表的间接引用。

查看输出中的其他地方,我们可以看到类型 0x4e 的定义。类型 0x2d 是与参数 z 相关联的 somestruct。

< 1><0x0000002d>    DW_TAG_structure_type
                      DW_AT_name                  "somestruct"
                      DW_AT_byte_size             0x00000008
                      DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                      DW_AT_decl_line             0x00000003
                      DW_AT_sibling               <0x0000004e>

< 1><0x0000004e>    DW_TAG_base_type
                      DW_AT_byte_size             0x00000004
                      DW_AT_encoding              DW_ATE_signed
                      DW_AT_name                  "int"

ptrace、ELF、DWARF 和 /proc 文件系统的组合允许 gdb 读取进程的静态和动态信息。另一个进程可以使用类似的功能来创建反射功能。

我已使用此策略的变体来创建自定义调试器和内存泄漏检测器。然而,我从未见过这种策略用于业务逻辑。

【讨论】:

    【解决方案2】:

    在 C 中并没有真正的本地方式来执行此操作。在其他语言中,您要寻找的是反射。你可以用宏和一些技巧来解决这个问题,但在一个基本原则上,你需要在编译时知道变量名和参数。

    【讨论】:

    • @Mat 显然,我在那里做了一个脑残。我指的是启动 program 的参数。更正。
    【解决方案3】:

    共享库函数dlsymdladdr 提供了有限的内省方式,提供名称到地址的转换,反之亦然。然而,这不是 C 语言的一部分,而是 OS 动态加载程序提供的一个函数。但是,您不能推断,例如,您找到的符号是变量还是函数。

    backtrace 等人构成了对标准的 GNU 扩展,允许分析函数的调用堆栈(调用历史)。如果二进制文件中仍然存在符号(函数名称),backtrace_symbols 将允许您检索它们。

    __LINE____FILE__ 预定义宏也提供了一种转储您所在位置的方法,并且可以作为一些非常有用的宏进行跟踪的基础。

    就是这样。 C 没有提供比这更多的内省。参数名称和类型在二进制文件中消失了,函数签名和函数结果类型也消失了。

    【讨论】:

      【解决方案4】:

      C 中没有反射或类似的东西。如果你想要这样的工具 - 你应该为此目的设计一些实用程序、宏并使用特殊的编码规则来达到预期的效果。但是 IMO - 它不会是可读和可理解的 C 代码。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-20
        • 2021-12-09
        • 2016-01-01
        • 1970-01-01
        • 2010-12-07
        • 2014-12-27
        • 1970-01-01
        • 2014-10-14
        相关资源
        最近更新 更多