1

Background: I'm trying to simplify my answer to the question c++ - Calling a lambda function in GDB - Stack Overflow.

Consider this simple program

int main(){
    auto a=[&](int x){return x+1;};
}

When it's compiled (for example to a.out), I can see

$ nm --demangle a.out  |grep lambda
000000000000113a t main::{lambda(int)#1}::operator()(int) const

$ nm  a.out  |grep 113a
000000000000113a t _ZZ4mainENKUliE_clEi

So there's a symbol _ZZ4mainENKUliE_clEi for the lambda operator() method in the text (code) section.

Given the exact name, I can print its value in gdb, and tab-completion also lists the symbol:

$ gdb -q ./a.out

(gdb) print _ZZ4mainENKUliE_clEi
$1 = {int (const struct {...} * const, int)} 0x55555555513a <operator()(int) const>

(gdb) print 'main::{lambda(int)#1}::operator()(int) const'
$2 = {int (const struct {...} * const, int)} 0x55555555513a <operator()(int) const>

But how can I find the symbol without using the external executable nm (inside gdb itself)? Answers using the Python API is preferred.


Failed attempts

As suggested in c - Ask GDB to list all functions in a program - Stack Overflow:

(gdb) info functions .*lambda.*
All functions matching regular expression ".*lambda.*":

File /build/gcc/src/gcc/libstdc++-v3/src/c++11/compatibility-thread-c++0x.cc:
        void std::once_flag::_Prepare_execution::_Prepare_execution<std::call_once<void (std::thread::*)(), std::
thread*>(std::once_flag&, void (std::thread::*&&)(), std::thread*&&)::{lambda()#1}>(void (std::thread::*&)())::{l
ambda()#1}::_FUN();

which is not the one I want, and

(gdb) info functions .*ZZ4.*
All functions matching regular expression ".*ZZ4.*":

doesn't match anything at all.

Also tried other things in Debugging with GDB - Examining the Symbol Table, maintenance print symbols doesn't have it either.

In Symbols In Python (Debugging with GDB), gdb.lookup_global_symbol("_ZZ4mainENKUliE_clEi") and gdb.lookup_static_symbol("_ZZ4mainENKUliE_clEi") both returns None.

file ./a.out in gdb does not help (suggested by GDB does not see symbols )

user202729
  • 3,358
  • 3
  • 25
  • 36
  • Also note that before the program starts the relocate part did not execute, and address may display as `0x11dec` instead of `0x555555571dec`. See [GDB: why does memory mapping change after run? - Stack Overflow](https://stackoverflow.com/questions/41208640/gdb-why-does-memory-mapping-change-after-run) – user202729 Dec 19 '21 at 03:04
  • Looks like `maint print symbols` can be used, but usually the output is huge and it isn't even very straightforward how to use it. – user202729 Dec 20 '21 at 05:03
  • Somewhat related but for variables [c - Get all global variables/local variables in gdb's python interface - Stack Overflow](https://stackoverflow.com/questions/30013252/get-all-global-variables-local-variables-in-gdbs-python-interface) – user202729 Mar 10 '22 at 02:42

1 Answers1

0

This solution finds the address of the .text section by parsing the textual output of info files, then find the symbol correspond to each address by parsing the output of info symbol gdb command.

Because it parses the textual output, in some special cases it might break. If anyone have any better solution/improvement please suggest/post a new answer.

I took a look at https://sourceware.org/gdb/current/onlinedocs/gdb/Progspaces-In-Python.html#Progspaces-In-Python / https://sourceware.org/gdb/current/onlinedocs/gdb/Objfiles-In-Python.html but can't find any.

I can't use block_for_pc, explained in this comment.


Run the Python code. The result is collected into the list symbols.

Read the comments in the code for more details how it works.

# (setup measure time)
import time
start=time.time()

# step 1. Find the limit of `.text`. Store in (a, b).
import re
[[a, b]]=[
        (int(match[1], 16), int(match[2], 16))
        for line in gdb.execute("info files", to_string=True).splitlines()
        for match in [re.fullmatch(r"\s*(0x[0-9a-f]+) - (0x[0-9a-f]+) is \.text", line)]
        if match
        ]

# Step 2. Iterate through symbols.
symbols=[]
mismatch=0
i=b-1
while i>=a:
    line=gdb.execute("info symbol "+hex(i), to_string=True)
    if line=='No symbol matches '+hex(i)+'.\n':
        # There's no symbol here. Unfortunately I can't find any way to quickly skip through it.
        i-=1
        mismatch+=1
        continue
    # Determine the symbol and the offset.
    match=re.fullmatch(r"(.+) \+ (\d+) in section \.text of .*\n?", line)
    if match:
        symbol=match[1]
        offset=int(match[2])
    else:
        match=re.fullmatch(r"(.+) in section \.text of .*\n?", line)
        symbol=match[1]
        offset=0
    # collect the symbol into the list.
    i-=offset
    symbols.append((symbol, hex(i)))
    i-=1

# (report time taken)
print(time.time()-start, len(symbols), mismatch)

For a reasonably large executable, nm takes 0.042s, while the script takes 0.778s and report 5769 symbols and 44171 misses. (the misses takes most of the time)

user202729
  • 3,358
  • 3
  • 25
  • 36