简体   繁体   中英

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

If I compile some C/C++ program using gcc with -g and/or -ggdb turned on, then if I start the program using gdb, I can print variable value inside gdb.

My question is, without gdb, can I achieve the same thing from inside the program? At runtime, given the name of the variable (represented as a runtime string), is it possible to read the debug information and then get the address of the variable as well as the type information?

Thanks.

How about about map file ? It will have info of all globals and its address. All you have to do is parse the map file and get address of the variable (python can help here).

In your program write a small routine to accept address and return value. If you are using it for logging like purpose you can do it over sockets event with a new thread so you don't interfear much with actual progarm.

I have not done this but with objdump / dltool it should be able to get the variable type information, also I am not sure how you can avoid illegal address access (ones that will cause SEG and BUS errors).

On the secod thought what is stopping you from using GDB itself ? You could use stripped version of the image to run and debug sym enabled inage to get variable type and address infomation.

Without any third-party programs or big libraries, you can add simple reflection with a few macros, depending on how ugly you'd like to have it

Here's a working sample:

#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;
}

Basically all global variables need to be declared as a macro such as DECLARE_GLOBAL(type, name) . Some basic info about the variable will then automatically be stored inside a std::map in global_decl and can be retrieved from there.

It involves a bunch of petty hackery and should probably not be used just like that. It's more of a pointer to give you an idea of how it could be done.

The good part about that method is that the overhead is practically zero. There is no boilerplate code that would have to be called for your program to read/write variables. The bad part is macros.

If you are looking for a solution without changing your code at all, you're probably better off using gdb or a similar tool.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM