###
### Load debuging information about GNU GRUB 2 modules into GDB
-### automatically. Needs readelf, Perl and gmodule.pl script
+### automatically. Needs readelf, Python and gdb_helper.py script
###
### Has to be launched from the writable and trusted
### directory containing *.image and *.module
### Lubomir Kundrak <lkudrak@skosi.org>
###
-# Add section numbers and addresses to .segments.tmp
-define dump_module_sections_helper
- set $mod = $arg0
- printf "%s", $mod->name
- set $segment = $mod->segment
- while ($segment)
- printf " %i 0x%lx", $segment->section, $segment->addr
- set $segment = $segment->next
- end
- printf "\n"
-end
+source gdb_helper.py
-define dump_module_sections
- # Set unlimited width so that lines don't get wrapped writing
- # to .segments.tmp
- with width 0 -- \
- with trace-commands off -- \
- pipe dump_module_sections_helper $arg0 | sh -c 'cat >>.segments.tmp'
-end
-document dump_module_sections
- Gather information about module whose mod structure was
- given for use with match_and_load_symbols
-end
-
-# Generate and execute GDB commands and delete temporary files
-# afterwards
-define match_and_load_symbols
- shell perl gmodule.pl <.segments.tmp >.loadsym.gdb
- source .loadsym.gdb
- shell rm -f .segments.tmp .loadsym.gdb
-end
-document match_and_load_symbols
- Launch script, that matches section names with information
- generated by dump_module_sections and load debugging info
- apropriately
-end
-
-###
-
-define load_module
- dump_module_sections $arg0
- match_and_load_symbols
-end
-document load_module
- Load debugging information for module given as argument.
-end
define load_all_modules
- shell rm -f .segments.tmp
set $this = grub_dl_head
while ($this != 0)
- dump_module_sections $this
+ load_module $this
set $this = $this->next
end
- if (grub_dl_head != 0)
- match_and_load_symbols
- end
end
document load_all_modules
Load debugging information for all loaded modules.
--- /dev/null
+import os
+import re
+import subprocess
+
+##### Convenience functions #####
+
+class IsUserCommand (gdb.Function):
+ """Set the second argument to true value if first argument is the name
+of a user-defined command.
+"""
+
+ def __init__ (self):
+ super (IsUserCommand, self).__init__ ("is_user_command")
+
+ def invoke (self, fmt, *args):
+ name = fmt.string () % tuple(a.string () for a in args)
+ for line in gdb.execute ("help user-defined", to_string=True).splitlines ():
+ line_parts = line.split(' -- ', 1)
+ if len (line_parts) > 1 and line_parts[0] == name:
+ return True
+ return False
+
+is_user_command = IsUserCommand ()
+
+##### Commands #####
+
+class GrubLoadModuleSymbols (gdb.Command):
+ """Load module symbols at correct locations.
+Takes one argument which is a pointer to a grub_dl_t struct."""
+
+ def __init__ (self):
+ super (GrubLoadModuleSymbols, self).__init__ ("load_module",
+ gdb.COMMAND_USER,
+ gdb.COMPLETE_EXPRESSION)
+
+ def invoke (self, arg, from_tty):
+ self.dont_repeat ()
+ args = gdb.string_to_argv (arg)
+ self.mod = gdb.parse_and_eval (args[0])
+ sections = self.get_section_offsets ()
+ section_names = self.get_section_names ()
+
+ sym_load_cmd_parts = ["add-symbol-file",
+ "%s.module" % (self.mod['name'].string (),)]
+ for idx, addr in sections:
+ section_name = section_names[idx]
+ if section_name == ".text":
+ sym_load_cmd_parts.append (addr)
+ else:
+ sym_load_cmd_parts.extend (["-s", section_name, addr])
+ gdb.execute (' '.join (sym_load_cmd_parts))
+
+ if is_user_command.invoke (gdb.Value ("onload_%s"), self.mod['name']):
+ gdb.execute ("onload_%s (grub_dl_t)%s" % (self.mod['name'].string (),
+ self.mod.format_string (format='x')))
+
+ def get_section_offsets (self):
+ sections = []
+ segment = self.mod['segment']
+ while segment:
+ sections.append ((int (segment['section']), segment['addr'].format_string (format='x')))
+ segment = segment['next']
+ return sections
+
+ def get_section_names (self):
+ re_index = re.compile ("^\s+\[\s*(\d+)\] (\S*)")
+ names = {}
+ modfilename = "%s.mod" % (self.mod['name'].string (),)
+
+ if not os.path.exists (modfilename):
+ raise RuntimeError ("%s not found in current directory" % (modfilename,))
+
+ c = subprocess.run (["readelf", "-SW", modfilename], text=True, capture_output=True)
+ for line in c.stdout.splitlines ()[4:]:
+ m = re_index.match (line)
+ if not m:
+ continue
+ idx, name = m.groups ()
+ names[int (idx)] = name
+ return names
+
+grub_load_module = GrubLoadModuleSymbols ()
+++ /dev/null
-###
-### Generate GDB commands, that load symbols for specified module,
-### with proper section relocations. See .gdbinit
-###
-### $Id: gmodule.pl,v 1.2 2006/05/14 11:38:42 lkundrak Exp lkundrak $
-### Lubomir Kundrak <lkudrak@skosi.org>
-###
-
-use strict;
-
-while (<>) {
- my ($name, %sections) = split;
-
- print "add-symbol-file $name.module";
-
- open (READELF, "readelf -S $name.mod |") or die;
- while (<READELF>) {
- /\[\s*(\d+)\]\s+(\.\S+)/ or next;
-
- if ($2 eq '.text') {
- print " $sections{$1}";
- next;
- }
-
- print " -s $2 $sections{$1}"
- if ($sections{$1} ne '0x0' and $sections{$1} ne '');
- };
- close (READELF);
- print "\n";
-}