aboutsummaryrefslogtreecommitdiff
path: root/gdb/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/contrib')
-rw-r--r--gdb/contrib/ari/create-web-ari-in-src.sh4
-rwxr-xr-xgdb/contrib/ari/gdb_ari.sh6
-rw-r--r--gdb/contrib/ari/gdb_find.sh2
-rw-r--r--gdb/contrib/ari/update-web-ari.sh10
-rwxr-xr-xgdb/contrib/cc-with-tweaks.sh7
-rwxr-xr-x[-rw-r--r--]gdb/contrib/check-gnu-style-pre-commit.sh (renamed from gdb/contrib/common-misspellings.txt)38
-rwxr-xr-xgdb/contrib/check-whitespace-pre-commit.py52
-rw-r--r--gdb/contrib/codespell-dictionary.txt1
-rw-r--r--gdb/contrib/codespell-ignore-words.txt2
-rwxr-xr-xgdb/contrib/codespell-log.sh95
-rwxr-xr-xgdb/contrib/dwarf-to-dwarf-assembler.py654
-rw-r--r--gdb/contrib/expect-read1.c2
-rwxr-xr-xgdb/contrib/expect-read1.sh2
-rwxr-xr-xgdb/contrib/gdb-add-index.sh2
-rwxr-xr-xgdb/contrib/license-check-new-files.sh149
-rw-r--r--gdb/contrib/setup.cfg19
-rwxr-xr-xgdb/contrib/spellcheck.sh536
-rw-r--r--gdb/contrib/test_pubnames_and_indexes.py2
-rwxr-xr-xgdb/contrib/words.sh2
19 files changed, 1018 insertions, 567 deletions
diff --git a/gdb/contrib/ari/create-web-ari-in-src.sh b/gdb/contrib/ari/create-web-ari-in-src.sh
index 64d3c73..b479ce5 100644
--- a/gdb/contrib/ari/create-web-ari-in-src.sh
+++ b/gdb/contrib/ari/create-web-ari-in-src.sh
@@ -2,7 +2,7 @@
# GDB script to create web ARI page directly from within gdb/ari directory.
#
-# Copyright (C) 2012-2024 Free Software Foundation, Inc.
+# Copyright (C) 2012-2025 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
@@ -50,7 +50,7 @@ if [ -z "${tempdir}" ] ; then
fi
fi
-# Default location of generate index.hmtl web page.
+# Default location of generated index.html web page.
if [ -z "${webdir}" ] ; then
# Use 'branch' subdir name if Tag contains branch
if [ -f "${srcdir}/gdb/CVS/Tag" ] ; then
diff --git a/gdb/contrib/ari/gdb_ari.sh b/gdb/contrib/ari/gdb_ari.sh
index 5ed7d61..e10bbe0 100755
--- a/gdb/contrib/ari/gdb_ari.sh
+++ b/gdb/contrib/ari/gdb_ari.sh
@@ -2,7 +2,7 @@
# GDB script to list of problems using awk.
#
-# Copyright (C) 2002-2024 Free Software Foundation, Inc.
+# Copyright (C) 2002-2025 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
@@ -60,7 +60,7 @@ Options:
-Werror Treat all problems as errors.
-Wall Report all problems.
-Wari Report problems that should be fixed in new code.
- -WCATEGORY Report problems in the specifed category. The category
+ -WCATEGORY Report problems in the specified category. The category
can be prefixed with "no-". Valid categories
are: ${all}
EOF
@@ -159,7 +159,7 @@ BEGIN {
PWD = "'`pwd`'"
}
-# Print the error message for BUG. Append SUPLEMENT if non-empty.
+# Print the error message for BUG. Append SUPPLEMENT if non-empty.
function print_bug(file,line,prefix,category,bug,doc,supplement, suffix,idx) {
if (print_idx) {
idx = bug ": "
diff --git a/gdb/contrib/ari/gdb_find.sh b/gdb/contrib/ari/gdb_find.sh
index f0aa6d1..b6bd3f9 100644
--- a/gdb/contrib/ari/gdb_find.sh
+++ b/gdb/contrib/ari/gdb_find.sh
@@ -2,7 +2,7 @@
# GDB script to create list of files to check using gdb_ari.sh.
#
-# Copyright (C) 2003-2024 Free Software Foundation, Inc.
+# Copyright (C) 2003-2025 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
diff --git a/gdb/contrib/ari/update-web-ari.sh b/gdb/contrib/ari/update-web-ari.sh
index 75ab029..117c0ba 100644
--- a/gdb/contrib/ari/update-web-ari.sh
+++ b/gdb/contrib/ari/update-web-ari.sh
@@ -2,7 +2,7 @@
# GDB script to create GDB ARI web page.
#
-# Copyright (C) 2001-2024 Free Software Foundation, Inc.
+# Copyright (C) 2001-2025 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
@@ -176,7 +176,7 @@ fi
# THIS HAS SUFFERED BIT ROT
if ${check_indent_p} && test -d "${srcdir}"
then
- printf "Analizing file indentation:" 1>&2
+ printf "Analyzing file indentation:" 1>&2
( cd "${srcdir}" && /bin/sh ${aridir}/gdb_find.sh ${project} | while read f
do
if /bin/sh ${aridir}/gdb_indent.sh < ${f} 2>/dev/null | cmp -s - ${f}
@@ -550,7 +550,7 @@ function print_heading (nb_file, where, bug_i) {
for (bug_i = 1; bug_i <= nr_bug; bug_i++) {
bug = i2bug[bug_i];
printf "<th>"
- # The title names are offset by one. Otherwize, when the browser
+ # The title names are offset by one. Otherwise, when the browser
# jumps to the name it leaves out half the relevant column.
#printf "<a name=\",%s\">&nbsp;</a>", bug
printf "<a name=\",%s\">&nbsp;</a>", i2bug[bug_i-1]
@@ -762,7 +762,7 @@ END {
print "bugs/oks: " bugs "/" oks
print bugs "/ (" oks "+" legacy "+" deprecated ")"
}
- # This value should be as low as possible
+ # This value should be as low as possible
print bugs / ( oks + legacy + deprecated )
}
' ${wwwdir}/ari.doc`
@@ -851,7 +851,7 @@ EOF
print_toc 0 -1 deprecate Deprecate <<EOF
Mechanisms that are a candidate for being made obsolete. Once core
GDB no longer depends on these mechanisms and/or there is a
-replacement available, these mechanims can be deprecated (adding the
+replacement available, these mechanisms can be deprecated (adding the
deprecated prefix) obsoleted (put into category obsolete) or deleted.
See obsolete and deprecated.
EOF
diff --git a/gdb/contrib/cc-with-tweaks.sh b/gdb/contrib/cc-with-tweaks.sh
index 4214b92..930ba5c 100755
--- a/gdb/contrib/cc-with-tweaks.sh
+++ b/gdb/contrib/cc-with-tweaks.sh
@@ -2,7 +2,7 @@
# Wrapper around gcc to tweak the output in various ways when running
# the testsuite.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 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
@@ -42,6 +42,7 @@
# -Z invoke objcopy --compress-debug-sections
# -z compress using dwz
# -m compress using dwz -m
+# -5 compress using dwz -m -5
# -i make an index (.gdb_index)
# -c make an index (currently .gdb_index) in a cache dir
# -n make a dwarf5 index (.debug_names)
@@ -88,6 +89,7 @@ want_index=false
index_options=""
want_index_cache=false
want_dwz=false
+dwz_5flag=
want_multi=false
want_dwp=false
want_objcopy_compress=false
@@ -101,6 +103,7 @@ while [ $# -gt 0 ]; do
-n) want_index=true; index_options=-dwarf-5;;
-c) want_index_cache=true ;;
-m) want_multi=true ;;
+ -5) want_multi=true; dwz_5flag=-5 ;;
-p) want_dwp=true ;;
-l) want_gnu_debuglink=true ;;
*) break ;;
@@ -269,7 +272,7 @@ elif [ "$want_multi" = true ]; then
rm -f "$dwz_file"
cp "$output_file" "${output_file}.alt"
- $DWZ -m "$dwz_file" "$output_file" "${output_file}.alt" > /dev/null
+ $DWZ $dwz_5flag -m "$dwz_file" "$output_file" "${output_file}.alt" > /dev/null
rm -f "${output_file}.alt"
# Validate dwz's work by checking if the expected output file exists.
diff --git a/gdb/contrib/common-misspellings.txt b/gdb/contrib/check-gnu-style-pre-commit.sh
index 5772f66..ab4b5e7 100644..100755
--- a/gdb/contrib/common-misspellings.txt
+++ b/gdb/contrib/check-gnu-style-pre-commit.sh
@@ -1,4 +1,6 @@
-# Copyright (C) 2024 Free Software Foundation, Inc.
+#!/bin/sh
+
+# Copyright (C) 2025 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
@@ -12,19 +14,29 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# This file contains additions to and overrides for
-# wikipedia-common-misspellings.txt.
+set -e
+
+scriptdir=$(cd "$(dirname "$0")" || exit 1; pwd -P)
+
+tmp=""
+
+cleanup()
+{
+ if [ "$tmp" != "" ]; then
+ rm -f "$tmp"
+ fi
+}
-# Common spelling mistakes.
+# Schedule cleanup.
+trap cleanup EXIT
-inbetween->between, in between, in-between
-sofar->so far
-doens't->doesn't
-behavour->behavior
-behaviour->behavior
-arrithemetic->arithmetic
-electricaly->electrically
+# Get temporary file.
+tmp=$(mktemp)
-# Identity rules.
+# Generate patch.
+git diff --staged "$@" \
+ > "$tmp"
-thru->thru
+# Verify patch. Ignore exit status.
+"$scriptdir"/../../contrib/check_GNU_style.py "$tmp" \
+ || true
diff --git a/gdb/contrib/check-whitespace-pre-commit.py b/gdb/contrib/check-whitespace-pre-commit.py
new file mode 100755
index 0000000..9d82f77
--- /dev/null
+++ b/gdb/contrib/check-whitespace-pre-commit.py
@@ -0,0 +1,52 @@
+#! /usr/bin/env python3
+
+# Copyright (C) 2025 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 re
+import subprocess
+import sys
+
+re_clean = re.compile(
+ "(^(gdb/testsuite/|gdbsupport/|gdbserver/)|[.](m4|ac|def|[chly])$|NEWS)"
+)
+re_clean_exclude = re.compile("^(gdb/symfile.c|gdb/cli/cli-cmds.c)$")
+
+clean = []
+other = []
+for f in sys.argv[1:]:
+ m = re_clean.search(f)
+ if m and re_clean_exclude.search(f):
+ m = None
+ if m:
+ clean.append(f)
+ else:
+ other.append(f)
+
+if other:
+ cmd = ["git", "--no-pager", "diff", "--staged", "--check"] + other
+ res = subprocess.run(cmd)
+ if res.returncode != 0:
+ sys.exit(res.returncode)
+
+if clean:
+ cmd = ["git", "hash-object", "-t", "tree", "/dev/null"]
+ res = subprocess.run(cmd, capture_output=True, text=True)
+ if res.returncode != 0:
+ sys.exit(res.returncode)
+ null_tree = res.stdout.rstrip("\n")
+
+ cmd = ["git", "diff-index", "--cached", "--check", null_tree] + clean
+ res = subprocess.run(cmd)
+ sys.exit(res.returncode)
diff --git a/gdb/contrib/codespell-dictionary.txt b/gdb/contrib/codespell-dictionary.txt
new file mode 100644
index 0000000..09bc309
--- /dev/null
+++ b/gdb/contrib/codespell-dictionary.txt
@@ -0,0 +1 @@
+gdbsever->gdbserver
diff --git a/gdb/contrib/codespell-ignore-words.txt b/gdb/contrib/codespell-ignore-words.txt
index 2d6e13a..c07df0c 100644
--- a/gdb/contrib/codespell-ignore-words.txt
+++ b/gdb/contrib/codespell-ignore-words.txt
@@ -1,2 +1,4 @@
configury
SME
+Synopsys
+aranges
diff --git a/gdb/contrib/codespell-log.sh b/gdb/contrib/codespell-log.sh
new file mode 100755
index 0000000..10780f8
--- /dev/null
+++ b/gdb/contrib/codespell-log.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2025 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/>.
+
+# Script to be used as pre-commit commit-msg hook to spell-check the commit
+# log using codespell.
+#
+# Using codespell directly as a pre-commit commit-msg hook has the drawback
+# that:
+# - if codespell fails, the commit fails
+# - if the commit log mentions a typo correction, it'll require a
+# codespell:ignore annotation.
+#
+# This script works around these problems by treating codespell output as a
+# hint, and ignoring codespell exit status.
+#
+# Implementation note: rather than using codespell directly, this script uses
+# pre-commit to call codespell, because it allows us to control the codespell
+# version that is used.
+
+# Exit on error.
+set -e
+
+# Initialize temporary file names.
+cfg=""
+output=""
+
+cleanup()
+{
+ for f in "$cfg" "$output"; do
+ if [ "$f" != "" ]; then
+ rm -f "$f"
+ fi
+ done
+}
+
+# Schedule cleanup.
+trap cleanup EXIT
+
+# Create temporary files.
+cfg=$(mktemp)
+output=$(mktemp)
+
+gen_cfg ()
+{
+ cat > "$1" <<EOF
+repos:
+- repo: https://github.com/codespell-project/codespell
+ rev: v2.4.1
+ hooks:
+ - id: codespell
+ name: codespell-log-internal
+ stages: [manual]
+ args: [--config, gdb/contrib/setup.cfg]
+EOF
+}
+
+# Generate pre-commit configuration file.
+gen_cfg "$cfg"
+
+# Setup pre-commit command to run.
+cmd=(pre-commit \
+ run \
+ -c "$cfg" \
+ codespell \
+ --hook-stage manual \
+ --files "$@")
+
+# Run pre-commit command.
+if "${cmd[@]}" \
+ > "$output" \
+ 2>&1; then
+ # Command succeeded quietly, we're done.
+ exit 0
+fi
+
+# Command failed quietly, now show the output.
+#
+# Simply doing "cat $output" doesn't produce colored output, so we just
+# run the command again, that should be fast enough.
+#
+# Ignore codespell exit status.
+"${cmd[@]}" || true
diff --git a/gdb/contrib/dwarf-to-dwarf-assembler.py b/gdb/contrib/dwarf-to-dwarf-assembler.py
new file mode 100755
index 0000000..37bd1ba
--- /dev/null
+++ b/gdb/contrib/dwarf-to-dwarf-assembler.py
@@ -0,0 +1,654 @@
+#!/usr/bin/env python3
+# pyright: strict
+
+# Copyright 2024, 2025 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/>.
+
+# Due to the pyelftools dependency, this script requires Python version
+# 3.10 or greater to run.
+
+"""A utility to convert ELF files with DWARF info to Dwarf::assemble code.
+
+Usage:
+ python ./asm_to_dwarf_assembler.py <path/to/elf/file>
+
+Dependencies:
+ Python >= 3.10
+ pyelftools >= 0.31
+
+Notes:
+- Line tables are not currently supported.
+- Non-contiguous subprograms are not currently supported.
+- If you want to use $srcfile or similar, you must edit the references to the
+ file name manually, including DW_AT_name attributes on compile units.
+- If run with binaries generated by make check-gdb, it may include an
+ additional compile_unit before and after the actual compile units. This is
+ an artifact of the normal compilation process, as these CUs are indeed in
+ the generated DWARF in some cases.
+"""
+
+import errno
+import re
+import sys
+from copy import copy
+from functools import cache
+from io import BytesIO, IOBase
+from logging import getLogger
+from typing import Annotated, Optional
+
+from elftools.dwarf.compileunit import CompileUnit as RawCompileUnit
+from elftools.dwarf.die import DIE as RawDIE
+from elftools.dwarf.die import AttributeValue
+from elftools.dwarf.enums import ENUM_DW_ATE, ENUM_DW_LANG
+from elftools.elf.elffile import ELFFile
+
+logger = getLogger(__file__)
+
+
+# While these aren't supported, their detection is important for replacing them
+# with SPECIAL_expr and for writing the placeholder {MANUAL} expr list.
+EXPR_ATTRIBUTE_FORMS = [
+ "DW_FORM_exprloc",
+ "DW_FORM_block",
+ "DW_FORM_block1",
+ "DW_FORM_block2",
+ "DW_FORM_block4",
+]
+
+# Map from language number to name.
+LANG_NAME = {v: k for k, v in ENUM_DW_LANG.items()}
+# Map from encoding number to name.
+ATE_NAME = {v: k for k, v in ENUM_DW_ATE.items()}
+
+# Workaround for my editor not to freak out over unclosed braces.
+lbrace, rbrace = "{", "}"
+
+
+@cache
+def get_indent_str(indent_count: int) -> str:
+ """Get whitespace string to prepend to another for indenting."""
+ indent = (indent_count // 2) * "\t"
+ if indent_count % 2 == 1:
+ indent += " "
+ return indent
+
+
+def indent(line: str, indent_count: int) -> str:
+ """Indent line by indent_count levels."""
+ return get_indent_str(indent_count) + line
+
+
+def labelify_str(s: str) -> str:
+ """Make s appropriate for a label name."""
+ # Replace "*" with the literal word "ptr".
+ s = s.replace("*", "ptr")
+
+ # Replace any non-"word" characters by "_".
+ s = re.sub(r"\W", "_", s)
+
+ # Remove consecutive "_"s.
+ s = re.sub(r"__+", "_", s)
+
+ return s
+
+
+class DWARFAttribute:
+ """Storage unit for a single DWARF attribute.
+
+ All its values are strings that are usually passed on
+ directly to format. The exceptions to this are attributes
+ with int values with DW_FORM_ref4 or DW_FORM_ref_addr form.
+ Their values are interpreted as the global offset of the DIE
+ being referenced, which are looked up dynamically to fetch
+ their labels.
+ """
+
+ def __init__(
+ self,
+ die_offset: int,
+ name: str,
+ value: str | bytes | int | bool,
+ form=None,
+ ):
+ self.die_offset = die_offset
+ self.name = name
+ self.value = value
+ self.form = form
+
+ def _format_expr_value(self) -> str:
+ self.form = "SPECIAL_expr"
+ return "{ MANUAL: Fill expr list }"
+
+ def _needs_escaping(self, str_value: str) -> bool:
+ charset = set(str_value)
+ return bool(charset.intersection({"{", "}", " ", "\t"}))
+
+ def _format_str(self, str_value: str) -> str:
+ if self._needs_escaping(str_value):
+ escaped_str = str(str_value)
+ # Replace single escape (which is itself escaped because of regex)
+ # with a double escape (which doesn't mean anything to regex so
+ # it doesn't need escaping).
+ escaped_str = re.sub(r"\\", r"\\", escaped_str)
+ escaped_str = re.sub("([{}])", r"\\\1", escaped_str)
+ return "{" + escaped_str + "}"
+ else:
+ return str_value
+
+ def _format_value(
+ self, offset_die_lookup: dict[int, "DWARFDIE"], indent_count: int = 0
+ ) -> str:
+ if self.form in EXPR_ATTRIBUTE_FORMS:
+ return self._format_expr_value()
+ elif isinstance(self.value, bool):
+ return str(int(self.value))
+ elif isinstance(self.value, int):
+ if self.form == "DW_FORM_ref4":
+ # ref4-style referencing label.
+ die = offset_die_lookup[self.value]
+ return ":$" + die.tcl_label
+ elif self.form == "DW_FORM_ref_addr":
+ # ref_addr-style referencing label.
+ die = offset_die_lookup[self.value]
+ return "%$" + die.tcl_label
+ else:
+ return str(self.value)
+ elif isinstance(self.value, bytes):
+ return self._format_str(self.value.decode("ascii"))
+ elif isinstance(self.value, str):
+ return self._format_str(self.value)
+ else:
+ raise NotImplementedError(f"Unknown data type: {type(self.value)}")
+
+ def format(
+ self, offset_die_lookup: dict[int, "DWARFDIE"], indent_count: int = 0
+ ) -> str:
+ """Format the attribute in the form {name value form}.
+
+ If form is DW_FORM_exprloc or DW_FORM_block, see next section on
+ DWARFOperations.
+
+ If it isn't, value is formatted as follows:
+ If bool, use "1" if True, "0" if False.
+ If int:
+ If form is DW_FORM_ref4, use ":$label" where label is the
+ tcl_label of the DWARFDIE at offset "value".
+ If form is DW_FORM_ref_addr, use "%$label" where label is
+ the tcl_label of the DWARFDIE at offset "value".
+ Else, use value directly.
+ If bytes, use value.decode("ascii")
+ If str, use value directly.
+ Any other type results in a NotImplementedError being raised.
+
+ Regarding DW_FORM_exprloc and DW_FORM_block:
+ The form is replaced with SPECIAL_expr.
+ The entries in the value are interpreted and decoded using the
+ dwarf_operations dictionary, and replaced with their names where
+ applicable.
+ """
+ s = ""
+ if isinstance(self.name, int):
+ s += "DW_AT_" + hex(self.name)
+ else:
+ s += self.name
+ s += " "
+
+ if self.name == "DW_AT_language" and isinstance(self.value, int):
+ s += "@" + LANG_NAME[self.value]
+ elif self.name == "DW_AT_encoding" and isinstance(self.value, int):
+ s += "@" + ATE_NAME[self.value]
+ else:
+ s += self._format_value(offset_die_lookup)
+
+ # Only explicitly state form if it's not a reference.
+ if self.form not in [None, "DW_FORM_ref4", "DW_FORM_ref_addr"]:
+ s += " " + self.form
+
+ return indent(s, indent_count)
+
+
+class DWARFDIE:
+ """This script's parsed version of a RawDIE."""
+
+ def __init__(
+ self,
+ offset: int,
+ tag: str,
+ attrs: dict[str, DWARFAttribute],
+ tcl_label: Optional[str] = None,
+ ):
+ self.offset: Annotated[int, "Global offset of the DIE."] = offset
+ self.tag: Annotated[str, "DWARF tag for this DIE."] = tag
+ self.attrs: Annotated[
+ dict[str, DWARFAttribute], "Dict of attributes for this DIE."
+ ] = copy(attrs)
+ self.children: Annotated[list[DWARFDIE], "List of child DIEs of this DIE."] = []
+ self.tcl_label: Annotated[
+ str,
+ "Label used by the Tcl code to reference this DIE, if any. These "
+ 'take the form of "label: " before the actual DIE definition.',
+ ] = tcl_label
+
+ def format_lines(
+ self, offset_die_lookup: dict[int, "DWARFDIE"], indent_count: int = 0
+ ) -> list[str]:
+ """Get the list of lines that represent this DIE in Dwarf assembler."""
+ die_lines = []
+
+ # Prepend label to first line, if it's set.
+ if self.tcl_label:
+ first_line_start = self.tcl_label + ": "
+ else:
+ first_line_start = ""
+
+ # First line, including label.
+ first_line = indent(first_line_start + self.tag + " " + lbrace, indent_count)
+ die_lines.append(first_line)
+
+ # Format attributes, if any.
+ if self.attrs:
+ for attr_name, attr in self.attrs.items():
+ attr_line = attr.format(
+ offset_die_lookup, indent_count=indent_count + 1
+ )
+ die_lines.append(attr_line)
+ die_lines.append(indent(rbrace, indent_count))
+ else:
+ # Don't create a new line, just append and immediately close the
+ # brace on the last line.
+ die_lines[-1] += rbrace
+
+ # Format children, if any.
+ if self.children:
+ # Only open a new brace if there are any children for the
+ # current DIE.
+ die_lines[-1] += " " + lbrace
+ for child in self.children:
+ child_lines = child.format_lines(
+ offset_die_lookup, indent_count=indent_count + 1
+ )
+ die_lines.extend(child_lines)
+ die_lines.append(indent(rbrace, indent_count))
+
+ return die_lines
+
+ def format(
+ self, offset_die_lookup: dict[int, "DWARFDIE"], indent_count: int = 0
+ ) -> str:
+ """Join result from format_lines into a single str."""
+ return "\n".join(self.format_lines(offset_die_lookup, indent_count))
+
+ def name(self) -> Optional[str]:
+ """Get DW_AT_name (if present) decoded as ASCII."""
+ raw_value = self.attrs.get("DW_AT_name")
+ if raw_value is None:
+ return None
+ else:
+ return raw_value.value.decode("ascii")
+
+ def type_name(self) -> str:
+ """Name of Dwarf tag, with the "DW_TAG_" prefix removed."""
+ return re.sub("DW_TAG_", "", self.tag)
+
+
+class DWARFCompileUnit(DWARFDIE):
+ """Wrapper subclass for CU DIEs.
+
+ This is necessary due to the special format CUs take in Dwarf::assemble.
+
+ Instead of simply:
+ DW_TAG_compile_unit {
+ <attributes>
+ } {
+ <children>
+ }
+
+ CUs are formatted as:
+ cu { <cu_special_vars> } {
+ DW_TAG_compile_unit {
+ <attributes>
+ } {
+ <children>
+ }
+ }
+ """
+
+ # Default value for parameter is_64 defined in dwarf.exp line 1553.
+ # This value is converted to 0/1 automatically when emitting
+ # Dwarf::assemble code.
+ default_is_64 = False
+
+ # Default value for parameter dwarf_version defined in dwarf.exp line 1552.
+ default_dwarf_version = 4
+
+ # Default value for parameter is_fission defined in dwarf.exp line 1556.
+ # Currently not implemented, see comment below.
+ # default_is_fission = False
+
+ # Tag that signifies a DIE is a compile unit.
+ compile_unit_tag = "DW_TAG_compile_unit"
+
+ def __init__(
+ self,
+ raw_die: RawDIE,
+ raw_cu: RawCompileUnit,
+ attrs: dict[str, DWARFAttribute],
+ ):
+ """Initialize additional instance variables for CU encoding.
+
+ The additional instance variables are:
+ - is_64_bit: bool
+ Whether this CU is 64 bit or not.
+ - dwarf_version: int
+ default DWARFCompileUnit.default_dwarf_version
+ Version of DWARF this CU is using.
+ - addr_size: Optional[int]
+ default None
+ Size of an address in bytes.
+
+ These variables are used to configure the first parameter of the cu
+ proc (which contains calls to the compile_unit proc in the body of
+ Dwarf::assemble).
+ """
+ super().__init__(raw_die.offset, DWARFCompileUnit.compile_unit_tag, attrs)
+ self.raw_cu = raw_cu
+ self.dwarf_version: int = raw_cu.header.get(
+ "version", DWARFCompileUnit.default_dwarf_version
+ )
+ self.addr_size: Optional[int] = raw_cu.header.get("address_size")
+ self.is_64_bit: bool = raw_cu.dwarf_format() == 64
+
+ # Fission is not currently implemented because I don't know where to
+ # fetch this information from.
+ # self.is_fission: bool = self.default_is_fission
+
+ # CU labels are not currently implemented because I haven't found where
+ # pyelftools exposes this information.
+ # self.cu_label: Optional[str] = None
+
+ def format_lines(
+ self,
+ offset_die_lookup: dict[int, DWARFDIE],
+ indent_count: int = 0,
+ ) -> list[str]:
+ lines = []
+ lines.append(self._get_header(indent_count))
+ inner_lines = super().format_lines(offset_die_lookup, indent_count + 1)
+ lines += inner_lines
+ lines.append(indent(rbrace, indent_count))
+ return lines
+
+ def _get_header(self, indent_count: int = 0) -> str:
+ """Assemble the first line of the surrounding 'cu {} {}' proc call."""
+ header = indent("cu " + lbrace, indent_count)
+ cu_params = []
+
+ if self.is_64_bit != DWARFCompileUnit.default_is_64:
+ # Convert from True/False to 1/0.
+ param_value = int(self.is_64_bit)
+ cu_params += ["is_64", str(param_value)]
+
+ if self.dwarf_version != DWARFCompileUnit.default_dwarf_version:
+ cu_params += ["version", str(self.dwarf_version)]
+
+ if self.addr_size is not None:
+ cu_params += ["addr_size", str(self.addr_size)]
+
+ # Fission is not currently implemented, see comment above.
+ # if self.is_fission != DWARFCompileUnit.default_is_fission:
+ # # Same as is_64_bit conversion, True/False -> 1/0.
+ # param_value = int(self.is_fission)
+ # cu_params += ["fission", str(param_value)]
+
+ # CU labels are not currently implemented, see commend above.
+ # if self.cu_label is not None:
+ # cu_params += ["label", self.cu_label]
+
+ if cu_params:
+ header += " ".join(cu_params)
+
+ header += rbrace + " " + lbrace
+ return header
+
+
+class DWARFParser:
+ """Converter from pyelftools's DWARF representation to this script's."""
+
+ def __init__(self, elf_file: IOBase):
+ """Init parser with file opened in binary mode.
+
+ File can be closed after this function is called.
+ """
+ self.raw_data = BytesIO(elf_file.read())
+ self.elf_data = ELFFile(self.raw_data)
+ self.dwarf_info = self.elf_data.get_dwarf_info()
+ self.offset_to_die: dict[int, DWARFDIE] = {}
+ self.label_to_die: dict[str, DWARFDIE] = {}
+ self.referenced_offsets: Annotated[
+ set[int], "The set of all offsets that were referenced by some DIE."
+ ] = set()
+ self.raw_cu_list: list[RawCompileUnit] = []
+ self.top_level_dies: list[DWARFDIE] = []
+ self.subprograms: list[DWARFDIE] = []
+ self.taken_labels: set[str] = set()
+
+ self._read_all_cus()
+ self._create_necessary_labels()
+
+ def _read_all_cus(self):
+ """Populate self.raw_cu_list with all CUs in self.dwarf_info."""
+ for cu in self.dwarf_info.iter_CUs():
+ self._read_cu(cu)
+
+ def _read_cu(self, raw_cu: RawCompileUnit):
+ """Read a compile_unit into self.cu_list."""
+ self.raw_cu_list.append(raw_cu)
+ for raw_die in raw_cu.iter_DIEs():
+ if not raw_die.is_null():
+ self._parse_die(raw_cu, raw_die)
+
+ def _parse_die(self, die_cu: RawCompileUnit, raw_die: RawDIE) -> DWARFDIE:
+ """Process a single DIE and add it to offset_to_die.
+
+ Look for DW_FORM_ref4 and DWD_FORM_ref_addr form attributes and replace
+ them with the global offset of the referenced DIE, and adding the
+ referenced DIE to a set. This will be used later to assign and use
+ labels only to DIEs that need it.
+
+ In case the DIE is a top-level DIE, add it to self.top_level_dies.
+
+ In case the DIE is a subprogram, add it to self.subprograms and call
+ self._use_vars_for_low_and_high_pc_attr with it.
+ """
+ processed_attrs = {}
+ attr_value: AttributeValue
+ for attr_name, attr_value in raw_die.attributes.items():
+ actual_value = attr_value.value
+ if attr_value.form in ("DW_FORM_ref4", "DW_FORM_ref_addr"):
+ referenced_die = raw_die.get_DIE_from_attribute(attr_name)
+ actual_value = referenced_die.offset
+ self.referenced_offsets.add(referenced_die.offset)
+
+ processed_attrs[attr_name] = DWARFAttribute(
+ raw_die.offset, attr_name, actual_value, attr_value.form
+ )
+
+ if raw_die.tag == DWARFCompileUnit.compile_unit_tag:
+ processed_die = DWARFCompileUnit(raw_die, die_cu, processed_attrs)
+ else:
+ processed_die = DWARFDIE(raw_die.offset, raw_die.tag, processed_attrs, None)
+
+ if raw_die.get_parent() is None:
+ # Top level DIE
+ self.top_level_dies.append(processed_die)
+ else:
+ # Setting the parent here assumes the parent was already processed
+ # prior to this DIE being found.
+ # As far as I'm aware, this is always true in DWARF.
+ processed_parent = self.offset_to_die[raw_die.get_parent().offset]
+ processed_parent.children.append(processed_die)
+
+ if processed_die.tag == "DW_TAG_subprogram":
+ self.subprograms.append(processed_die)
+ self._use_vars_for_low_and_high_pc_attr(processed_die)
+
+ self.offset_to_die[processed_die.offset] = processed_die
+ return processed_die
+
+ def _create_necessary_labels(self):
+ """Create labels to DIEs that were referenced by others."""
+ for offset in self.referenced_offsets:
+ die = self.offset_to_die[offset]
+ self._create_label_for_die(die)
+
+ def _use_vars_for_low_and_high_pc_attr(self, subprogram: DWARFDIE) -> None:
+ """Replace existing PC attributes with Tcl variables.
+
+ If DW_AT_low_pc exists for this DIE, replace it with accessing the
+ variable whose name is given by self.subprogram_start_var(subprogram).
+
+ If DW_AT_high_pc exists for this DIE, replace it with accessing the
+ variable whose name is given by self.subprogram_end_var(subprogram).
+ """
+ low_pc_attr_name = "DW_AT_low_pc"
+ if low_pc_attr_name in subprogram.attrs:
+ start = self.subprogram_start_var(subprogram)
+ subprogram.attrs[low_pc_attr_name].value = start
+
+ high_pc_attr_name = "DW_AT_high_pc"
+ if high_pc_attr_name in subprogram.attrs:
+ end = self.subprogram_end_var(subprogram)
+ subprogram.attrs[high_pc_attr_name].value = end
+
+ def _create_label_for_die(self, die: DWARFDIE) -> None:
+ """Set tcl_label to a unique string among other DIEs for this parser.
+
+ As a first attempt, use labelify(die.name()). If the DIE does not have
+ a name, use labelify(die.type_name()).
+
+ If the chosen initial label is already taken, try again appending "_2".
+ While the attempt is still taken, try again replacing it with "_3", then
+ "_4", and so on.
+
+ This function also creates an entry on self.label_to_die.
+ """
+ if die.tcl_label is not None:
+ return
+
+ label = labelify_str(die.name() or die.type_name())
+
+ # Deduplicate label in case of collision
+ if label in self.taken_labels:
+ suffix_nr = 2
+
+ # Walrus operator to prevent writing the assembled label_suffix
+ # string literal twice. This could be rewritten by copying the
+ # string literal to the line after the end of the while loop,
+ # but I deemed it would be too frail in case one of them needs
+ # to be changed and the other is forgotten.
+ while (new_label := f"{label}_{suffix_nr}") in self.taken_labels:
+ suffix_nr += 1
+ label = new_label
+
+ die.tcl_label = label
+ self.label_to_die[label] = die
+ self.taken_labels.add(label)
+
+ def subprogram_start_var(self, subprogram: DWARFDIE) -> str:
+ """Name of the Tcl variable that holds the low PC for a subprogram."""
+ return f"${subprogram.name()}_start"
+
+ def subprogram_end_var(self, subprogram: DWARFDIE) -> str:
+ """Name of the Tcl variable that holds the high PC for a subprogram."""
+ return f"${subprogram.name()}_end"
+
+ def all_labels(self) -> set[str]:
+ """Get a copy of the set of all labels known to the parser so far."""
+ return copy(self.taken_labels)
+
+
+class DWARFAssemblerGenerator:
+ """Class that generates Dwarf::assemble code out of a DWARFParser."""
+
+ def __init__(self, dwarf_parser: DWARFParser, output=sys.stdout):
+ self.dwarf_parser = dwarf_parser
+ self.output = output
+
+ def emit(self, line: str, indent_count: int) -> None:
+ """Print a single line indented indent_count times to self.output.
+
+ If line is empty, it will always print an empty line, even with nonzero
+ indent_count.
+ """
+ if line:
+ line = get_indent_str(indent_count) + line
+ print(line, file=self.output)
+
+ def generate_die(self, die: DWARFDIE, indent_count: int):
+ """Generate the lines that represent a DIE."""
+ die_lines = die.format(self.dwarf_parser.offset_to_die, indent_count)
+ self.emit(die_lines, 0)
+
+ def generate(self):
+ indent_count = 0
+
+ self.emit("Dwarf::assemble $asm_file {", indent_count)
+
+ # Begin Dwarf::assemble body.
+ indent_count += 1
+ self.emit("global srcdir subdir srcfile", indent_count)
+
+ all_labels = self.dwarf_parser.all_labels()
+ if all_labels:
+ self.emit("declare_labels " + " ".join(all_labels), indent_count)
+
+ self.emit("", 0)
+ for subprogram in self.dwarf_parser.subprograms:
+ self.emit(f"get_func_info {subprogram.name()}", indent_count)
+
+ for die in self.dwarf_parser.top_level_dies:
+ self.generate_die(die, indent_count)
+
+ # TODO: line table, if it's within scope (it probably isn't).
+
+ # End Dwarf::assemble body.
+ indent_count -= 1
+ self.emit(rbrace, indent_count)
+
+
+def main(argv):
+ try:
+ filename = argv[1]
+ except IndexError:
+ print("Usage:", file=sys.stderr)
+ print("python ./dwarf-to-dwarf-assembler.py FILE", file=sys.stderr)
+ sys.exit(errno.EOPNOTSUPP)
+
+ try:
+ with open(filename, "rb") as elf_file:
+ parser = DWARFParser(elf_file)
+ except Exception as e:
+ print(
+ "Error parsing ELF file. Does it contain DWARF information?",
+ file=sys.stderr,
+ )
+ print(str(e), file=sys.stderr)
+ sys.exit(errno.ENODATA)
+ generator = DWARFAssemblerGenerator(parser)
+ generator.generate()
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/gdb/contrib/expect-read1.c b/gdb/contrib/expect-read1.c
index 454ecda..bb7317b 100644
--- a/gdb/contrib/expect-read1.c
+++ b/gdb/contrib/expect-read1.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013-2024 Free Software Foundation, Inc.
+/* Copyright (C) 2013-2025 Free Software Foundation, Inc.
This file is part of GDB.
diff --git a/gdb/contrib/expect-read1.sh b/gdb/contrib/expect-read1.sh
index 82c638c..e8589a2 100755
--- a/gdb/contrib/expect-read1.sh
+++ b/gdb/contrib/expect-read1.sh
@@ -1,7 +1,7 @@
#! /bin/sh
# runtest wrapper to reliably reproduce racy incomplete reads in the testsuite.
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 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
diff --git a/gdb/contrib/gdb-add-index.sh b/gdb/contrib/gdb-add-index.sh
index b299f83..d2c523f 100755
--- a/gdb/contrib/gdb-add-index.sh
+++ b/gdb/contrib/gdb-add-index.sh
@@ -2,7 +2,7 @@
# Add a .gdb_index section to a file.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 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
diff --git a/gdb/contrib/license-check-new-files.sh b/gdb/contrib/license-check-new-files.sh
new file mode 100755
index 0000000..710afa1
--- /dev/null
+++ b/gdb/contrib/license-check-new-files.sh
@@ -0,0 +1,149 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2025 Free Software Foundation, Inc.
+#
+# This file is part of GDB.
+#
+# 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/>.
+
+# This program requires the python modules GitPython (git) and scancode-toolkit.
+# It builds a list of all the newly added files to the repository and scans
+# each file for a license, printing it to the terminal. If "--skip" is used,
+# it will only output non-"common" licenses, e.g., omitting "GPL-3.0-or-later".
+# This makes it a little bit easier to detect any possible new licenses.
+#
+# Example:
+# bash$ cd /path/to/binutils-gdb/gdb
+# bash$ ./contrib/license-check-new-files.sh -s gdb-15-branchpoint gdb-16-branchpoint
+# Scanning directories gdb*/...
+# gdb/contrib/common-misspellings.txt: no longer in repo?
+# gdb/contrib/spellcheck.sh: no longer in repo?
+# gdbsupport/unordered_dense.h: MIT
+
+import os
+import sys
+import argparse
+from pathlib import PurePath
+from git import Repo
+from scancode import api
+
+# A list of "common" licenses. If "--skip" is used, any file
+# with a license in this list will be omitted from the output.
+COMMON_LICENSES = ["GPL-2.0-or-later", "GPL-3.0-or-later"]
+
+# Default list of directories to scan. Default scans are limited to
+# gdb-specific git directories because much of the rest of binutils-gdb
+# is actually owned by other projects/packages.
+DEFAULT_SCAN_DIRS = "gdb*"
+
+
+# Get the commit object associated with the string commit CSTR
+# from the git repository REPO.
+#
+# Returns the object or prints an error and exits.
+def get_commit(repo, cstr):
+ try:
+ return repo.commit(cstr)
+ except:
+ print(f'unknown commit "{cstr}"')
+ sys.exit(2)
+
+
+# Uses scancode-toolkit package to scan FILE's licenses.
+# Returns the full license dict from scancode on success or
+# propagates any exceptions.
+def get_licenses_for_file(file):
+ return api.get_licenses(file)
+
+
+# Helper function to print FILE to the terminal if skipping
+# common licenses.
+def skip_print_file(skip, file):
+ if skip:
+ print(f"{file}: ", end="")
+
+
+def main(argv):
+ parser = argparse.ArgumentParser()
+ parser.add_argument("from_commit")
+ parser.add_argument("to_commit")
+ parser.add_argument(
+ "-s", "--skip", help="skip common licenses in output", action="store_true"
+ )
+ parser.add_argument(
+ "-p",
+ "--paths",
+ help=f'paths to scan (default is "{DEFAULT_SCAN_DIRS}")',
+ type=str,
+ default=DEFAULT_SCAN_DIRS,
+ )
+ args = parser.parse_args()
+
+ # Commit boundaries to search for new files
+ from_commit = args.from_commit
+ to_commit = args.to_commit
+
+ # Get the list of new files from git. Try the current directory,
+ # looping up to the root attempting to find a valid git repository.
+ path = PurePath(os.getcwd())
+ paths = list(path.parents)
+ paths.insert(0, path)
+ for dir in paths:
+ try:
+ repo = Repo(dir)
+ break
+ except:
+ pass
+
+ if dir == path.parents[-1]:
+ print(f'not a git repository (or any parent up to mount point "{dir}")')
+ sys.exit(2)
+
+ # Get from/to commits
+ fc = get_commit(repo, from_commit)
+ tc = get_commit(repo, to_commit)
+
+ # Loop over new files
+ paths = [str(dir) for dir in args.paths.split(",")]
+ print(f'Scanning directories {",".join(f"{s}/" for s in paths)}...')
+ for file in fc.diff(tc, paths=paths).iter_change_type("A"):
+ filename = file.a_path
+ if not args.skip:
+ print(f"checking licenses for {filename}... ", end="", flush=True)
+ try:
+ f = dir.joinpath(dir, filename).as_posix()
+ lic = get_licenses_for_file(f)
+ if len(lic["license_clues"]) > 1:
+ print("multiple licenses detected")
+ elif (
+ not args.skip
+ or lic["detected_license_expression_spdx"] not in COMMON_LICENSES
+ ):
+ skip_print_file(args.skip, filename)
+ print(f"{lic['detected_license_expression_spdx']}")
+ except OSError:
+ # Likely hit a file that was added to the repo and subsequently removed.
+ skip_print_file(args.skip, filename)
+ print("no longer in repo?")
+ except KeyboardInterrupt:
+ print("interrupted")
+ break
+ except Exception as e:
+ # If scanning fails, there is little we can do but print an error.
+ skip_print_file(args.skip, filename)
+ print(e)
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/gdb/contrib/setup.cfg b/gdb/contrib/setup.cfg
new file mode 100644
index 0000000..5541a01
--- /dev/null
+++ b/gdb/contrib/setup.cfg
@@ -0,0 +1,19 @@
+[codespell]
+
+# Skip ChangeLogs and generated files.
+skip = */ChangeLog*,*/configure,gdbsupport/Makefile.in,*.dat,*.eps,gdb/features/*.c,gdb/ada-casefold.h,gdb/copying.c,gdb/gdbarch-gen.h,gdb/gdbarch-gen.c,gdb/target-delegates-gen.c
+
+ignore-words = gdb/contrib/codespell-ignore-words.txt
+dictionary = gdb/contrib/codespell-dictionary.txt,-
+
+# Ignore all URLs.
+uri-ignore-words-list = *
+
+# This codespell issue (
+# https://github.com/codespell-project/codespell/issues/3381 ) reports the
+# need to have support for ignoring blocks of code.
+# A suggestion there is to use ignore-multiline-regex, which does work, but
+# has a bug: using -w drops all newlines in the updated files (
+# https://github.com/codespell-project/codespell/issues/3642 ).
+# Consequently, disabled. To be enabled when the bug is fixed.
+#ignore-multiline-regex = codespell:ignore-begin.*codespell:ignore-end
diff --git a/gdb/contrib/spellcheck.sh b/gdb/contrib/spellcheck.sh
deleted file mode 100755
index 420891f..0000000
--- a/gdb/contrib/spellcheck.sh
+++ /dev/null
@@ -1,536 +0,0 @@
-#!/bin/bash
-
-# Copyright (C) 2024 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/>.
-
-# Script to auto-correct common spelling mistakes.
-#
-# Example usage:
-# $ ./gdb/contrib/spellcheck.sh gdb*
-
-scriptdir=$(cd "$(dirname "$0")" || exit; pwd -P)
-this_script=$scriptdir/$(basename "$0")
-
-url=https://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines
-cache_dir=$scriptdir/../../.git
-cache_file=wikipedia-common-misspellings.txt
-dictionary=$cache_dir/$cache_file
-local_dictionary=$scriptdir/common-misspellings.txt
-cache_file2=spell-check.pat1
-
-bash_version_at_least ()
-{
- local major
- major="$1"
- local minor
- minor="$2"
-
- if [ "$bash_major" = "" ]; then
- bash_major=$(echo "$BASH_VERSION" | awk -F '.' '{print $1}')
- bash_minor=$(echo "$BASH_VERSION" | awk -F '.' '{print $2}')
- fi
-
- if [ "$bash_major" -lt "$major" ]; then
- # Major version less then required, return false.
- return 1
- fi
-
- if [ "$bash_major" -gt "$major" ]; then
- # Major version more then required, return true.
- return 0
- fi
-
- # Check minor version.
- [ "$bash_minor" -ge "$minor" ]
-}
-
-# Separators: space, slash, tab, colon, comma.
-declare -a grep_separators
-grep_separators=(
- " "
- "/"
- " "
- ":"
- ","
- "\""
-)
-declare -a sed_separators
-sed_separators=(
- " "
- "/"
- "\t"
- ":"
- ","
- "\""
-)
-
-# Pre: start of line, left parenthesis.
-declare -a grep_pre
-grep_pre=(
- "^"
- "\("
-)
-declare -a sed_pre
-sed_pre=(
- "^"
- "("
-)
-
-# Post: dot, right parenthesis, end of line.
-declare -a grep_post
-grep_post=(
- "\."
- "\)"
- "$"
-)
-declare -a sed_post
-sed_post=(
- "\."
- ")"
- "$"
-)
-
-join ()
-{
- local or
- or="$1"
- shift
-
- local res
- res=""
-
- local first
- first=true
-
- for item in "$@"; do
- if $first; then
- first=false
- res="$item"
- else
- res="$res$or$item"
- fi
- done
-
- echo "$res"
-}
-
-grep_or="|"
-sed_or="\|"
-
-grep_join ()
-{
- local res
- res=$(join $grep_or "$@")
- echo "($res)"
-}
-
-sed_join ()
-{
- local res
- res=$(join $sed_or "$@")
- echo "\($res\)"
-}
-
-usage ()
-{
- echo "usage: $(basename "$0") [--check] <file|dir>+"
- echo " $(basename "$0") --print-dictionary"
-}
-
-make_absolute ()
-{
- local arg
- arg="$1"
-
- case "$arg" in
- /*)
- ;;
- *)
- arg=$(pwd -P)/"$arg"
- ;;
- esac
-
- echo "$arg"
-}
-
-parse_args ()
-{
- local files
- files=$(mktemp)
- trap 'rm -f "$files"' EXIT
-
- if [ $# -eq 1 ] && [ "$1" = "--print-dictionary" ]; then
- print_dictionary=true
- return
- fi
-
- while true; do
- case " $1 " in
- " --check ")
- check=true
- shift
- ;;
- *)
- break
- ;;
- esac
- done
-
- if [ $# -eq -0 ]; then
- usage
- exit 1
- fi
-
- local arg
- for arg in "$@"; do
- if [ -f "$arg" ]; then
- arg=$(make_absolute "$arg")
- readlink -e "$arg" \
- >> "$files"
- elif [ -d "$arg" ]; then
- arg=$(make_absolute "$arg")
- local f
- find "$arg" -type f -exec readlink -e {} \; \
- >> "$files"
- else
- echo "Not a file or directory: $arg"
- exit 1
- fi
- done
-
- mapfile -t unique_files \
- < <(sort -u "$files" \
- | grep -v ChangeLog)
-
- rm -f "$files"
- trap "" EXIT
-}
-
-get_dictionary ()
-{
- if [ -f "$dictionary" ]; then
- return
- fi
-
- local webpage
- webpage=$(mktemp)
- trap 'rm -f "$webpage"' EXIT
-
- # Download web page containing table.
- wget $url -O "$webpage"
-
- # Extract table from web page.
- awk '/<pre>/,/<\/pre>/' "$webpage" \
- | sed 's/<pre>//;s/<\/pre>//' \
- | grep -E -v "^$" \
- > "$dictionary"
-
- rm -f "$webpage"
- trap "" EXIT
-}
-
-output_local_dictionary ()
-{
- # Filter out comments and empty lines.
- grep -E -v \
- "^#|^$" \
- "$local_dictionary"
-}
-
-output_dictionaries ()
-{
- (
- output_local_dictionary
- cat "$dictionary"
- ) | grep -E -v "[A-Z]"
-}
-
-parse_dictionary ()
-{
- # Parse dictionary.
- mapfile -t words \
- < <(awk -F '->' '{print $1}' <(output_dictionaries))
- mapfile -t replacements \
- < <(awk -F '->' '{print $2}' <(output_dictionaries))
-
- local words_done
- declare -A words_done
- local i word replacement
- i=0
- for word in "${words[@]}"; do
- replacement=${replacements[i]}
-
- # Skip words that are already handled. This ensures that the local
- # dictionary overrides the wiki dictionary.
- if [ "${words_done[$word]}" == 1 ]; then
- words[i]=""
- replacements[i]=""
- i=$((i + 1))
- continue
- fi
- words_done[$word]=1
-
- # Skip identity rules.
- if [ "$word" = "$replacement" ]; then
- words[i]=""
- replacements[i]=""
- fi
-
- i=$((i + 1))
- done
-}
-
-print_dictionary ()
-{
- local i word replacement
- i=0
- for word in "${words[@]}"; do
- replacement=${replacements[i]}
- i=$((i + 1))
-
- if [ "$word" == "" ]; then
- continue
- fi
-
- echo "$word -> $replacement"
- done
-}
-
-find_files_matching_words ()
-{
- local cache_id
- cache_id=$(cat "$local_dictionary" "$dictionary" "$this_script" \
- | md5sum \
- | awk '{print $1}')
-
- local patfile
- patfile="$cache_dir/$cache_file2".$cache_id
-
- local pat
- if [ -f "$patfile" ]; then
- pat=$(cat "$patfile")
- else
- rm -f "$cache_dir/$cache_file2".*
-
- declare -a re_words
- mapfile -t re_words \
- < <(for f in "${words[@]}"; do
- if [ "$f" = "" ]; then
- continue
- fi
- echo "$f"
- done \
- | sed "s/^\(.\)/[\u\1\1]/")
-
- pat=$(grep_join "${re_words[@]}")
-
- local before after
- before=$(grep_join \
- "${grep_pre[@]}" \
- "${grep_separators[@]}")
- after=$(grep_join \
- "${grep_separators[@]}" \
- "${grep_post[@]}")
-
- pat="$before$pat$after"
-
- echo "$pat" \
- > "$patfile"
- fi
-
- grep -E \
- -l \
- "$pat" \
- "$@"
-}
-
-find_files_matching_word ()
-{
- local pat
- pat="$1"
- shift
-
- local before after
- before=$(grep_join \
- "${grep_pre[@]}" \
- "${grep_separators[@]}")
- after=$(grep_join \
- "${grep_separators[@]}" \
- "${grep_post[@]}")
-
- if bash_version_at_least 5 1; then
- patc=${pat@u}
- else
- # shellcheck disable=SC2001
- patc=$(echo "$pat" | sed 's/^\(.\)/\u\1/')
- fi
- pat="($patc|$pat)"
-
- pat="$before$pat$after"
-
- grep -E \
- -l \
- "$pat" \
- "$@"
-}
-
-replace_word_in_file ()
-{
- local word
- word="$1"
-
- local replacement
- replacement="$2"
-
- local file
- file="$3"
-
- local before after
- before=$(sed_join \
- "${sed_pre[@]}" \
- "${sed_separators[@]}")
- after=$(sed_join \
- "${sed_separators[@]}" \
- "${sed_post[@]}")
-
- if bash_version_at_least 5 1; then
- wordc=${word@u}
- replacementc=${replacement@u}
- else
- # shellcheck disable=SC2001
- wordc=$(echo "$word" | sed 's/^\(.\)/\u\1/')
- # shellcheck disable=SC2001
- replacementc=$(echo "$replacement" | sed 's/^\(.\)/\u\1/')
- fi
-
- local repl1
- local repl2
- repl1="s%$before$word$after%\1$replacement\2%g"
- repl2="s%$before$wordc$after%\1$replacementc\2%g"
-
- sed -i \
- "$repl1;$repl2" \
- "$file"
-}
-
-replace_word_in_files ()
-{
- local word
- word="$1"
-
- local replacement
- replacement="$2"
-
- shift 2
-
- local id
- id="$word -> $replacement"
-
- # Reduce set of files for sed to operate on.
- local files_matching_word
- declare -a files_matching_word
- mapfile -t files_matching_word \
- < <(find_files_matching_word "$word" "$@")
-
- if [ ${#files_matching_word[@]} -eq 0 ]; then
- return
- fi
-
- if echo "$replacement"| grep -q ","; then
- echo "TODO: $id"
- return
- fi
-
- declare -A md5sums
-
- local changed f before after
- changed=false
- for f in "${files_matching_word[@]}"; do
- if [ "${md5sums[$f]}" = "" ]; then
- md5sums[$f]=$(md5sum "$f")
- fi
-
- before="${md5sums[$f]}"
-
- replace_word_in_file \
- "$word" \
- "$replacement" \
- "$f"
-
- after=$(md5sum "$f")
-
- if [ "$after" != "$before" ]; then
- md5sums[$f]="$after"
- changed=true
- fi
- done
-
- if $changed; then
- echo "$id"
- fi
-
- find_files_matching_word "$word" "${files_matching_word[@]}" \
- | awk "{ printf \"TODO: $id: replacement failed: %s\n\", \$0}"
-}
-
-main ()
-{
- declare -a unique_files
- check=false
- print_dictionary=false
- parse_args "$@"
-
- get_dictionary
-
- declare -a words
- declare -a replacements
- parse_dictionary
-
- if $print_dictionary; then
- print_dictionary
- exit 0
- fi
-
- # Reduce set of files for sed to operate on.
- local files_matching_words
- declare -a files_matching_words
- mapfile -t files_matching_words \
- < <(find_files_matching_words "${unique_files[@]}")
-
- if [ ${#files_matching_words[@]} -eq 0 ]; then
- return
- fi
-
- if $check; then
- exit 1
- fi
-
- local i word replacement
- i=0
- for word in "${words[@]}"; do
- replacement=${replacements[i]}
- i=$((i + 1))
-
- if [ "$word" = "" ]; then
- continue
- fi
-
- replace_word_in_files \
- "$word" \
- "$replacement" \
- "${files_matching_words[@]}"
- done
-}
-
-main "$@"
diff --git a/gdb/contrib/test_pubnames_and_indexes.py b/gdb/contrib/test_pubnames_and_indexes.py
index a3f5c92..7a3dec6 100644
--- a/gdb/contrib/test_pubnames_and_indexes.py
+++ b/gdb/contrib/test_pubnames_and_indexes.py
@@ -1,6 +1,6 @@
#! /usr/bin/env python3
-# Copyright (C) 2011-2024 Free Software Foundation, Inc.
+# Copyright (C) 2011-2025 Free Software Foundation, Inc.
#
# This file is part of GDB.
#
diff --git a/gdb/contrib/words.sh b/gdb/contrib/words.sh
index 90b1bd9..a4bb2c7 100755
--- a/gdb/contrib/words.sh
+++ b/gdb/contrib/words.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2019-2024 Free Software Foundation, Inc.
+# Copyright (C) 2019-2025 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