[PATCH] tools: Add gdbinit helper with LoadSymbolFiles command.

Rémi Bernon rbernon at codeweavers.com
Mon Jun 21 14:33:09 CDT 2021


To help gdb reload symbol files from /proc/<pid>/maps, making it
possible to load debug info for ELF and PE modules for Wine processes.

When sourced (from ~/.gdbinit for instance), this adds a new
"load-symbol-files" command (with an "lsf" alias), which automatically
calls add-symbol-file on every mapped file that can be read as ELF or
PE, with the correct section offset.

The command has to be run manually, for instance after executing for
a while, to load new modules that may have been loaded, as there's no
way for gdb to be notified of such changes automatically.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---

This is some script I've been using for a while, and more or less broken
versions are also used by other people, so I figured maybe it would be
better to have a proper working version officially distributed with Wine
source instead, as it's pretty useful for debugging Wine under gdb.

It's still a bit manual to use, as gdb cannot easily be notified of
dynamic library loading [1], but I think it's much better than having
nothing.

[1] in theory there's ways to do it using systemtap probes, but it's
going to be hard to make it work, especially for PE modules.

 tools/gdbinit.py | 108 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)
 create mode 100644 tools/gdbinit.py

diff --git a/tools/gdbinit.py b/tools/gdbinit.py
new file mode 100644
index 00000000000..265d97722b7
--- /dev/null
+++ b/tools/gdbinit.py
@@ -0,0 +1,108 @@
+#!/bin/env python3
+
+# Copyright 2021 Rémi Bernon for CodeWeavers
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+from __future__ import print_function
+
+import gdb
+import re
+import subprocess
+import sys
+
+class LoadSymbolFiles(gdb.Command):
+  'Command to load symbol files directly from /proc/<pid>/maps.'
+  
+  def __init__(self):
+    sup = super(LoadSymbolFiles, self)
+    sup.__init__('load-symbol-files', gdb.COMMAND_FILES, gdb.COMPLETE_NONE,
+                 False)
+
+    self.libs = {}
+    gdb.execute('alias -a lsf = load-symbol-files', True)
+
+  def invoke(self, arg, from_tty):
+    pid = gdb.selected_inferior().pid
+    if not pid in self.libs: self.libs[pid] = {}
+
+    def command(cmd, confirm=from_tty):
+      to_string = not from_tty
+      gdb.execute(cmd, from_tty=confirm, to_string=to_string)
+
+    def execute(cmd):
+      return subprocess.check_output(cmd, stderr=subprocess.STDOUT) \
+                       .decode('utf-8')
+
+    # load mappings addresses
+    libs = {}
+    with open('/proc/{}/maps'.format(pid), 'r') as maps:
+      for line in maps:
+        addr, _, _, _, node, path = re.split(r'\s+', line, 5)
+        path = path.strip()
+        if node == '0': continue
+        if path in libs: continue
+        libs[path] = int(addr.split('-')[0], 16)
+
+    # unload symbol file if address changed
+    for k in set(libs) & set(self.libs[pid]):
+      if libs[k] != self.libs[pid][k]:
+        command('remove-symbol-file "{}"'.format(k), confirm=False)
+        del self.libs[k]
+
+    # load symbol file for new mappings
+    for k in set(libs) - set(self.libs[pid]):
+        if arg is not None and re.search(arg, k) is None: continue
+        addr = self.libs[pid][k] = libs[k]
+        offs = None
+
+        try:
+          out = execute(['file', k])
+        except:
+          continue
+
+        # try loading mapping as ELF
+        try:
+          out = execute(['readelf', '-l', k])
+          for line in out.split('\n'):
+            if not 'LOAD' in line: continue
+            base = int(line.split()[2], 16)
+            break
+
+          out = execute(['objdump', '-h', k])
+          for line in out.split('\n'):
+            if not '.text' in line: continue
+            offs = int(line.split()[3], 16) - base
+            break
+          if offs is None: continue
+
+        # try again, assuming mapping is PE
+        except:
+          try:
+            out = execute(['objdump', '-h', k])
+            for line in out.split('\n'):
+              if not '.text' in line: continue
+              offs = int(line.split()[5], 16)
+              break
+            if offs is None: continue
+
+          except:
+            continue
+
+        command('add-symbol-file "{}" 0x{:x}'.format(k, addr + offs),
+                confirm=False)
+
+
+LoadSymbolFiles()
-- 
2.31.0




More information about the wine-devel mailing list