123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- #
- # gdb helper commands and functions for Linux kernel debugging
- #
- # load kernel and module symbols
- #
- # Copyright (c) Siemens AG, 2011-2013
- #
- # Authors:
- # Jan Kiszka <jan.kiszka@siemens.com>
- #
- # This work is licensed under the terms of the GNU GPL version 2.
- #
- import gdb
- import os
- import re
- from linux import modules
- if hasattr(gdb, 'Breakpoint'):
- class LoadModuleBreakpoint(gdb.Breakpoint):
- def __init__(self, spec, gdb_command):
- super(LoadModuleBreakpoint, self).__init__(spec, internal=True)
- self.silent = True
- self.gdb_command = gdb_command
- def stop(self):
- module = gdb.parse_and_eval("mod")
- module_name = module['name'].string()
- cmd = self.gdb_command
- # enforce update if object file is not found
- cmd.module_files_updated = False
- # Disable pagination while reporting symbol (re-)loading.
- # The console input is blocked in this context so that we would
- # get stuck waiting for the user to acknowledge paged output.
- show_pagination = gdb.execute("show pagination", to_string=True)
- pagination = show_pagination.endswith("on.\n")
- gdb.execute("set pagination off")
- if module_name in cmd.loaded_modules:
- gdb.write("refreshing all symbols to reload module "
- "'{0}'\n".format(module_name))
- cmd.load_all_symbols()
- else:
- cmd.load_module_symbols(module)
- # restore pagination state
- gdb.execute("set pagination %s" % ("on" if pagination else "off"))
- return False
- class LxSymbols(gdb.Command):
- """(Re-)load symbols of Linux kernel and currently loaded modules.
- The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
- are scanned recursively, starting in the same directory. Optionally, the module
- search path can be extended by a space separated list of paths passed to the
- lx-symbols command."""
- module_paths = []
- module_files = []
- module_files_updated = False
- loaded_modules = []
- breakpoint = None
- def __init__(self):
- super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES,
- gdb.COMPLETE_FILENAME)
- def _update_module_files(self):
- self.module_files = []
- for path in self.module_paths:
- gdb.write("scanning for modules in {0}\n".format(path))
- for root, dirs, files in os.walk(path):
- for name in files:
- if name.endswith(".ko"):
- self.module_files.append(root + "/" + name)
- self.module_files_updated = True
- def _get_module_file(self, module_name):
- module_pattern = ".*/{0}\.ko$".format(
- module_name.replace("_", r"[_\-]"))
- for name in self.module_files:
- if re.match(module_pattern, name) and os.path.exists(name):
- return name
- return None
- def _section_arguments(self, module):
- try:
- sect_attrs = module['sect_attrs'].dereference()
- except gdb.error:
- return ""
- attrs = sect_attrs['attrs']
- section_name_to_address = {
- attrs[n]['name'].string(): attrs[n]['address']
- for n in range(int(sect_attrs['nsections']))}
- args = []
- for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]:
- address = section_name_to_address.get(section_name)
- if address:
- args.append(" -s {name} {addr}".format(
- name=section_name, addr=str(address)))
- return "".join(args)
- def load_module_symbols(self, module):
- module_name = module['name'].string()
- module_addr = str(module['module_core']).split()[0]
- module_file = self._get_module_file(module_name)
- if not module_file and not self.module_files_updated:
- self._update_module_files()
- module_file = self._get_module_file(module_name)
- if module_file:
- gdb.write("loading @{addr}: {filename}\n".format(
- addr=module_addr, filename=module_file))
- cmdline = "add-symbol-file {filename} {addr}{sections}".format(
- filename=module_file,
- addr=module_addr,
- sections=self._section_arguments(module))
- gdb.execute(cmdline, to_string=True)
- if module_name not in self.loaded_modules:
- self.loaded_modules.append(module_name)
- else:
- gdb.write("no module object found for '{0}'\n".format(module_name))
- def load_all_symbols(self):
- gdb.write("loading vmlinux\n")
- # Dropping symbols will disable all breakpoints. So save their states
- # and restore them afterward.
- saved_states = []
- if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
- for bp in gdb.breakpoints():
- saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
- # drop all current symbols and reload vmlinux
- gdb.execute("symbol-file", to_string=True)
- gdb.execute("symbol-file vmlinux")
- self.loaded_modules = []
- module_list = modules.module_list()
- if not module_list:
- gdb.write("no modules found\n")
- else:
- [self.load_module_symbols(module) for module in module_list]
- for saved_state in saved_states:
- saved_state['breakpoint'].enabled = saved_state['enabled']
- def invoke(self, arg, from_tty):
- self.module_paths = arg.split()
- self.module_paths.append(os.getcwd())
- # enforce update
- self.module_files = []
- self.module_files_updated = False
- self.load_all_symbols()
- if hasattr(gdb, 'Breakpoint'):
- if self.breakpoint is not None:
- self.breakpoint.delete()
- self.breakpoint = None
- self.breakpoint = LoadModuleBreakpoint(
- "kernel/module.c:do_init_module", self)
- else:
- gdb.write("Note: symbol update on module loading not supported "
- "with this gdb version\n")
- LxSymbols()
|