Skip to content

Instantly share code, notes, and snippets.

@nhaehnle
Created November 26, 2023 18:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nhaehnle/754a914587f5da52c37c3481eb806996 to your computer and use it in GitHub Desktop.
Save nhaehnle/754a914587f5da52c37c3481eb806996 to your computer and use it in GitHub Desktop.
gdb script for loading symbol files by correlating stack frames with /proc/$pid/maps
# gdb script:
#
# source /path/to/this/script
# auto-add-symbol-files
#
# Tries to load symbol files by correlating stack frames with /proc/$pid/maps.
# Useful when debugging native code issues in programs running under Wine/Proton.
#
# Author: Nicolai Hähnle <nhaehnle@gmail.com>
# Use at your own risk, blah blah.
import gdb
import re
import subprocess
# RE for /proc/$pid/maps
RE_map = re.compile(r'([0-9a-f]+)-([0-9a-f]+) [r-][w-][x-]p ([0-9a-f]+) [0-9:]+ [0-9]+ +(.*)$')
# RE for output of readelf
RE_section = re.compile(r' *\[ *[0-9]+\] ([^ ]+) +(?:[^ ]+) +([0-9a-f]+) +(?:[0-9a-z]+)')
class AutoAddSymbolFiles(gdb.Command):
def __init__(self):
super(AutoAddSymbolFiles, self).__init__("auto-add-symbol-files", gdb.COMMAND_USER)
def invoke(self, args, from_tty):
print("Invoked blah", args)
progspace = gdb.current_progspace()
pid = gdb.selected_inferior().pid
frame = gdb.newest_frame()
depth = 0
max_depth = None
while frame is not None:
if frame.function() is None:
pc = frame.pc()
if progspace.objfile_for_address(pc):
# We have no debug info even though an objfile has already been loaded
# for the address. Unwinding isn't going to work properly, so just bail.
print(f'Missing debug info at PC {pc:x}, stopping here.')
break
if depth == max_depth:
print(f'Re-scanning the same frame, something must have gone wrong.')
break
# See if we can find an objfile
with open(f"/proc/{pid}/maps", "r") as maps:
for map_line in maps:
m = RE_map.match(map_line)
if m is None:
continue
begin = int(m.group(1), base=16)
end = int(m.group(2), base=16)
file_offset = int(m.group(3), base=16)
file_name = m.group(4)
if pc < begin or end <= pc:
continue
print(f'Found candidate object file {file_name} for PC {pc:x}')
break
else:
print(f'Could not find candidate object file for PC {pc:x}')
break
# We found our candidate file. Get the section info by shelling out to readelf
out = subprocess.check_output(['readelf', '-S', file_name]) \
.decode().split('\n')
for out_line in out:
m = RE_section.match(out_line)
if m is None:
continue
section = m.group(1)
section_file_offset = int(m.group(2), base=16)
if section == '.text':
break
else:
print(f'Did not find .text section')
break
# Found the .text section
text_address = begin - file_offset + section_file_offset
print(f'Loading {file_name} with .text at {text_address:x}')
gdb.execute(f'add-symbol-file {file_name} {text_address}')
# Re-compute the frames
max_depth = depth
frame = gdb.newest_frame()
depth = 0
continue
frame = frame.older()
depth += 1
AutoAddSymbolFiles()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment