2332bd8eddf1118daa8b849bcf7ce1029eb1cd17
[linux-2.6-block.git] / scripts / gdb / linux / symbols.py
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
14 import gdb
15 import os
16 import re
17 import struct
18
19 from itertools import count
20 from linux import modules, utils, constants
21
22
23 if 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
52 def 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
76 def 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
87 class LxSymbols(gdb.Command):
88     """(Re-)load symbols of Linux kernel and currently loaded modules.
89
90 The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
91 are scanned recursively, starting in the same directory. Optionally, the module
92 search path can be extended by a space separated list of paths passed to the
93 lx-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
231 LxSymbols()