0

Is there a way to fetch function argument and variable from stack frame For example

foo(int a){
    char z;
    // Can I know what is the name of my local variable and argument inside this function 
}

What I want inside this function is name of arguments and variables. I am also interested in knowing their type too. I also have access to stack frame the function pointer. Can I get this information from function pointer about the argument, variable names and their data type?

I am fine compiling it with -g or another flag for symbols to be present in executable at runtime.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
app
  • 421
  • 1
  • 4
  • 7
  • 1
    The name of the local variable is `z`. Not sure what your problem is. And there is no requirement to use a stack by the standard anyway. And on recent platforms like x86/x64/ARM, it very likely does not. And the type is apparently `int` C does not support dynamic typing (this includes compiler extensions). – too honest for this site Sep 16 '16 at 18:09
  • 1
    Using standard C? No, there is no way. For GCC you might want to [read the documentation](https://gcc.gnu.org/onlinedocs/) to see what (non-standard) functionality it have to help you with this. – Some programmer dude Sep 16 '16 at 18:09
  • [Introspection](https://en.wikipedia.org/wiki/Introspection_(computer_science)) or [reflection](https://en.wikipedia.org/wiki/Reflection_(computer_programming)) are the terms to describe what you want.... – gilez Sep 16 '16 at 19:21
  • @Olaf: You should clarify what you mean by "C does not support dynamic typing (this includes compiler extensions)". Are you saying that C does not support dynamic typing even *with* compiler extensions (*probably* true), or that C does not support dynamic typing ***and*** that the implicit `int` in his code is a compiler extension (false for C89)? – Tim Čas Sep 16 '16 at 20:09
  • @TimČas: I don't talk about ancient version of C, but modern C. That started with C99 and I always refer to standard C which is C11 **only**. The `int` is apparently an error on my side (no idea why I made it), as the type of `z` is clearly `char` Occam's razor: The type is explicitly specified and conversions are only applied in expressions, not declarations. Any compiler doing them would violate the standard. (FYI: an implementation can (e.g. ARM does) use wider internal operations, it just must have the same _observable behaviour_ as the abstract machine of the C standard requires.) – too honest for this site Sep 17 '16 at 00:12

5 Answers5

4

// Can i know what is the name of my local variable and arugment inside this function

If you break inside the function with GDB, and if you've compiled your code with -g, then GDB info locals can tell you the names of local variables and arguments, so clearly that information is available.

The bad news: you'll need to implement 50% of a debugger to get to that info, which is a very non-trivial task.

So the answer is: yes, you can, but it will take so much effort, you really should look for a different solution to whatever you are actually trying to achieve.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
1

The C language itself does not provide this ability, but most executable formats do provide a way to enable this capability, since it's required by debuggers.

You can often get function names from the callstack by using a library like libunwind or libbacktrace, but they're not always portable and they aren't always trivial to perform (execution cost), and they require that you build your program with debug symbols available.

In either case, this is only reliable when you build without optimization. As soon as the optimizer is involved, all bets are off.

For example,

if (pointer && pointer->sub_->something_) {
     pointer->sub_->action();  //1
     return nullptr;
}
/// ...
if (pointer) {
     pointer->sub_->action();  //2
     return nullptr;
}
/// ...

I've actually seen this in a production crash bug: the compiler told us we were accessing a null pointer at //1, which is clearly impossible. We couldn't repro the crash in testing, and the function was particularly long and complex.

What happened was that the compiler collapsed all the pointer->sub_->action(); return nullptrs to one stub function which came from //1, and it was actually the unchecked call at //2 that was the source of the crash.

Between optimizations like this, inlining of functions, whole program optimization, etc, it can be incredibly difficult to accurately tell what is what from the machine state of a running program relative to the source code.

A further complication for stack traces is that in optimized code they often contain a forwarding address. Consider:

int f() {
    g();
    h();
}

If you were to check the callstack in g there is a good chance it would look like it had been called from h: the compiler can manipulate the stack so that when g returns it goes straight to h rather than wastefully returning to f just to get another jump.

Variables are even harder - the optimizer works hard to eliminate them entirely, to usefully shuffle them around in registers, and so forth.

But you could in-theory build your own simple reflection system, wrapping variables in containers. This often gets clumsy, though.

For tracking the call stack:

#include <iostream>
#include <vector>

struct Callsite {
    const char* file_;
    size_t line_;

     static thread_local std::vector<Callsite*> callStack;

    Callsite(const char* file, size_t line) : file_(file), line_(line) {
        callStack.push_back(this);
    }
    ~Callsite() noexcept { callStack.pop_back(); }
};

thread_local std::vector<Callsite*> Callsite::callStack;

#define ENTER  Callsite __callsite_entry(__FILE__, __LINE__);

void f() {
    ENTER;

    for (auto&& stack: Callsite::callStack) {
        std::cout << stack->file_ << ":" << stack->line_ << "\n";
    }
}

int main() {
    ENTER;
    f();
}

Live demo: http://ideone.com/ZAUVib

kfsone
  • 23,617
  • 2
  • 42
  • 74
0

No, there is no way to know the names of variables inside a function. That is just not part of C.

You may be able to compile your code with special flags to generate various additional files containing information about the generated code. Then you can open the files and find the variable names. But that will be highly compiler dependant.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
0

Even if the code was not compiled with debugger support enabled, you can infer variable types including signedness by looking at the associated assembly listing. This is how tools like IDA Pro do it.

fpmurphy
  • 2,464
  • 1
  • 18
  • 22
0

There was just a question about reflection a few days ago in the gdb tag. I have included my answer below. As Employed Russian noted, implementing reflection in a C library appears possible since it is done in gdb.

As others noted, reflection is not built into the C or C++ language. There are a variety of ideas here

However, reflection is possible in C/C++ with a 3rd party library and debugging symbols in the executable or an external file.

The dwarfdump executable more or less does what you are hoping for. With the DWARF information details on function, variables, types, etc are available. In a similar fashion, the libdwarfdump functionality could be used by a process to inspect itself.

Here is a simple manual example:

typedef struct somestruct 
{
   int i;
   int j;
} somestruct ;

int abc(int x, float y , struct somestruct z ){
    char a;
    int b ;
}


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

   struct somestruct z;
   abc(1,1.0f,z);
   return 0;
}

and the partial output from dwarfdump

< 1><0x00000055>    DW_TAG_subprogram
                      DW_AT_external              yes(1)
                      DW_AT_name                  "abc"
                      DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                      DW_AT_decl_line             0x00000009
                      DW_AT_prototyped            yes(1)
                      DW_AT_type                  <0x0000004e>
                      DW_AT_low_pc                0x004004ed
                      DW_AT_high_pc               <offset-from-lowpc>18
                      DW_AT_frame_base            len 0x0001: 9c: DW_OP_call_frame_cfa
                      DW_AT_GNU_all_call_sites    yes(1)
                      DW_AT_sibling               <0x000000ad>
< 2><0x00000076>      DW_TAG_formal_parameter
                        DW_AT_name                  "x"
                        DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                        DW_AT_decl_line             0x00000009
                        DW_AT_type                  <0x0000004e>
                        DW_AT_location              len 0x0002: 916c: DW_OP_fbreg -20
< 2><0x00000082>      DW_TAG_formal_parameter
                        DW_AT_name                  "y"
                        DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                        DW_AT_decl_line             0x00000009
                        DW_AT_type                  <0x000000ad>
                        DW_AT_location              len 0x0002: 9168: DW_OP_fbreg -24
< 2><0x0000008e>      DW_TAG_formal_parameter
                        DW_AT_name                  "z"
                        DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                        DW_AT_decl_line             0x00000009
                        DW_AT_type                  <0x0000002d>
                        DW_AT_location              len 0x0002: 9160:        DW_OP_fbreg -32

With careful study, we can see the fragment defines the function 'abc' with arguements x, y and z.

The type of parameter x is an indirection to a type table with key 0x4e.

Looking elsewhere in the output, we can see the definition for type 0x4e. Type 0x2d is the somestruct which ties back to parameter z.

< 1><0x0000002d>    DW_TAG_structure_type
                      DW_AT_name                  "somestruct"
                      DW_AT_byte_size             0x00000008
                      DW_AT_decl_file             0x00000001 /tmp/dwarf.c
                      DW_AT_decl_line             0x00000003
                      DW_AT_sibling               <0x0000004e>

< 1><0x0000004e>    DW_TAG_base_type
                      DW_AT_byte_size             0x00000004
                      DW_AT_encoding              DW_ATE_signed
                      DW_AT_name                  "int"

The combination of ptrace, ELF, DWARF and the /proc filesystem allow gdb to read static and dynamic information for a process. Another process could use similar functionality to create Reflection functionality.

I have used variants of this strategy to create custom debuggers and memory leak detectors. I have never seen this strategy used for business logic however.

Community
  • 1
  • 1
Matthew Fisher
  • 2,258
  • 2
  • 14
  • 23