Back to home page

OSCL-LXR

 
 

    


0001 #
0002 # gdb helper commands and functions for Linux kernel debugging
0003 #
0004 #  load kernel and module symbols
0005 #
0006 # Copyright (c) Siemens AG, 2011-2013
0007 #
0008 # Authors:
0009 #  Jan Kiszka <jan.kiszka@siemens.com>
0010 #
0011 # This work is licensed under the terms of the GNU GPL version 2.
0012 #
0013 
0014 import gdb
0015 import os
0016 import re
0017 
0018 from linux import modules, utils
0019 
0020 
0021 if hasattr(gdb, 'Breakpoint'):
0022     class LoadModuleBreakpoint(gdb.Breakpoint):
0023         def __init__(self, spec, gdb_command):
0024             super(LoadModuleBreakpoint, self).__init__(spec, internal=True)
0025             self.silent = True
0026             self.gdb_command = gdb_command
0027 
0028         def stop(self):
0029             module = gdb.parse_and_eval("mod")
0030             module_name = module['name'].string()
0031             cmd = self.gdb_command
0032 
0033             # enforce update if object file is not found
0034             cmd.module_files_updated = False
0035 
0036             # Disable pagination while reporting symbol (re-)loading.
0037             # The console input is blocked in this context so that we would
0038             # get stuck waiting for the user to acknowledge paged output.
0039             show_pagination = gdb.execute("show pagination", to_string=True)
0040             pagination = show_pagination.endswith("on.\n")
0041             gdb.execute("set pagination off")
0042 
0043             if module_name in cmd.loaded_modules:
0044                 gdb.write("refreshing all symbols to reload module "
0045                           "'{0}'\n".format(module_name))
0046                 cmd.load_all_symbols()
0047             else:
0048                 cmd.load_module_symbols(module)
0049 
0050             # restore pagination state
0051             gdb.execute("set pagination %s" % ("on" if pagination else "off"))
0052 
0053             return False
0054 
0055 
0056 class LxSymbols(gdb.Command):
0057     """(Re-)load symbols of Linux kernel and currently loaded modules.
0058 
0059 The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
0060 are scanned recursively, starting in the same directory. Optionally, the module
0061 search path can be extended by a space separated list of paths passed to the
0062 lx-symbols command."""
0063 
0064     module_paths = []
0065     module_files = []
0066     module_files_updated = False
0067     loaded_modules = []
0068     breakpoint = None
0069 
0070     def __init__(self):
0071         super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES,
0072                                         gdb.COMPLETE_FILENAME)
0073 
0074     def _update_module_files(self):
0075         self.module_files = []
0076         for path in self.module_paths:
0077             gdb.write("scanning for modules in {0}\n".format(path))
0078             for root, dirs, files in os.walk(path):
0079                 for name in files:
0080                     if name.endswith(".ko") or name.endswith(".ko.debug"):
0081                         self.module_files.append(root + "/" + name)
0082         self.module_files_updated = True
0083 
0084     def _get_module_file(self, module_name):
0085         module_pattern = ".*/{0}\.ko(?:.debug)?$".format(
0086             module_name.replace("_", r"[_\-]"))
0087         for name in self.module_files:
0088             if re.match(module_pattern, name) and os.path.exists(name):
0089                 return name
0090         return None
0091 
0092     def _section_arguments(self, module):
0093         try:
0094             sect_attrs = module['sect_attrs'].dereference()
0095         except gdb.error:
0096             return ""
0097         attrs = sect_attrs['attrs']
0098         section_name_to_address = {
0099             attrs[n]['battr']['attr']['name'].string(): attrs[n]['address']
0100             for n in range(int(sect_attrs['nsections']))}
0101         args = []
0102         for section_name in [".data", ".data..read_mostly", ".rodata", ".bss",
0103                              ".text", ".text.hot", ".text.unlikely"]:
0104             address = section_name_to_address.get(section_name)
0105             if address:
0106                 args.append(" -s {name} {addr}".format(
0107                     name=section_name, addr=str(address)))
0108         return "".join(args)
0109 
0110     def load_module_symbols(self, module):
0111         module_name = module['name'].string()
0112         module_addr = str(module['core_layout']['base']).split()[0]
0113 
0114         module_file = self._get_module_file(module_name)
0115         if not module_file and not self.module_files_updated:
0116             self._update_module_files()
0117             module_file = self._get_module_file(module_name)
0118 
0119         if module_file:
0120             if utils.is_target_arch('s390'):
0121                 # Module text is preceded by PLT stubs on s390.
0122                 module_arch = module['arch']
0123                 plt_offset = int(module_arch['plt_offset'])
0124                 plt_size = int(module_arch['plt_size'])
0125                 module_addr = hex(int(module_addr, 0) + plt_offset + plt_size)
0126             gdb.write("loading @{addr}: {filename}\n".format(
0127                 addr=module_addr, filename=module_file))
0128             cmdline = "add-symbol-file {filename} {addr}{sections}".format(
0129                 filename=module_file,
0130                 addr=module_addr,
0131                 sections=self._section_arguments(module))
0132             gdb.execute(cmdline, to_string=True)
0133             if module_name not in self.loaded_modules:
0134                 self.loaded_modules.append(module_name)
0135         else:
0136             gdb.write("no module object found for '{0}'\n".format(module_name))
0137 
0138     def load_all_symbols(self):
0139         gdb.write("loading vmlinux\n")
0140 
0141         # Dropping symbols will disable all breakpoints. So save their states
0142         # and restore them afterward.
0143         saved_states = []
0144         if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
0145             for bp in gdb.breakpoints():
0146                 saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
0147 
0148         # drop all current symbols and reload vmlinux
0149         orig_vmlinux = 'vmlinux'
0150         for obj in gdb.objfiles():
0151             if (obj.filename.endswith('vmlinux') or
0152                 obj.filename.endswith('vmlinux.debug')):
0153                 orig_vmlinux = obj.filename
0154         gdb.execute("symbol-file", to_string=True)
0155         gdb.execute("symbol-file {0}".format(orig_vmlinux))
0156 
0157         self.loaded_modules = []
0158         module_list = modules.module_list()
0159         if not module_list:
0160             gdb.write("no modules found\n")
0161         else:
0162             [self.load_module_symbols(module) for module in module_list]
0163 
0164         for saved_state in saved_states:
0165             saved_state['breakpoint'].enabled = saved_state['enabled']
0166 
0167     def invoke(self, arg, from_tty):
0168         self.module_paths = [os.path.abspath(os.path.expanduser(p))
0169                              for p in arg.split()]
0170         self.module_paths.append(os.getcwd())
0171 
0172         # enforce update
0173         self.module_files = []
0174         self.module_files_updated = False
0175 
0176         self.load_all_symbols()
0177 
0178         if hasattr(gdb, 'Breakpoint'):
0179             if self.breakpoint is not None:
0180                 self.breakpoint.delete()
0181                 self.breakpoint = None
0182             self.breakpoint = LoadModuleBreakpoint(
0183                 "kernel/module/main.c:do_init_module", self)
0184         else:
0185             gdb.write("Note: symbol update on module loading not supported "
0186                       "with this gdb version\n")
0187 
0188 
0189 LxSymbols()