1# 2# gdb helper commands and functions for Linux kernel debugging 3# 4# load kernel and module symbols 5# 6# Copyright (c) Siemens AG, 2011-2013 7# 8# Authors: 9# Jan Kiszka <jan.kiszka@siemens.com> 10# 11# This work is licensed under the terms of the GNU GPL version 2. 12# 13 14import gdb 15import os 16import re 17 18from linux import modules, utils 19 20 21if hasattr(gdb, 'Breakpoint'): 22 class LoadModuleBreakpoint(gdb.Breakpoint): 23 def __init__(self, spec, gdb_command): 24 super(LoadModuleBreakpoint, self).__init__(spec, internal=True) 25 self.silent = True 26 self.gdb_command = gdb_command 27 28 def stop(self): 29 module = gdb.parse_and_eval("mod") 30 module_name = module['name'].string() 31 cmd = self.gdb_command 32 33 # enforce update if object file is not found 34 cmd.module_files_updated = False 35 36 # Disable pagination while reporting symbol (re-)loading. 37 # The console input is blocked in this context so that we would 38 # get stuck waiting for the user to acknowledge paged output. 39 show_pagination = gdb.execute("show pagination", to_string=True) 40 pagination = show_pagination.endswith("on.\n") 41 gdb.execute("set pagination off") 42 43 if module_name in cmd.loaded_modules: 44 gdb.write("refreshing all symbols to reload module " 45 "'{0}'\n".format(module_name)) 46 cmd.load_all_symbols() 47 else: 48 cmd.load_module_symbols(module) 49 50 # restore pagination state 51 gdb.execute("set pagination %s" % ("on" if pagination else "off")) 52 53 return False 54 55 56class LxSymbols(gdb.Command): 57 """(Re-)load symbols of Linux kernel and currently loaded modules. 58 59The kernel (vmlinux) is taken from the current working directly. Modules (.ko) 60are scanned recursively, starting in the same directory. Optionally, the module 61search path can be extended by a space separated list of paths passed to the 62lx-symbols command.""" 63 64 module_paths = [] 65 module_files = [] 66 module_files_updated = False 67 loaded_modules = [] 68 breakpoint = None 69 70 def __init__(self): 71 super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES, 72 gdb.COMPLETE_FILENAME) 73 74 def _update_module_files(self): 75 self.module_files = [] 76 for path in self.module_paths: 77 gdb.write("scanning for modules in {0}\n".format(path)) 78 for root, dirs, files in os.walk(path): 79 for name in files: 80 if name.endswith(".ko") or name.endswith(".ko.debug"): 81 self.module_files.append(root + "/" + name) 82 self.module_files_updated = True 83 84 def _get_module_file(self, module_name): 85 module_pattern = ".*/{0}\.ko(?:.debug)?$".format( 86 module_name.replace("_", r"[_\-]")) 87 for name in self.module_files: 88 if re.match(module_pattern, name) and os.path.exists(name): 89 return name 90 return None 91 92 def _section_arguments(self, module): 93 try: 94 sect_attrs = module['sect_attrs'].dereference() 95 except gdb.error: 96 return "" 97 attrs = sect_attrs['attrs'] 98 section_name_to_address = { 99 attrs[n]['name'].string(): attrs[n]['address'] 100 for n in range(int(sect_attrs['nsections']))} 101 args = [] 102 for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]: 103 address = section_name_to_address.get(section_name) 104 if address: 105 args.append(" -s {name} {addr}".format( 106 name=section_name, addr=str(address))) 107 return "".join(args) 108 109 def load_module_symbols(self, module): 110 module_name = module['name'].string() 111 module_addr = str(module['core_layout']['base']).split()[0] 112 113 module_file = self._get_module_file(module_name) 114 if not module_file and not self.module_files_updated: 115 self._update_module_files() 116 module_file = self._get_module_file(module_name) 117 118 if module_file: 119 if utils.is_target_arch('s390'): 120 # Module text is preceded by PLT stubs on s390. 121 module_arch = module['arch'] 122 plt_offset = int(module_arch['plt_offset']) 123 plt_size = int(module_arch['plt_size']) 124 module_addr = hex(int(module_addr, 0) + plt_offset + plt_size) 125 gdb.write("loading @{addr}: {filename}\n".format( 126 addr=module_addr, filename=module_file)) 127 cmdline = "add-symbol-file {filename} {addr}{sections}".format( 128 filename=module_file, 129 addr=module_addr, 130 sections=self._section_arguments(module)) 131 gdb.execute(cmdline, to_string=True) 132 if module_name not in self.loaded_modules: 133 self.loaded_modules.append(module_name) 134 else: 135 gdb.write("no module object found for '{0}'\n".format(module_name)) 136 137 def load_all_symbols(self): 138 gdb.write("loading vmlinux\n") 139 140 # Dropping symbols will disable all breakpoints. So save their states 141 # and restore them afterward. 142 saved_states = [] 143 if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: 144 for bp in gdb.breakpoints(): 145 saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) 146 147 # drop all current symbols and reload vmlinux 148 orig_vmlinux = 'vmlinux' 149 for obj in gdb.objfiles(): 150 if obj.filename.endswith('vmlinux'): 151 orig_vmlinux = obj.filename 152 gdb.execute("symbol-file", to_string=True) 153 gdb.execute("symbol-file {0}".format(orig_vmlinux)) 154 155 self.loaded_modules = [] 156 module_list = modules.module_list() 157 if not module_list: 158 gdb.write("no modules found\n") 159 else: 160 [self.load_module_symbols(module) for module in module_list] 161 162 for saved_state in saved_states: 163 saved_state['breakpoint'].enabled = saved_state['enabled'] 164 165 def invoke(self, arg, from_tty): 166 self.module_paths = [os.path.expanduser(p) for p in arg.split()] 167 self.module_paths.append(os.getcwd()) 168 169 # enforce update 170 self.module_files = [] 171 self.module_files_updated = False 172 173 self.load_all_symbols() 174 175 if hasattr(gdb, 'Breakpoint'): 176 if self.breakpoint is not None: 177 self.breakpoint.delete() 178 self.breakpoint = None 179 self.breakpoint = LoadModuleBreakpoint( 180 "kernel/module.c:do_init_module", self) 181 else: 182 gdb.write("Note: symbol update on module loading not supported " 183 "with this gdb version\n") 184 185 186LxSymbols() 187