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 87class LxSymbols(gdb.Command): 88 """(Re-)load symbols of Linux kernel and currently loaded modules. 89 90The kernel (vmlinux) is taken from the current working directly. Modules (.ko) 91are scanned recursively, starting in the same directory. Optionally, the module 92search path can be extended by a space separated list of paths passed to the 93lx-symbols command.""" 94 95 module_paths = [] 96 module_files = [] 97 module_files_updated = False 98 loaded_modules = [] 99 breakpoint = None 100 101 def __init__(self): 102 super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES, 103 gdb.COMPLETE_FILENAME) 104 105 def _update_module_files(self): 106 self.module_files = [] 107 for path in self.module_paths: 108 gdb.write("scanning for modules in {0}\n".format(path)) 109 for root, dirs, files in os.walk(path): 110 for name in files: 111 if name.endswith(".ko") or name.endswith(".ko.debug"): 112 self.module_files.append(root + "/" + name) 113 self.module_files_updated = True 114 115 def _get_module_file(self, module_name): 116 module_pattern = r".*/{0}\.ko(?:.debug)?$".format( 117 module_name.replace("_", r"[_\-]")) 118 for name in self.module_files: 119 if re.match(module_pattern, name) and os.path.exists(name): 120 return name 121 return None 122 123 def _section_arguments(self, module, module_addr): 124 try: 125 sect_attrs = module['sect_attrs'].dereference() 126 except gdb.error: 127 return str(module_addr) 128 129 section_name_to_address = {} 130 for i in count(): 131 # this is a NULL terminated array 132 if sect_attrs['grp']['bin_attrs'][i] == 0x0: 133 break 134 135 attr = sect_attrs['grp']['bin_attrs'][i].dereference() 136 section_name_to_address[attr['attr']['name'].string()] = attr['private'] 137 138 textaddr = section_name_to_address.get(".text", module_addr) 139 args = [] 140 for section_name in [".data", ".data..read_mostly", ".rodata", ".bss", 141 ".text.hot", ".text.unlikely"]: 142 address = section_name_to_address.get(section_name) 143 if address: 144 args.append(" -s {name} {addr}".format( 145 name=section_name, addr=str(address))) 146 return "{textaddr} {sections}".format( 147 textaddr=textaddr, sections="".join(args)) 148 149 def load_module_symbols(self, module): 150 module_name = module['name'].string() 151 module_addr = str(module['mem'][constants.LX_MOD_TEXT]['base']).split()[0] 152 153 module_file = self._get_module_file(module_name) 154 if not module_file and not self.module_files_updated: 155 self._update_module_files() 156 module_file = self._get_module_file(module_name) 157 158 if module_file: 159 if utils.is_target_arch('s390'): 160 # Module text is preceded by PLT stubs on s390. 161 module_arch = module['arch'] 162 plt_offset = int(module_arch['plt_offset']) 163 plt_size = int(module_arch['plt_size']) 164 module_addr = hex(int(module_addr, 0) + plt_offset + plt_size) 165 gdb.write("loading @{addr}: {filename}\n".format( 166 addr=module_addr, filename=module_file)) 167 cmdline = "add-symbol-file {filename} {sections}".format( 168 filename=module_file, 169 sections=self._section_arguments(module, module_addr)) 170 gdb.execute(cmdline, to_string=True) 171 if module_name not in self.loaded_modules: 172 self.loaded_modules.append(module_name) 173 else: 174 gdb.write("no module object found for '{0}'\n".format(module_name)) 175 176 def load_all_symbols(self): 177 gdb.write("loading vmlinux\n") 178 179 # Dropping symbols will disable all breakpoints. So save their states 180 # and restore them afterward. 181 saved_states = [] 182 if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: 183 for bp in gdb.breakpoints(): 184 saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) 185 186 # drop all current symbols and reload vmlinux 187 orig_vmlinux = utils.get_vmlinux() 188 gdb.execute("symbol-file", to_string=True) 189 kerneloffset = get_kerneloffset() 190 if kerneloffset is None: 191 offset_arg = "" 192 else: 193 offset_arg = " -o " + hex(kerneloffset) 194 gdb.execute("symbol-file {0}{1}".format(orig_vmlinux, offset_arg)) 195 196 self.loaded_modules = [] 197 module_list = modules.module_list() 198 if not module_list: 199 gdb.write("no modules found\n") 200 else: 201 [self.load_module_symbols(module) for module in module_list] 202 203 for saved_state in saved_states: 204 saved_state['breakpoint'].enabled = saved_state['enabled'] 205 206 def invoke(self, arg, from_tty): 207 self.module_paths = [os.path.abspath(os.path.expanduser(p)) 208 for p in arg.split()] 209 self.module_paths.append(os.getcwd()) 210 211 # enforce update 212 self.module_files = [] 213 self.module_files_updated = False 214 215 self.load_all_symbols() 216 217 if not modules.has_modules(): 218 return 219 220 if hasattr(gdb, 'Breakpoint'): 221 if self.breakpoint is not None: 222 self.breakpoint.delete() 223 self.breakpoint = None 224 self.breakpoint = LoadModuleBreakpoint( 225 "kernel/module/main.c:do_init_module", self) 226 else: 227 gdb.write("Note: symbol update on module loading not supported " 228 "with this gdb version\n") 229 230 231LxSymbols() 232