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