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 with utils.pagination_off(): 42 if module_name in cmd.loaded_modules: 43 gdb.write("refreshing all symbols to reload module " 44 "'{0}'\n".format(module_name)) 45 cmd.load_all_symbols() 46 else: 47 cmd.load_module_symbols(module) 48 49 return False 50 51 52def get_vmcore_s390(): 53 with utils.qemu_phy_mem_mode(): 54 vmcore_info = 0x0e0c 55 paddr_vmcoreinfo_note = gdb.parse_and_eval("*(unsigned long long *)" + 56 hex(vmcore_info)) 57 if paddr_vmcoreinfo_note == 0 or paddr_vmcoreinfo_note & 1: 58 # In the early boot case, extract vm_layout.kaslr_offset from the 59 # vmlinux image in physical memory. 60 if paddr_vmcoreinfo_note == 0: 61 kaslr_offset_phys = 0 62 else: 63 kaslr_offset_phys = paddr_vmcoreinfo_note - 1 64 with utils.pagination_off(): 65 gdb.execute("symbol-file {0} -o {1}".format( 66 utils.get_vmlinux(), hex(kaslr_offset_phys))) 67 kaslr_offset = gdb.parse_and_eval("vm_layout.kaslr_offset") 68 return "KERNELOFFSET=" + hex(kaslr_offset)[2:] 69 inferior = gdb.selected_inferior() 70 elf_note = inferior.read_memory(paddr_vmcoreinfo_note, 12) 71 n_namesz, n_descsz, n_type = struct.unpack(">III", elf_note) 72 desc_paddr = paddr_vmcoreinfo_note + len(elf_note) + n_namesz + 1 73 return gdb.parse_and_eval("(char *)" + hex(desc_paddr)).string() 74 75 76def get_kerneloffset(): 77 if utils.is_target_arch('s390'): 78 try: 79 vmcore_str = get_vmcore_s390() 80 except gdb.error as e: 81 gdb.write("{}\n".format(e)) 82 return None 83 return utils.parse_vmcore(vmcore_str).kerneloffset 84 return None 85 86 87def is_in_s390_decompressor(): 88 # DAT is always off in decompressor. Use this as an indicator. 89 # Note that in the kernel, DAT can be off during kexec() or restart. 90 # Accept this imprecision in order to avoid complicating things. 91 # It is unlikely that someone will run lx-symbols at these points. 92 pswm = int(gdb.parse_and_eval("$pswm")) 93 return (pswm & 0x0400000000000000) == 0 94 95 96def skip_decompressor(): 97 if utils.is_target_arch("s390"): 98 if is_in_s390_decompressor(): 99 # The address of the jump_to_kernel function is statically placed 100 # into svc_old_psw.addr (see ipl_data.c); read it from there. DAT 101 # is off, so we do not need to care about lowcore relocation. 102 svc_old_pswa = 0x148 103 jump_to_kernel = int(gdb.parse_and_eval("*(unsigned long long *)" + 104 hex(svc_old_pswa))) 105 gdb.execute("tbreak *" + hex(jump_to_kernel)) 106 gdb.execute("continue") 107 while is_in_s390_decompressor(): 108 gdb.execute("stepi") 109 110 111class LxSymbols(gdb.Command): 112 """(Re-)load symbols of Linux kernel and currently loaded modules. 113 114The kernel (vmlinux) is taken from the current working directly. Modules (.ko) 115are scanned recursively, starting in the same directory. Optionally, the module 116search path can be extended by a space separated list of paths passed to the 117lx-symbols command.""" 118 119 module_paths = [] 120 module_files = [] 121 module_files_updated = False 122 loaded_modules = [] 123 breakpoint = None 124 125 def __init__(self): 126 super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES, 127 gdb.COMPLETE_FILENAME) 128 129 def _update_module_files(self): 130 self.module_files = [] 131 for path in self.module_paths: 132 gdb.write("scanning for modules in {0}\n".format(path)) 133 for root, dirs, files in os.walk(path): 134 for name in files: 135 if name.endswith(".ko") or name.endswith(".ko.debug"): 136 self.module_files.append(root + "/" + name) 137 self.module_files_updated = True 138 139 def _get_module_file(self, module_name): 140 module_pattern = r".*/{0}\.ko(?:.debug)?$".format( 141 module_name.replace("_", r"[_\-]")) 142 for name in self.module_files: 143 if re.match(module_pattern, name) and os.path.exists(name): 144 return name 145 return None 146 147 def _section_arguments(self, module, module_addr): 148 try: 149 sect_attrs = module['sect_attrs'].dereference() 150 except gdb.error: 151 return str(module_addr) 152 153 section_name_to_address = {} 154 for i in count(): 155 # this is a NULL terminated array 156 if sect_attrs['grp']['bin_attrs'][i] == 0x0: 157 break 158 159 attr = sect_attrs['grp']['bin_attrs'][i].dereference() 160 section_name_to_address[attr['attr']['name'].string()] = attr['private'] 161 162 textaddr = section_name_to_address.get(".text", module_addr) 163 args = [] 164 for section_name in [".data", ".data..read_mostly", ".rodata", ".bss", 165 ".text.hot", ".text.unlikely"]: 166 address = section_name_to_address.get(section_name) 167 if address: 168 args.append(" -s {name} {addr}".format( 169 name=section_name, addr=str(address))) 170 return "{textaddr} {sections}".format( 171 textaddr=textaddr, sections="".join(args)) 172 173 def load_module_symbols(self, module): 174 module_name = module['name'].string() 175 module_addr = str(module['mem'][constants.LX_MOD_TEXT]['base']).split()[0] 176 177 module_file = self._get_module_file(module_name) 178 if not module_file and not self.module_files_updated: 179 self._update_module_files() 180 module_file = self._get_module_file(module_name) 181 182 if module_file: 183 if utils.is_target_arch('s390'): 184 # Module text is preceded by PLT stubs on s390. 185 module_arch = module['arch'] 186 plt_offset = int(module_arch['plt_offset']) 187 plt_size = int(module_arch['plt_size']) 188 module_addr = hex(int(module_addr, 0) + plt_offset + plt_size) 189 gdb.write("loading @{addr}: {filename}\n".format( 190 addr=module_addr, filename=module_file)) 191 cmdline = "add-symbol-file {filename} {sections}".format( 192 filename=module_file, 193 sections=self._section_arguments(module, module_addr)) 194 gdb.execute(cmdline, to_string=True) 195 if module_name not in self.loaded_modules: 196 self.loaded_modules.append(module_name) 197 else: 198 gdb.write("no module object found for '{0}'\n".format(module_name)) 199 200 def load_all_symbols(self): 201 gdb.write("loading vmlinux\n") 202 203 # Dropping symbols will disable all breakpoints. So save their states 204 # and restore them afterward. 205 saved_states = [] 206 if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: 207 for bp in gdb.breakpoints(): 208 saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) 209 210 # drop all current symbols and reload vmlinux 211 orig_vmlinux = utils.get_vmlinux() 212 gdb.execute("symbol-file", to_string=True) 213 kerneloffset = get_kerneloffset() 214 if kerneloffset is None: 215 offset_arg = "" 216 else: 217 offset_arg = " -o " + hex(kerneloffset) 218 gdb.execute("symbol-file {0}{1}".format(orig_vmlinux, offset_arg)) 219 220 self.loaded_modules = [] 221 module_list = modules.module_list() 222 if not module_list: 223 gdb.write("no modules found\n") 224 else: 225 [self.load_module_symbols(module) for module in module_list] 226 227 for saved_state in saved_states: 228 saved_state['breakpoint'].enabled = saved_state['enabled'] 229 230 def invoke(self, arg, from_tty): 231 skip_decompressor() 232 233 self.module_paths = [os.path.abspath(os.path.expanduser(p)) 234 for p in arg.split()] 235 self.module_paths.append(os.getcwd()) 236 237 # enforce update 238 self.module_files = [] 239 self.module_files_updated = False 240 241 self.load_all_symbols() 242 243 if not modules.has_modules(): 244 return 245 246 if hasattr(gdb, 'Breakpoint'): 247 if self.breakpoint is not None: 248 self.breakpoint.delete() 249 self.breakpoint = None 250 self.breakpoint = LoadModuleBreakpoint( 251 "kernel/module/main.c:do_init_module", self) 252 else: 253 gdb.write("Note: symbol update on module loading not supported " 254 "with this gdb version\n") 255 256 257LxSymbols() 258