symbols.py 13 KB

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