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