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

Get global variable address by its name if compiled with debug information

本文关键字:获取 地址 全局变量 调试 信息 编译 如果      更新时间:2023-10-16

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

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

谢谢。

地图文件呢?它将拥有所有全局变量及其地址的信息。你所要做的就是解析映射文件并获取变量的地址(python可以在这里提供帮助)。

在你的程序中写一个小程序来接受地址和返回值。如果你用它来记录日志,你可以用一个新的线程在套接字事件上完成它,这样你就不会和实际的程序有太多的交集。

我没有这样做,但与objdump/dltool它应该能够获得变量类型信息,我也不确定如何避免非法地址访问(那些会导致SEG和BUS错误)。

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

不需要任何第三方程序或大型库,您可以使用几个宏添加简单的反射,这取决于您希望它有多丑

下面是一个工作示例:

#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)。关于变量的一些基本信息将自动存储在global_decl中的std::map中,并且可以从那里检索。

它涉及一堆琐碎的hack,可能不应该这样使用。它更像是一个指针,让你知道如何做到这一点。

该方法的优点是开销几乎为零。您的程序读/写变量时不需要调用样板代码。最糟糕的部分是宏。

如果您正在寻找一个不需要修改代码的解决方案,您最好使用gdb或类似的工具。