| # Copyright (C) 2025-2026 Free Software Foundation, Inc. | |
| # This program is free software; you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation; either version 3 of the License, or | |
| # (at your option) any later version. | |
| # | |
| # This program is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| import pathlib | |
| import gdb | |
| class Mapping: | |
| def __init__(self, mapping, region): | |
| self._mapping = mapping | |
| self._region = region | |
| @property | |
| def start(self): | |
| return self._region.start | |
| @property | |
| def end(self): | |
| return self._region.end | |
| @property | |
| def offset(self): | |
| return self._region.file_offset | |
| @property | |
| def filename(self): | |
| return self._mapping.filename | |
| def info_proc_mappings(): | |
| ptr_size = gdb.lookup_type("void").pointer().sizeof | |
| print("Mapped address spaces:") | |
| print("") | |
| if ptr_size == 4: | |
| format_str = "%-10s %-10s %-10s %-10s %s " | |
| else: | |
| format_str = "%-18s %-18s %-18s %-18s %s " | |
| print(format_str % ("Start Addr", "End Addr", "Size", "Offset", "File")) | |
| core = gdb.selected_inferior().corefile | |
| mappings = core.mapped_files() | |
| result = [] | |
| for m in mappings: | |
| for r in m.regions: | |
| result.append(Mapping(m, r)) | |
| result.sort(key=lambda x: x.start) | |
| for r in result: | |
| sz = r.end - r.start | |
| if ptr_size == 4: | |
| t = ( | |
| "0x%08x" % r.start, | |
| "0x%08x" % r.end, | |
| "0x%-8x" % sz, | |
| "0x%-8x" % r.offset, | |
| "%s" % r.filename, | |
| ) | |
| else: | |
| t = ( | |
| "0x%016x" % r.start, | |
| "0x%016x" % r.end, | |
| "0x%-16x" % sz, | |
| "0x%-16x" % r.offset, | |
| "%s" % r.filename, | |
| ) | |
| print(format_str % t) | |
| class InfoProcPyMappings(gdb.Command): | |
| def __init__(self): | |
| gdb.Command.__init__(self, "info proc py-mappings", gdb.COMMAND_DATA) | |
| def invoke(self, args, from_tty): | |
| info_proc_mappings() | |
| InfoProcPyMappings() | |
| # Assume that a core file is currently loaded. | |
| # | |
| # Look through all the objfiles for the current inferior, and record | |
| # any that have a build-id. | |
| # | |
| # Then look through the core file mapped files. Look for entries that | |
| # correspond with the loaded objfiles. For these matching entries, | |
| # capture the build-id extracted from the core file. | |
| # | |
| # Finally, print a table with the build-id from the objfile, the | |
| # build-id from the core file, and the file name. | |
| # | |
| # This is then processed from the test script to check the build-ids | |
| # match. | |
| class ShowBuildIds(gdb.Command): | |
| def __init__(self): | |
| gdb.Command.__init__(self, "show-build-ids", gdb.COMMAND_DATA) | |
| def invoke(self, args, from_tty): | |
| inf = gdb.selected_inferior() | |
| objfiles = inf.progspace.objfiles() | |
| path_to_build_id = {} | |
| # Initial length based on column headings. | |
| longest_build_id = 18 | |
| for o in objfiles: | |
| if not o.is_file or o.build_id is None or o.owner is not None: | |
| continue | |
| p = pathlib.Path(o.filename).resolve() | |
| b = o.build_id | |
| path_to_build_id[p] = {"objfile": b, "corefile": "missing"} | |
| if len(b) > longest_build_id: | |
| longest_build_id = len(b) | |
| core_mapped_files = inf.corefile.mapped_files() | |
| for m in core_mapped_files: | |
| p = pathlib.Path(m.filename).resolve() | |
| b = m.build_id | |
| if b is not None and len(b) > longest_build_id: | |
| longest_build_id = len(b) | |
| if p in path_to_build_id: | |
| path_to_build_id[p]["corefile"] = b | |
| format_str = ( | |
| "%-" + str(longest_build_id) + "s %-" + str(longest_build_id) + "s %s" | |
| ) | |
| def make_title(string, length=0): | |
| if length > 0: | |
| padding_len = length - len(string) | |
| else: | |
| padding_len = 0 | |
| padding = " " * padding_len | |
| style = gdb.Style("title") | |
| return style.apply(string) + padding | |
| print( | |
| "%s %s %s" | |
| % ( | |
| make_title("Objfile Build-Id", longest_build_id), | |
| make_title("Core File Build-Id", longest_build_id), | |
| make_title("File Name"), | |
| ) | |
| ) | |
| for p, b in path_to_build_id.items(): | |
| print(format_str % (b["objfile"], b["corefile"], p)) | |
| ShowBuildIds() | |
| class CheckMainExec(gdb.Command): | |
| def __init__(self): | |
| gdb.Command.__init__(self, "check-main-executable", gdb.COMMAND_DATA) | |
| def invoke(self, args, from_tty): | |
| inf = gdb.selected_inferior() | |
| pspace = inf.progspace | |
| exec_filename = pathlib.Path(pspace.executable_filename).resolve() | |
| count = 0 | |
| core_mapped_files = inf.corefile.mapped_files() | |
| for m in core_mapped_files: | |
| if not m.is_main_executable: | |
| continue | |
| p = pathlib.Path(m.filename).resolve() | |
| count += 1 | |
| assert exec_filename == p, "main exec filename mismatch" | |
| assert count == 1, "invalid main executable count" | |
| print("PASS") | |
| CheckMainExec() | |
| print("Success") |