]>
Commit | Line | Data |
---|---|---|
66051720 JK |
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 | |
66051720 | 17 | |
6ad18b73 | 18 | from linux import modules |
66051720 JK |
19 | |
20 | ||
82b41e3d JK |
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 | ||
a9c5bcfa JK |
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 | ||
82b41e3d JK |
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) | |
a9c5bcfa JK |
49 | |
50 | # restore pagination state | |
51 | gdb.execute("set pagination %s" % ("on" if pagination else "off")) | |
52 | ||
82b41e3d JK |
53 | return False |
54 | ||
55 | ||
66051720 JK |
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 | |
82b41e3d JK |
67 | loaded_modules = [] |
68 | breakpoint = None | |
66051720 JK |
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( | |
276d97d9 | 86 | module_name.replace("_", r"[_\-]")) |
66051720 JK |
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 = { | |
6ad18b73 | 99 | attrs[n]['name'].string(): attrs[n]['address'] |
276d97d9 | 100 | for n in range(int(sect_attrs['nsections']))} |
66051720 JK |
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() | |
ad4db3b2 | 111 | module_addr = str(module['core_layout']['base']).split()[0] |
66051720 JK |
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 | gdb.write("loading @{addr}: {filename}\n".format( | |
120 | addr=module_addr, filename=module_file)) | |
121 | cmdline = "add-symbol-file {filename} {addr}{sections}".format( | |
122 | filename=module_file, | |
123 | addr=module_addr, | |
124 | sections=self._section_arguments(module)) | |
125 | gdb.execute(cmdline, to_string=True) | |
6ad18b73 | 126 | if module_name not in self.loaded_modules: |
82b41e3d | 127 | self.loaded_modules.append(module_name) |
66051720 JK |
128 | else: |
129 | gdb.write("no module object found for '{0}'\n".format(module_name)) | |
130 | ||
131 | def load_all_symbols(self): | |
132 | gdb.write("loading vmlinux\n") | |
133 | ||
134 | # Dropping symbols will disable all breakpoints. So save their states | |
135 | # and restore them afterward. | |
136 | saved_states = [] | |
137 | if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: | |
138 | for bp in gdb.breakpoints(): | |
139 | saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) | |
140 | ||
141 | # drop all current symbols and reload vmlinux | |
dfe4529e SB |
142 | orig_vmlinux = 'vmlinux' |
143 | for obj in gdb.objfiles(): | |
144 | if obj.filename.endswith('vmlinux'): | |
145 | orig_vmlinux = obj.filename | |
66051720 | 146 | gdb.execute("symbol-file", to_string=True) |
dfe4529e | 147 | gdb.execute("symbol-file {0}".format(orig_vmlinux)) |
66051720 | 148 | |
82b41e3d | 149 | self.loaded_modules = [] |
fffb944c | 150 | module_list = modules.module_list() |
66051720 JK |
151 | if not module_list: |
152 | gdb.write("no modules found\n") | |
153 | else: | |
154 | [self.load_module_symbols(module) for module in module_list] | |
155 | ||
156 | for saved_state in saved_states: | |
157 | saved_state['breakpoint'].enabled = saved_state['enabled'] | |
158 | ||
159 | def invoke(self, arg, from_tty): | |
552ab2a3 | 160 | self.module_paths = [os.path.expanduser(p) for p in arg.split()] |
66051720 JK |
161 | self.module_paths.append(os.getcwd()) |
162 | ||
163 | # enforce update | |
164 | self.module_files = [] | |
165 | self.module_files_updated = False | |
166 | ||
167 | self.load_all_symbols() | |
168 | ||
82b41e3d | 169 | if hasattr(gdb, 'Breakpoint'): |
6ad18b73 | 170 | if self.breakpoint is not None: |
82b41e3d JK |
171 | self.breakpoint.delete() |
172 | self.breakpoint = None | |
173 | self.breakpoint = LoadModuleBreakpoint( | |
174 | "kernel/module.c:do_init_module", self) | |
175 | else: | |
176 | gdb.write("Note: symbol update on module loading not supported " | |
177 | "with this gdb version\n") | |
178 | ||
66051720 JK |
179 | |
180 | LxSymbols() |