xref: /linux/scripts/gdb/linux/symbols.py (revision 52338415)
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
17
18from linux import modules, utils
19
20
21if 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
56class LxSymbols(gdb.Command):
57    """(Re-)load symbols of Linux kernel and currently loaded modules.
58
59The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
60are scanned recursively, starting in the same directory. Optionally, the module
61search path can be extended by a space separated list of paths passed to the
62lx-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
186LxSymbols()
187