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