如果使用调试信息编译,则通过其名称获取全局变量地址
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 或类似工具。
【讨论】:
我知道这种方法。我在这个问题中要问的正是如何避免这种手动工作,而是自动进行,而不需要修改代码。但无论如何,感谢您花时间写长答案。以上是关于如果使用调试信息编译,则通过其名称获取全局变量地址的主要内容,如果未能解决你的问题,请参考以下文章