xref: /linux/scripts/gdb/linux/symbols.py (revision bc46b7cbc58c4cb562b6a45a1fbc7b8e7b23df58)
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
17import struct
18
19from itertools import count
20from linux import modules, utils, constants
21
22
23if hasattr(gdb, 'Breakpoint'):
24    class LoadModuleBreakpoint(gdb.Breakpoint):
25        def __init__(self, spec, gdb_command):
26            super(LoadModuleBreakpoint, self).__init__(spec, internal=True)
27            self.silent = True
28            self.gdb_command = gdb_command
29
30        def stop(self):
31            module = gdb.parse_and_eval("mod")
32            module_name = module['name'].string()
33            cmd = self.gdb_command
34
35            # enforce update if object file is not found
36            cmd.module_files_updated = False
37
38            # Disable pagination while reporting symbol (re-)loading.
39            # The console input is blocked in this context so that we would
40            # get stuck waiting for the user to acknowledge paged output.
41            with utils.pagination_off():
42                if module_name in cmd.loaded_modules:
43                    gdb.write("refreshing all symbols to reload module "
44                              "'{0}'\n".format(module_name))
45                    cmd.load_all_symbols()
46                else:
47                    cmd.load_module_symbols(module)
48
49            return False
50
51
52def get_vmcore_s390():
53    with utils.qemu_phy_mem_mode():
54        vmcore_info = 0x0e0c
55        paddr_vmcoreinfo_note = gdb.parse_and_eval("*(unsigned long long *)" +
56                                                   hex(vmcore_info))
57        if paddr_vmcoreinfo_note == 0 or paddr_vmcoreinfo_note & 1:
58            # In the early boot case, extract vm_layout.kaslr_offset from the
59            # vmlinux image in physical memory.
60            if paddr_vmcoreinfo_note == 0:
61                kaslr_offset_phys = 0
62            else:
63                kaslr_offset_phys = paddr_vmcoreinfo_note - 1
64            with utils.pagination_off():
65                gdb.execute("symbol-file {0} -o {1}".format(
66                    utils.get_vmlinux(), hex(kaslr_offset_phys)))
67            kaslr_offset = gdb.parse_and_eval("vm_layout.kaslr_offset")
68            return "KERNELOFFSET=" + hex(kaslr_offset)[2:]
69        inferior = gdb.selected_inferior()
70        elf_note = inferior.read_memory(paddr_vmcoreinfo_note, 12)
71        n_namesz, n_descsz, n_type = struct.unpack(">III", elf_note)
72        desc_paddr = paddr_vmcoreinfo_note + len(elf_note) + n_namesz + 1
73        return gdb.parse_and_eval("(char *)" + hex(desc_paddr)).string()
74
75
76def get_kerneloffset():
77    if utils.is_target_arch('s390'):
78        try:
79            vmcore_str = get_vmcore_s390()
80        except gdb.error as e:
81            gdb.write("{}\n".format(e))
82            return None
83        return utils.parse_vmcore(vmcore_str).kerneloffset
84    return None
85
86
87def is_in_s390_decompressor():
88    # DAT is always off in decompressor. Use this as an indicator.
89    # Note that in the kernel, DAT can be off during kexec() or restart.
90    # Accept this imprecision in order to avoid complicating things.
91    # It is unlikely that someone will run lx-symbols at these points.
92    pswm = int(gdb.parse_and_eval("$pswm"))
93    return (pswm & 0x0400000000000000) == 0
94
95
96def skip_decompressor():
97    if utils.is_target_arch("s390"):
98        if is_in_s390_decompressor():
99            # The address of the jump_to_kernel function is statically placed
100            # into svc_old_psw.addr (see ipl_data.c); read it from there. DAT
101            # is off, so we do not need to care about lowcore relocation.
102            svc_old_pswa = 0x148
103            jump_to_kernel = int(gdb.parse_and_eval("*(unsigned long long *)" +
104                                                    hex(svc_old_pswa)))
105            gdb.execute("tbreak *" + hex(jump_to_kernel))
106            gdb.execute("continue")
107            while is_in_s390_decompressor():
108                gdb.execute("stepi")
109
110
111class LxSymbols(gdb.Command):
112    """(Re-)load symbols of Linux kernel and currently loaded modules.
113
114The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
115are scanned recursively, starting in the same directory. Optionally, the module
116search path can be extended by a space separated list of paths passed to the
117lx-symbols command."""
118
119    module_paths = []
120    module_files = []
121    module_files_updated = False
122    loaded_modules = []
123    breakpoint = None
124
125    def __init__(self):
126        super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES,
127                                        gdb.COMPLETE_FILENAME)
128
129    def _update_module_files(self):
130        self.module_files = []
131        for path in self.module_paths:
132            gdb.write("scanning for modules in {0}\n".format(path))
133            for root, dirs, files in os.walk(path):
134                for name in files:
135                    if name.endswith(".ko") or name.endswith(".ko.debug"):
136                        self.module_files.append(root + "/" + name)
137        self.module_files_updated = True
138
139    def _get_module_file(self, module_name):
140        module_pattern = r".*/{0}\.ko(?:.debug)?$".format(
141            module_name.replace("_", r"[_\-]"))
142        for name in self.module_files:
143            if re.match(module_pattern, name) and os.path.exists(name):
144                return name
145        return None
146
147    def _section_arguments(self, module, module_addr):
148        try:
149            sect_attrs = module['sect_attrs'].dereference()
150        except gdb.error:
151            return str(module_addr)
152
153        section_name_to_address = {}
154        for i in count():
155            # this is a NULL terminated array
156            if sect_attrs['grp']['bin_attrs'][i] == 0x0:
157                break
158
159            attr = sect_attrs['grp']['bin_attrs'][i].dereference()
160            section_name_to_address[attr['attr']['name'].string()] = attr['private']
161
162        textaddr = section_name_to_address.get(".text", module_addr)
163        args = []
164        for section_name in [".data", ".data..read_mostly", ".rodata", ".bss",
165                             ".text.hot", ".text.unlikely"]:
166            address = section_name_to_address.get(section_name)
167            if address:
168                args.append(" -s {name} {addr}".format(
169                    name=section_name, addr=str(address)))
170        return "{textaddr} {sections}".format(
171            textaddr=textaddr, sections="".join(args))
172
173    def load_module_symbols(self, module):
174        module_name = module['name'].string()
175        module_addr = str(module['mem'][constants.LX_MOD_TEXT]['base']).split()[0]
176
177        module_file = self._get_module_file(module_name)
178        if not module_file and not self.module_files_updated:
179            self._update_module_files()
180            module_file = self._get_module_file(module_name)
181
182        if module_file:
183            if utils.is_target_arch('s390'):
184                # Module text is preceded by PLT stubs on s390.
185                module_arch = module['arch']
186                plt_offset = int(module_arch['plt_offset'])
187                plt_size = int(module_arch['plt_size'])
188                module_addr = hex(int(module_addr, 0) + plt_offset + plt_size)
189            gdb.write("loading @{addr}: {filename}\n".format(
190                addr=module_addr, filename=module_file))
191            cmdline = "add-symbol-file {filename} {sections}".format(
192                filename=module_file,
193                sections=self._section_arguments(module, module_addr))
194            gdb.execute(cmdline, to_string=True)
195            if module_name not in self.loaded_modules:
196                self.loaded_modules.append(module_name)
197        else:
198            gdb.write("no module object found for '{0}'\n".format(module_name))
199
200    def load_all_symbols(self):
201        gdb.write("loading vmlinux\n")
202
203        # Dropping symbols will disable all breakpoints. So save their states
204        # and restore them afterward.
205        saved_states = []
206        if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
207            for bp in gdb.breakpoints():
208                saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
209
210        # drop all current symbols and reload vmlinux
211        orig_vmlinux = utils.get_vmlinux()
212        gdb.execute("symbol-file", to_string=True)
213        kerneloffset = get_kerneloffset()
214        if kerneloffset is None:
215            offset_arg = ""
216        else:
217            offset_arg = " -o " + hex(kerneloffset)
218        gdb.execute("symbol-file {0}{1}".format(orig_vmlinux, offset_arg))
219
220        self.loaded_modules = []
221        module_list = modules.module_list()
222        if not module_list:
223            gdb.write("no modules found\n")
224        else:
225            [self.load_module_symbols(module) for module in module_list]
226
227        for saved_state in saved_states:
228            saved_state['breakpoint'].enabled = saved_state['enabled']
229
230    def invoke(self, arg, from_tty):
231        skip_decompressor()
232
233        self.module_paths = [os.path.abspath(os.path.expanduser(p))
234                             for p in arg.split()]
235        self.module_paths.append(os.getcwd())
236
237        # enforce update
238        self.module_files = []
239        self.module_files_updated = False
240
241        self.load_all_symbols()
242
243        if not modules.has_modules():
244            return
245
246        if hasattr(gdb, 'Breakpoint'):
247            if self.breakpoint is not None:
248                self.breakpoint.delete()
249                self.breakpoint = None
250            self.breakpoint = LoadModuleBreakpoint(
251                "kernel/module/main.c:do_init_module", self)
252        else:
253            gdb.write("Note: symbol update on module loading not supported "
254                      "with this gdb version\n")
255
256
257LxSymbols()
258