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 <[email protected]> 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, constants 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, module_addr): 93 try: 94 sect_attrs = module['sect_attrs'].dereference() 95 except gdb.error: 96 return str(module_addr) 97 98 attrs = sect_attrs['attrs'] 99 section_name_to_address = { 100 attrs[n]['battr']['attr']['name'].string(): attrs[n]['address'] 101 for n in range(int(sect_attrs['nsections']))} 102 103 textaddr = section_name_to_address.get(".text", module_addr) 104 args = [] 105 for section_name in [".data", ".data..read_mostly", ".rodata", ".bss", 106 ".text.hot", ".text.unlikely"]: 107 address = section_name_to_address.get(section_name) 108 if address: 109 args.append(" -s {name} {addr}".format( 110 name=section_name, addr=str(address))) 111 return "{textaddr} {sections}".format( 112 textaddr=textaddr, sections="".join(args)) 113 114 def load_module_symbols(self, module, module_file=None): 115 module_name = module['name'].string() 116 module_addr = str(module['mem'][constants.LX_MOD_TEXT]['base']).split()[0] 117 118 if not module_file: 119 module_file = self._get_module_file(module_name) 120 if not module_file and not self.module_files_updated: 121 self._update_module_files() 122 module_file = self._get_module_file(module_name) 123 124 if module_file: 125 if utils.is_target_arch('s390'): 126 # Module text is preceded by PLT stubs on s390. 127 module_arch = module['arch'] 128 plt_offset = int(module_arch['plt_offset']) 129 plt_size = int(module_arch['plt_size']) 130 module_addr = hex(int(module_addr, 0) + plt_offset + plt_size) 131 gdb.write("loading @{addr}: {filename}\n".format( 132 addr=module_addr, filename=module_file)) 133 cmdline = "add-symbol-file {filename} {sections}".format( 134 filename=module_file, 135 sections=self._section_arguments(module, module_addr)) 136 gdb.execute(cmdline, to_string=True) 137 if module_name not in self.loaded_modules: 138 self.loaded_modules.append(module_name) 139 else: 140 gdb.write("no module object found for '{0}'\n".format(module_name)) 141 142 def load_ko_symbols(self, mod_path): 143 self.loaded_modules = [] 144 module_list = modules.module_list() 145 146 for module in module_list: 147 module_name = module['name'].string() 148 module_pattern = ".*/{0}\.ko(?:.debug)?$".format( 149 module_name.replace("_", r"[_\-]")) 150 if re.match(module_pattern, mod_path) and os.path.exists(mod_path): 151 self.load_module_symbols(module, mod_path) 152 return 153 raise gdb.GdbError("%s is not a valid .ko\n" % mod_path) 154 155 def load_all_symbols(self): 156 gdb.write("loading vmlinux\n") 157 158 # Dropping symbols will disable all breakpoints. So save their states 159 # and restore them afterward. 160 saved_states = [] 161 if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: 162 for bp in gdb.breakpoints(): 163 saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) 164 165 # drop all current symbols and reload vmlinux 166 orig_vmlinux = 'vmlinux' 167 for obj in gdb.objfiles(): 168 if (obj.filename.endswith('vmlinux') or 169 obj.filename.endswith('vmlinux.debug')): 170 orig_vmlinux = obj.filename 171 gdb.execute("symbol-file", to_string=True) 172 gdb.execute("symbol-file {0}".format(orig_vmlinux)) 173 174 self.loaded_modules = [] 175 module_list = modules.module_list() 176 if not module_list: 177 gdb.write("no modules found\n") 178 else: 179 [self.load_module_symbols(module) for module in module_list] 180 181 for saved_state in saved_states: 182 saved_state['breakpoint'].enabled = saved_state['enabled'] 183 184 def invoke(self, arg, from_tty): 185 self.module_paths = [os.path.abspath(os.path.expanduser(p)) 186 for p in arg.split()] 187 self.module_paths.append(os.getcwd()) 188 189 # enforce update 190 self.module_files = [] 191 self.module_files_updated = False 192 193 argv = gdb.string_to_argv(arg) 194 if len(argv) == 1: 195 self.load_ko_symbols(argv[0]) 196 return 197 198 self.load_all_symbols() 199 200 if hasattr(gdb, 'Breakpoint'): 201 if self.breakpoint is not None: 202 self.breakpoint.delete() 203 self.breakpoint = None 204 self.breakpoint = LoadModuleBreakpoint( 205 "kernel/module/main.c:do_init_module", self) 206 else: 207 gdb.write("Note: symbol update on module loading not supported " 208 "with this gdb version\n") 209 210 211LxSymbols() 212