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