xref: /linux/scripts/gdb/linux/symbols.py (revision 7d4e49a77d9930c69751b9192448fda6ff9100f1)
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
87class LxSymbols(gdb.Command):
88    """(Re-)load symbols of Linux kernel and currently loaded modules.
89
90The kernel (vmlinux) is taken from the current working directly. Modules (.ko)
91are scanned recursively, starting in the same directory. Optionally, the module
92search path can be extended by a space separated list of paths passed to the
93lx-symbols command."""
94
95    module_paths = []
96    module_files = []
97    module_files_updated = False
98    loaded_modules = []
99    breakpoint = None
100
101    def __init__(self):
102        super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES,
103                                        gdb.COMPLETE_FILENAME)
104
105    def _update_module_files(self):
106        self.module_files = []
107        for path in self.module_paths:
108            gdb.write("scanning for modules in {0}\n".format(path))
109            for root, dirs, files in os.walk(path):
110                for name in files:
111                    if name.endswith(".ko") or name.endswith(".ko.debug"):
112                        self.module_files.append(root + "/" + name)
113        self.module_files_updated = True
114
115    def _get_module_file(self, module_name):
116        module_pattern = r".*/{0}\.ko(?:.debug)?$".format(
117            module_name.replace("_", r"[_\-]"))
118        for name in self.module_files:
119            if re.match(module_pattern, name) and os.path.exists(name):
120                return name
121        return None
122
123    def _section_arguments(self, module, module_addr):
124        try:
125            sect_attrs = module['sect_attrs'].dereference()
126        except gdb.error:
127            return str(module_addr)
128
129        section_name_to_address = {}
130        for i in count():
131            # this is a NULL terminated array
132            if sect_attrs['grp']['bin_attrs'][i] == 0x0:
133                break
134
135            attr = sect_attrs['grp']['bin_attrs'][i].dereference()
136            section_name_to_address[attr['attr']['name'].string()] = attr['private']
137
138        textaddr = section_name_to_address.get(".text", module_addr)
139        args = []
140        for section_name in [".data", ".data..read_mostly", ".rodata", ".bss",
141                             ".text.hot", ".text.unlikely"]:
142            address = section_name_to_address.get(section_name)
143            if address:
144                args.append(" -s {name} {addr}".format(
145                    name=section_name, addr=str(address)))
146        return "{textaddr} {sections}".format(
147            textaddr=textaddr, sections="".join(args))
148
149    def load_module_symbols(self, module):
150        module_name = module['name'].string()
151        module_addr = str(module['mem'][constants.LX_MOD_TEXT]['base']).split()[0]
152
153        module_file = self._get_module_file(module_name)
154        if not module_file and not self.module_files_updated:
155            self._update_module_files()
156            module_file = self._get_module_file(module_name)
157
158        if module_file:
159            if utils.is_target_arch('s390'):
160                # Module text is preceded by PLT stubs on s390.
161                module_arch = module['arch']
162                plt_offset = int(module_arch['plt_offset'])
163                plt_size = int(module_arch['plt_size'])
164                module_addr = hex(int(module_addr, 0) + plt_offset + plt_size)
165            gdb.write("loading @{addr}: {filename}\n".format(
166                addr=module_addr, filename=module_file))
167            cmdline = "add-symbol-file {filename} {sections}".format(
168                filename=module_file,
169                sections=self._section_arguments(module, module_addr))
170            gdb.execute(cmdline, to_string=True)
171            if module_name not in self.loaded_modules:
172                self.loaded_modules.append(module_name)
173        else:
174            gdb.write("no module object found for '{0}'\n".format(module_name))
175
176    def load_all_symbols(self):
177        gdb.write("loading vmlinux\n")
178
179        # Dropping symbols will disable all breakpoints. So save their states
180        # and restore them afterward.
181        saved_states = []
182        if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None:
183            for bp in gdb.breakpoints():
184                saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
185
186        # drop all current symbols and reload vmlinux
187        orig_vmlinux = utils.get_vmlinux()
188        gdb.execute("symbol-file", to_string=True)
189        kerneloffset = get_kerneloffset()
190        if kerneloffset is None:
191            offset_arg = ""
192        else:
193            offset_arg = " -o " + hex(kerneloffset)
194        gdb.execute("symbol-file {0}{1}".format(orig_vmlinux, offset_arg))
195
196        self.loaded_modules = []
197        module_list = modules.module_list()
198        if not module_list:
199            gdb.write("no modules found\n")
200        else:
201            [self.load_module_symbols(module) for module in module_list]
202
203        for saved_state in saved_states:
204            saved_state['breakpoint'].enabled = saved_state['enabled']
205
206    def invoke(self, arg, from_tty):
207        self.module_paths = [os.path.abspath(os.path.expanduser(p))
208                             for p in arg.split()]
209        self.module_paths.append(os.getcwd())
210
211        # enforce update
212        self.module_files = []
213        self.module_files_updated = False
214
215        self.load_all_symbols()
216
217        if not modules.has_modules():
218            return
219
220        if hasattr(gdb, 'Breakpoint'):
221            if self.breakpoint is not None:
222                self.breakpoint.delete()
223                self.breakpoint = None
224            self.breakpoint = LoadModuleBreakpoint(
225                "kernel/module/main.c:do_init_module", self)
226        else:
227            gdb.write("Note: symbol update on module loading not supported "
228                      "with this gdb version\n")
229
230
231LxSymbols()
232