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