scripts/gdb: fix debugging modules compiled with hot/cold partitioning
[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                              ".text", ".text.hot", ".text.unlikely"]:
104             address = section_name_to_address.get(section_name)
105             if address:
106                 args.append(" -s {name} {addr}".format(
107                     name=section_name, addr=str(address)))
108         return "".join(args)
109
110     def load_module_symbols(self, module):
111         module_name = module['name'].string()
112         module_addr = str(module['core_layout']['base']).split()[0]
113
114         module_file = self._get_module_file(module_name)
115         if not module_file and not self.module_files_updated:
116             self._update_module_files()
117             module_file = self._get_module_file(module_name)
118
119         if module_file:
120             if utils.is_target_arch('s390'):
121                 # Module text is preceded by PLT stubs on s390.
122                 module_arch = module['arch']
123                 plt_offset = int(module_arch['plt_offset'])
124                 plt_size = int(module_arch['plt_size'])
125                 module_addr = hex(int(module_addr, 0) + plt_offset + plt_size)
126             gdb.write("loading @{addr}: {filename}\n".format(
127                 addr=module_addr, filename=module_file))
128             cmdline = "add-symbol-file {filename} {addr}{sections}".format(
129                 filename=module_file,
130                 addr=module_addr,
131                 sections=self._section_arguments(module))
132             gdb.execute(cmdline, to_string=True)
133             if module_name not in self.loaded_modules:
134                 self.loaded_modules.append(module_name)
135         else:
136             gdb.write("no module object found for '{0}'\n".format(module_name))
137
138     def load_all_symbols(self):
139         gdb.write("loading vmlinux\n")
140
141         # Dropping symbols will disable all breakpoints. So save their states
142         # and restore them afterward.
143         saved_states = []
144         if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
145             for bp in gdb.breakpoints():
146                 saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
147
148         # drop all current symbols and reload vmlinux
149         orig_vmlinux = 'vmlinux'
150         for obj in gdb.objfiles():
151             if obj.filename.endswith('vmlinux'):
152                 orig_vmlinux = obj.filename
153         gdb.execute("symbol-file", to_string=True)
154         gdb.execute("symbol-file {0}".format(orig_vmlinux))
155
156         self.loaded_modules = []
157         module_list = modules.module_list()
158         if not module_list:
159             gdb.write("no modules found\n")
160         else:
161             [self.load_module_symbols(module) for module in module_list]
162
163         for saved_state in saved_states:
164             saved_state['breakpoint'].enabled = saved_state['enabled']
165
166     def invoke(self, arg, from_tty):
167         self.module_paths = [os.path.expanduser(p) for p in arg.split()]
168         self.module_paths.append(os.getcwd())
169
170         # enforce update
171         self.module_files = []
172         self.module_files_updated = False
173
174         self.load_all_symbols()
175
176         if hasattr(gdb, 'Breakpoint'):
177             if self.breakpoint is not None:
178                 self.breakpoint.delete()
179                 self.breakpoint = None
180             self.breakpoint = LoadModuleBreakpoint(
181                 "kernel/module.c:do_init_module", self)
182         else:
183             gdb.write("Note: symbol update on module loading not supported "
184                       "with this gdb version\n")
185
186
187 LxSymbols()