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 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()