| # 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") |