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

时间:2014-08-08 03:42:21

标签: c++ c gdb

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

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

感谢。

2 个答案:

答案 0 :(得分:0)

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

在你的程序中编写一个小例程来接受地址和返回值。如果您正在使用它进行类似目的的记录,您可以使用新线程在套接字事件上执行此操作,这样您就不会对实际的程序进行很多干预。

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

关于什么阻止你使用GDB本身?您可以使用图像的剥离版本来运行和调试sym enabled inage以获取变量类型和地址信息。

答案 1 :(得分:0)

如果没有任何第三方程序或大型库,您可以使用一些宏添加简单的反射,具体取决于您希望拥有它的难度

这是一个工作样本:

#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的{​​{1}}内,并可从那里检索。

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

关于该方法的好处是开销实际上为零。没有必须为程序调用读/写变量的样板代码。不好的部分是宏。

如果您正在寻找的解决方案,而根本不需要更改代码,那么您最好使用gdb或类似的工具。