• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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"):
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$".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['module_core']).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            gdb.write("loading @{addr}: {filename}\n".format(
121                addr=module_addr, filename=module_file))
122            cmdline = "add-symbol-file {filename} {addr}{sections}".format(
123                filename=module_file,
124                addr=module_addr,
125                sections=self._section_arguments(module))
126            gdb.execute(cmdline, to_string=True)
127            if module_name not in self.loaded_modules:
128                self.loaded_modules.append(module_name)
129        else:
130            gdb.write("no module object found for '{0}'\n".format(module_name))
131
132    def load_all_symbols(self):
133        gdb.write("loading vmlinux\n")
134
135        # Dropping symbols will disable all breakpoints. So save their states
136        # and restore them afterward.
137        saved_states = []
138        if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
139            for bp in gdb.breakpoints():
140                saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
141
142        # drop all current symbols and reload vmlinux
143        gdb.execute("symbol-file", to_string=True)
144        gdb.execute("symbol-file vmlinux")
145
146        self.loaded_modules = []
147        module_list = modules.module_list()
148        if not module_list:
149            gdb.write("no modules found\n")
150        else:
151            [self.load_module_symbols(module) for module in module_list]
152
153        for saved_state in saved_states:
154            saved_state['breakpoint'].enabled = saved_state['enabled']
155
156    def invoke(self, arg, from_tty):
157        self.module_paths = arg.split()
158        self.module_paths.append(os.getcwd())
159
160        # enforce update
161        self.module_files = []
162        self.module_files_updated = False
163
164        self.load_all_symbols()
165
166        if hasattr(gdb, 'Breakpoint'):
167            if self.breakpoint is not None:
168                self.breakpoint.delete()
169                self.breakpoint = None
170            self.breakpoint = LoadModuleBreakpoint(
171                "kernel/module.c:do_init_module", self)
172        else:
173            gdb.write("Note: symbol update on module loading not supported "
174                      "with this gdb version\n")
175
176
177LxSymbols()
178