如果使用调试信息编译,则通过其名称获取全局变量地址

Posted

技术标签:

【中文标题】如果使用调试信息编译,则通过其名称获取全局变量地址【英文标题】:Get global variable address by its name if compiled with debug information 【发布时间】:2014-08-08 03:42:21 【问题描述】:

如果我使用 gcc 编译一些 C/C++ 程序并打开 -g 和/或 -ggdb,那么如果我使用 gdb 启动程序,我可以在 gdb 中打印变量值。

我的问题是,如果没有 gdb,我可以从程序内部实现相同的目标吗?在运行时,给定变量的名称(表示为运行时字符串),是否可以读取调试信息,然后得到变量的地址以及类型信息?

谢谢。

【问题讨论】:

我没有说清楚,我的意思是在运行时给定变量的名称(字符串或 const char*)。 您使用的是什么平台?如果是某些 UNIX 变体,请阅读 dlsym 的手册页。 @DavidSchwartz,指向dlsym 的好指针。类型信息呢? C 没有那种内省。 @DavidSchwartz 我的意思是 gdb 怎么知道某个变量的类型呢?我假设它是通过读取二进制文件中的调试信息来完成的,对吗?如果这是正确的,那么理论上应该是可能的,对吧? 【参考方案1】:

地图文件怎么样?它将包含所有全局变量及其地址的信息。您所要做的就是解析地图文件并获取变量的地址(python 可以在这里提供帮助)。

在你的程序中编写一个小程序来接受地址和返回值。如果您将它用于类似目的的日志记录,则可以使用新线程通过套接字事件执行此操作,这样您就不会过多地干扰实际程序。

我没有这样做,但是使用 objdump / dltool 它应该能够获取变量类型信息,而且我不确定如何避免非法地址访问(会导致 SEG 和 BUS 错误的访问)。

第二个想法是什么阻止您使用 GDB 本身?您可以使用图像的剥离版本来运行和调试启用 sym 的 inage 以获取变量类型和地址信息。

【讨论】:

我希望能够在运行时从程序内部通过变量名访问变量,而无需太多开销。使用 gdb 需要由 gdb 启动或附加进程,这使得一切都变得超级慢。也有可能运行程序的机器与构建它的机器不同,因此不需要安装 gdb。【参考方案2】:

无需任何第三方程序或大型库,您可以使用一些宏添加简单的反射,具体取决于您希望它有多丑

这是一个工作示例:

#include <map>
#include <stdio.h>
#include <iostream>
#include <string>

#define DEBUG_VAR_NAMES //undef to remove the tracking

struct GlobalRecord //will hold info about variables
    const char* name;
    const char* type;
    const void* addr;
;

#ifdef DEBUG_VAR_NAMES


static const GlobalRecord* global_decl( const char* name, bool set, const GlobalRecord* record )
    static std::map<const char*, const GlobalRecord*> globals; //holds pointers to all record structs

    if( set )
        globals[name] = record;

    const auto it = globals.find( name );

    if( it == globals.end() )
        return nullptr;     //just return nullptr if a var could not be found
    else
        return it->second;



static GlobalRecord global_init( const char* type, const char* name, void* addr, const GlobalRecord* record ) 
    global_decl( name, true, record );
    return GlobalRecord name, type, addr ;


#define COMMA ,
#define DECLARE_GLOBAL_INIT(type, name, init) type name = init; GlobalRecord name##_rec = global_init( #type, #name, (void*)&name, & name##_rec)
#define DECLARE_GLOBAL(type, name) type name; GlobalRecord name##_rec = global_init( #type, #name, (void*)&name, & name##_rec)
#define GET_GLOBAL(name) global_decl(name, false, nullptr)

#else

#define COMMA ,
#define DECLARE_GLOBAL_INIT(type, name, init) type name = init;
#define DECLARE_GLOBAL(type, name) type name;
#define GET_GLOBAL(name) ((const GlobalRecord*) nullptr) //This is a bad idea(TM).

#endif


//SAMPLE HERE
//Declare 3 global vars for testing.

//declaring a variable is pretty simple.
//it's a macro call with <type>, <name>, <optional init value> as arguments:
DECLARE_GLOBAL_INIT( int, my_int, 5 ); //instead of: int my_int = 5;
DECLARE_GLOBAL_INIT( std::string, my_string, "hi there" ); //instead of std::string my_string = "hi there";
DECLARE_GLOBAL( std::map<int COMMA int>, my_map ); //<- commas must be replaced with a macro


void print_var( const char* name )
    std::cout << '\n';

    if( GET_GLOBAL( name ) == nullptr )
          std::cout << "Var " << name << " not found.\n";  
          return;
    

    std::cout << "Variable: " << GET_GLOBAL( name )->name << "\n";
    std::cout << "The address of " << name << " is recorded as: " << GET_GLOBAL( name )->addr << "\n";
    std::cout << "The type of " << name << " is recorded as : " << GET_GLOBAL( name )->type << "\n";


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


    print_var( "my_int" );
    print_var( "my_string" );
    print_var( "my_map" );
    print_var( "my_double" );

    return 0;

基本上所有的全局变量都需要声明为一个宏,比如DECLARE_GLOBAL(type, name)。然后,有关变量的一些基本信息将自动存储在 std::map 中的 global_decl 中,并且可以从那里检索。

它涉及一堆琐碎的hacky,可能不应该那样使用。它更像是一个指针,让您了解如何完成它。

这种方法的好处是开销几乎为零。您的程序无需调用样板代码即可读取/写入变量。不好的部分是宏。

如果您正在寻找一个完全不更改代码的解决方案,那么最好使用 gdb 或类似工具。

【讨论】:

我知道这种方法。我在这个问题中要问的正是如何避免这种手动工作,而是自动进行,而不需要修改代码。但无论如何,感谢您花时间写长答案。

以上是关于如果使用调试信息编译,则通过其名称获取全局变量地址的主要内容,如果未能解决你的问题,请参考以下文章

c语言转化为arm汇编指令

如何获取目标文件中类成员的符号?

如何从 Delphi 程序或编译器生成的调试信息中提取局部变量信息(地址和类型)?

调试信息是不是显示 C++/MSVC 中的代码?

JavaScript:在 IE 中列出全局变量

堆地址范围内全局变量的地址