Created
November 26, 2023 18:56
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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