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