diff options
49 files changed, 2042 insertions, 2161 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index be3ee82..88288e0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -65,6 +65,12 @@ repos: - id: isort types_or: [file] files: 'gdb/.*\.py(\.in)?$' + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + files: '^(gdbsupport|gdbserver)/' + args: [--config, gdb/contrib/setup.cfg] - repo: local hooks: - id: check-include-guards diff --git a/bfd/elf-attrs.c b/bfd/elf-attrs.c index e80575b..bc653d7 100644 --- a/bfd/elf-attrs.c +++ b/bfd/elf-attrs.c @@ -510,8 +510,8 @@ bfd_elf_parse_attr_section_v1 (bfd *abfd, bfd_byte *p, bfd_byte *p_end) if (section_len <= 4) { _bfd_error_handler - (_("%pB: error: attribute section length too small: %ld"), - abfd, (long) section_len); + (_("%pB: error: attribute section length too small: %ld"), + abfd, (long) section_len); break; } section_len -= 4; @@ -541,15 +541,15 @@ bfd_elf_parse_attr_section_v1 (bfd *abfd, bfd_byte *p, bfd_byte *p_end) orig_p = p; tag = _bfd_safe_read_leb128 (abfd, &p, false, p_end); if (p_end - p >= 4) - { - subsection_len = bfd_get_32 (abfd, p); - p += 4; - } + { + subsection_len = bfd_get_32 (abfd, p); + p += 4; + } else - { - p = p_end; - break; - } + { + p = p_end; + break; + } if (subsection_len > section_len) subsection_len = section_len; section_len -= subsection_len; @@ -638,12 +638,12 @@ _bfd_elf_parse_attributes (bfd *abfd, Elf_Internal_Shdr * hdr) /* The first character is the version of the attributes. Currently only version 'A' is recognised here. */ if (*cursor != 'A') - { - _bfd_error_handler (_("%pB: error: unknown attributes version '%c'(%d) " - "- expecting 'A'\n"), abfd, *cursor, *cursor); - bfd_set_error (bfd_error_wrong_format); - goto free_data; - } + { + _bfd_error_handler (_("%pB: error: unknown attributes version '%c'(%d) " + "- expecting 'A'\n"), abfd, *cursor, *cursor); + bfd_set_error (bfd_error_wrong_format); + goto free_data; + } ++cursor; @@ -685,9 +685,9 @@ _bfd_elf_merge_object_attributes (bfd *ibfd, struct bfd_link_info *info) { _bfd_error_handler /* xgettext:c-format */ - (_("error: %pB: object has vendor-specific contents that " - "must be processed by the '%s' toolchain"), - ibfd, in_attr->s); + (_("error: %pB: object has vendor-specific contents that " + "must be processed by the '%s' toolchain"), + ibfd, in_attr->s); return false; } diff --git a/bfd/elf32-nds32.c b/bfd/elf32-nds32.c index e240c31..1aff60a 100644 --- a/bfd/elf32-nds32.c +++ b/bfd/elf32-nds32.c @@ -2692,7 +2692,7 @@ nds32_elf_do_9_pcrel_reloc (bfd * abfd, bfd_vma symbol_value, bfd_vma addend) { - bfd_signed_vma relocation; + bfd_vma relocation; unsigned short x; bfd_reloc_status_type status; @@ -2708,7 +2708,7 @@ nds32_elf_do_9_pcrel_reloc (bfd * abfd, before doing pcrel calculations. */ relocation -= (offset & -(bfd_vma) 2); - if (relocation < -ACCURATE_8BIT_S1 || relocation >= ACCURATE_8BIT_S1) + if (relocation + ACCURATE_8BIT_S1 >= 2 * ACCURATE_8BIT_S1) status = bfd_reloc_overflow; else status = bfd_reloc_ok; diff --git a/bfd/version.h b/bfd/version.h index beca84c..baeb56a 100644 --- a/bfd/version.h +++ b/bfd/version.h @@ -16,7 +16,7 @@ In releases, the date is not included in either version strings or sonames. */ -#define BFD_VERSION_DATE 20250330 +#define BFD_VERSION_DATE 20250401 #define BFD_VERSION @bfd_version@ #define BFD_VERSION_STRING @bfd_version_package@ @bfd_version_string@ #define REPORT_BUGS_TO @report_bugs_to@ diff --git a/binutils/debug.c b/binutils/debug.c index dcc8ccd..465b18e 100644 --- a/binutils/debug.c +++ b/binutils/debug.c @@ -2554,9 +2554,6 @@ debug_write_type (struct debug_handle *info, case DEBUG_KIND_UNION_CLASS: return debug_write_class_type (info, fns, fhandle, type, tag); case DEBUG_KIND_ENUM: - if (type->u.kenum == NULL) - return (*fns->enum_type) (fhandle, tag, (const char **) NULL, - (bfd_signed_vma *) NULL); return (*fns->enum_type) (fhandle, tag, type->u.kenum->names, type->u.kenum->values); case DEBUG_KIND_POINTER: @@ -3097,9 +3094,9 @@ debug_type_samep (struct debug_handle *info, struct debug_type_s *t1, break; case DEBUG_KIND_ENUM: - if (t1->u.kenum == NULL) - ret = t2->u.kenum == NULL; - else if (t2->u.kenum == NULL) + if (t1->u.kenum->names == NULL) + ret = t2->u.kenum->names == NULL; + else if (t2->u.kenum->names == NULL) ret = false; else { diff --git a/binutils/testsuite/binutils-all/compress.exp b/binutils/testsuite/binutils-all/compress.exp index 5db9417..db167f0 100644 --- a/binutils/testsuite/binutils-all/compress.exp +++ b/binutils/testsuite/binutils-all/compress.exp @@ -163,24 +163,24 @@ if ![string match "" $got] then { } set testname "objdump compress debug sections" -set got [remote_exec host "$OBJDUMP -W ${compressedfile}.o" "" "/dev/null" "objdump.out"] +set got [remote_exec host "$OBJDUMP -W ${compressedfile}.o" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "$testname" send_log "$got\n" } -if { [regexp_diff objdump.out $srcdir/$subdir/dw2-1.W] } then { +if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/dw2-1.W] } then { fail "$testname" } else { pass "$testname" } set testname "objdump compress debug sections 3" -set got [remote_exec host "$OBJDUMP -W ${compressedfile3}.o" "" "/dev/null" "objdump.out"] +set got [remote_exec host "$OBJDUMP -W ${compressedfile3}.o" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "$testname" send_log "$got\n" } -if { [regexp_diff objdump.out $srcdir/$subdir/dw2-3.W] } then { +if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/dw2-3.W] } then { fail "$testname" } else { pass "$testname" @@ -565,12 +565,12 @@ if ![string match "" $got] then { } set testname "objdump compress debug sections 3 with zlib-gabi" -set got [remote_exec host "$OBJDUMP -W ${compressedfile3}gabi.o" "" "/dev/null" "objdump.out"] +set got [remote_exec host "$OBJDUMP -W ${compressedfile3}gabi.o" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "$testname" send_log "$got\n" } -if { [regexp_diff objdump.out $srcdir/$subdir/dw2-3gabi.W] } then { +if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/dw2-3gabi.W] } then { fail "$testname" } else { pass "$testname" @@ -639,7 +639,7 @@ proc convert_test { testname as_flags objcop_flags } { return } - set got [remote_exec host "$OBJDUMP -W ${copyfile}.o" "" "/dev/null" "objdump.out"] + set got [remote_exec host "$OBJDUMP -W ${copyfile}.o" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "$testname (reason: unexpected output)" @@ -648,7 +648,7 @@ proc convert_test { testname as_flags objcop_flags } { return } - if { [regexp_diff objdump.out $srcdir/$subdir/dw2-3.W] } then { + if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/dw2-3.W] } then { fail "$testname" } else { pass "$testname" diff --git a/binutils/testsuite/binutils-all/objdump.exp b/binutils/testsuite/binutils-all/objdump.exp index 76ee30f..f3142b1 100644 --- a/binutils/testsuite/binutils-all/objdump.exp +++ b/binutils/testsuite/binutils-all/objdump.exp @@ -454,7 +454,7 @@ if { ![is_elf_format] } then { set compressed_testfile tmpdir/dw2-compressed.${obj} } - set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -s -j .zdebug_abbrev $compressed_testfile" "" "/dev/null" "objdump.out"] + set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -s -j .zdebug_abbrev $compressed_testfile" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "objdump -s -j .zdebug_abbrev (reason: unexpected output)" @@ -462,7 +462,7 @@ if { ![is_elf_format] } then { send_log "\n" } - if { [regexp_diff objdump.out $srcdir/$subdir/objdump.s] } then { + if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/objdump.s] } then { fail "objdump -s -j .zdebug_abbrev" } else { pass "objdump -s -j .zdebug_abbrev" @@ -470,7 +470,7 @@ if { ![is_elf_format] } then { # Test objdump -W on a file that contains some compressed .debug sections - set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -W $compressed_testfile" "" "/dev/null" "objdump.out"] + set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -W $compressed_testfile" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "objdump -W (reason: unexpected output)" @@ -478,7 +478,7 @@ if { ![is_elf_format] } then { send_log "\n" } - if { [regexp_diff objdump.out $srcdir/$subdir/objdump.W] } then { + if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/objdump.W] } then { fail "objdump -W" } else { pass "objdump -W" @@ -486,7 +486,7 @@ if { ![is_elf_format] } then { # Test objdump -Z -s on a file that contains some compressed .debug sections - set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -Z -s -j .zdebug_abbrev $compressed_testfile" "" "/dev/null" "objdump.out"] + set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -Z -s -j .zdebug_abbrev $compressed_testfile" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "objdump -Z -s (reason: unexpected output)" @@ -494,7 +494,7 @@ if { ![is_elf_format] } then { send_log "\n" } - if { [regexp_diff objdump.out $srcdir/$subdir/objdump.Zs] } then { + if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/objdump.Zs] } then { fail "objdump -Z -s" } else { pass "objdump -Z -s" @@ -530,7 +530,7 @@ if { ![is_elf_format] set decodedline_testfile tmpdir/dw2-decodedline.${obj} } - set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -WL $decodedline_testfile" "" "/dev/null" "objdump.out"] + set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -WL $decodedline_testfile" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "objdump -WL (reason: unexpected output)" @@ -538,7 +538,7 @@ if { ![is_elf_format] send_log "\n" } - if { [regexp_diff objdump.out $srcdir/$subdir/objdump.WL] } then { + if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/objdump.WL] } then { fail "objdump -WL" } else { pass "objdump -WL" @@ -558,7 +558,7 @@ if { ![is_elf_format] } then { set ranges_testfile tmpdir/dw2-ranges.${obj} } - set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS --dwarf=Ranges $ranges_testfile" "" "/dev/null" "objdump.out"] + set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS --dwarf=Ranges $ranges_testfile" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "objdump -W for debug_ranges (reason: unexpected output)" @@ -567,7 +567,7 @@ if { ![is_elf_format] } then { } setup_xfail "msp430-*-*" - if { [regexp_diff objdump.out $srcdir/$subdir/dw2-ranges.W] } then { + if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/dw2-ranges.W] } then { fail "objdump -W for debug_ranges" } else { pass "objdump -W for debug_ranges" @@ -587,7 +587,7 @@ if { ![is_elf_format] } then { set op_testfile tmpdir/dw5-op.${obj} } - set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -Wi $op_testfile" "" "/dev/null" "objdump.out"] + set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -Wi $op_testfile" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "objdump -Wi (reason: unexpected output)" @@ -595,8 +595,8 @@ if { ![is_elf_format] } then { send_log "\n" } - set got [remote_exec host "tail -n +4 objdump.out" "" "/dev/null" "objdump.out"] - set output [remote_upload host objdump.out] + set got [remote_exec host "tail -n +4 tmpdir/objdump.out" "" "/dev/null" "tmpdir/objdump.tail"] + set output [remote_upload host tmpdir/objdump.tail] if ![file size $output] then { # If the output file is empty, then this target does not @@ -606,7 +606,7 @@ if { ![is_elf_format] } then { return } - if { [regexp_diff objdump.out $srcdir/$subdir/dw5-op.W] } then { + if { [regexp_diff tmpdir/objdump.tail $srcdir/$subdir/dw5-op.W] } then { fail "objdump -Wi for DW_OP_*" } else { pass "objdump -Wi for DW_OP_*" @@ -712,7 +712,7 @@ if { [is_elf_format] } then { set testfile tmpdir/debuglink.${obj} } - set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -Wk -WN $testfile" "" "/dev/null" "objdump.out"] + set got [remote_exec host "$OBJDUMP $OBJDUMPFLAGS -Wk -WN $testfile" "" "/dev/null" "tmpdir/objdump.out"] if { [lindex $got 0] != 0 || ![string match "" [lindex $got 1]] } then { fail "objdump -Wk (reason: unexpected output)" @@ -720,7 +720,7 @@ if { [is_elf_format] } then { send_log "\n" } - if { [regexp_diff objdump.out $srcdir/$subdir/objdump.Wk] } then { + if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/objdump.Wk] } then { fail "objdump -Wk (reason: output does not match expectations)" } else { pass "objdump -Wk" @@ -764,7 +764,7 @@ proc test_follow_debuglink { options dumpfile } { if { [regexp_diff tmpdir/objdump.out $srcdir/$subdir/$dumpfile] } then { fail $test - verbose "output is \n[file_contents objdump.out]" 2 + verbose "output is \n[file_contents tmpdir/objdump.out]" 2 return } diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 6203bcf..0c4102d 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1097,7 +1097,9 @@ COMMON_SFILES = \ dwarf2/attribute.c \ dwarf2/comp-unit-head.c \ dwarf2/cooked-index.c \ - dwarf2/cooked-index-storage.c \ + dwarf2/cooked-index-entry.c \ + dwarf2/cooked-index-shard.c \ + dwarf2/cooked-index-worker.c \ dwarf2/cooked-indexer.c \ dwarf2/cu.c \ dwarf2/die.c \ @@ -1356,7 +1358,9 @@ HFILES_NO_SRCDIR = \ dummy-frame.h \ dwarf2/aranges.h \ dwarf2/cooked-index.h \ - dwarf2/cooked-index-storage.h \ + dwarf2/cooked-index-entry.h \ + dwarf2/cooked-index-shard.h \ + dwarf2/cooked-index-worker.h \ dwarf2/cooked-indexer.h \ dwarf2/cu.h \ dwarf2/frame-tailcall.h \ diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 0b4ae7f..c825699 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -2275,7 +2275,7 @@ aarch64_canonicalize_syscall (enum aarch64_syscall syscall_number) SYSCALL_MAP (clone); SYSCALL_MAP (execve); - SYSCALL_MAP_RENAME (mmap, gdb_sys_mmap2); + SYSCALL_MAP_RENAME (mmap, gdb_sys_old_mmap); SYSCALL_MAP (fadvise64); SYSCALL_MAP (swapon); diff --git a/gdb/addrmap.c b/gdb/addrmap.c index 9eb330b..1fc95f3 100644 --- a/gdb/addrmap.c +++ b/gdb/addrmap.c @@ -178,6 +178,29 @@ addrmap_mutable::force_transition (CORE_ADDR addr) } +/* Compare keys as CORE_ADDR * values. */ +static int +splay_compare_CORE_ADDR_ptr (splay_tree_key ak, splay_tree_key bk) +{ + CORE_ADDR a = * (CORE_ADDR *) ak; + CORE_ADDR b = * (CORE_ADDR *) bk; + + /* We can't just return a-b here, because of over/underflow. */ + if (a < b) + return -1; + else if (a == b) + return 0; + else + return 1; +} + + +static void +xfree_wrapper (splay_tree_key key) +{ + xfree ((void *) key); +} + void addrmap_mutable::set_empty (CORE_ADDR start, CORE_ADDR end_inclusive, void *obj) @@ -185,6 +208,10 @@ addrmap_mutable::set_empty (CORE_ADDR start, CORE_ADDR end_inclusive, splay_tree_node n, next; void *prior_value; + if (tree == nullptr) + tree = splay_tree_new (splay_compare_CORE_ADDR_ptr, xfree_wrapper, + nullptr /* no delete value */); + /* If we're being asked to set all empty portions of the given address range to empty, then probably the caller is confused. (If that turns out to be useful in some cases, then we can change @@ -233,6 +260,9 @@ addrmap_mutable::set_empty (CORE_ADDR start, CORE_ADDR end_inclusive, void * addrmap_mutable::do_find (CORE_ADDR addr) const { + if (tree == nullptr) + return nullptr; + splay_tree_node n = splay_tree_lookup (addr); if (n != nullptr) { @@ -287,16 +317,6 @@ addrmap_fixed::addrmap_fixed (struct obstack *obstack, gdb_assert (num_transitions == transition_count); } - -void -addrmap_mutable::relocate (CORE_ADDR offset) -{ - /* Not needed yet. */ - internal_error (_("addrmap_relocate is not implemented yet " - "for mutable addrmaps")); -} - - /* This is a splay_tree_foreach_fn. */ static int @@ -311,43 +331,20 @@ addrmap_mutable_foreach_worker (splay_tree_node node, void *data) int addrmap_mutable::do_foreach (addrmap_foreach_fn fn) const { - return splay_tree_foreach (tree, addrmap_mutable_foreach_worker, &fn); -} - - -/* Compare keys as CORE_ADDR * values. */ -static int -splay_compare_CORE_ADDR_ptr (splay_tree_key ak, splay_tree_key bk) -{ - CORE_ADDR a = * (CORE_ADDR *) ak; - CORE_ADDR b = * (CORE_ADDR *) bk; - - /* We can't just return a-b here, because of over/underflow. */ - if (a < b) - return -1; - else if (a == b) + if (tree == nullptr) return 0; - else - return 1; -} - - -static void -xfree_wrapper (splay_tree_key key) -{ - xfree ((void *) key); + return splay_tree_foreach (tree, addrmap_mutable_foreach_worker, &fn); } -addrmap_mutable::addrmap_mutable () - : tree (splay_tree_new (splay_compare_CORE_ADDR_ptr, xfree_wrapper, - nullptr /* no delete value */)) -{ -} -addrmap_mutable::~addrmap_mutable () +void +addrmap_mutable::clear () { if (tree != nullptr) - splay_tree_delete (tree); + { + splay_tree_delete (tree); + tree = nullptr; + } } @@ -443,7 +440,7 @@ test_addrmap () CHECK_ADDRMAP_FIND (map, array, 13, 19, nullptr); /* Create corresponding fixed addrmap. */ - struct addrmap *map2 + addrmap_fixed *map2 = new (&temp_obstack) addrmap_fixed (&temp_obstack, map.get ()); SELF_CHECK (map2 != nullptr); CHECK_ADDRMAP_FIND (map2, array, 0, 9, nullptr); diff --git a/gdb/addrmap.h b/gdb/addrmap.h index 95f6ec8..a2feb68 100644 --- a/gdb/addrmap.h +++ b/gdb/addrmap.h @@ -52,10 +52,6 @@ struct addrmap void *find (CORE_ADDR addr) { return this->do_find (addr); } - /* Relocate all the addresses in MAP by OFFSET. (This can be applied - to either mutable or immutable maps.) */ - virtual void relocate (CORE_ADDR offset) = 0; - /* Call FN for every address in MAP, following an in-order traversal. If FN ever returns a non-zero value, the iteration ceases immediately, and the value is returned. Otherwise, this function @@ -94,7 +90,8 @@ public: addrmap_fixed (addrmap_fixed &&other) = default; addrmap_fixed &operator= (addrmap_fixed &&) = default; - void relocate (CORE_ADDR offset) override; + /* Relocate all the addresses in this map by OFFSET. */ + void relocate (CORE_ADDR offset); private: void *do_find (CORE_ADDR addr) const override; @@ -126,8 +123,12 @@ struct addrmap_mutable final : public addrmap { public: - addrmap_mutable (); - ~addrmap_mutable (); + addrmap_mutable () = default; + ~addrmap_mutable () + { + clear (); + } + DISABLE_COPY_AND_ASSIGN (addrmap_mutable); addrmap_mutable (addrmap_mutable &&other) @@ -138,7 +139,13 @@ public: addrmap_mutable &operator= (addrmap_mutable &&other) { - std::swap (tree, other.tree); + /* Handle self-move. */ + if (this != &other) + { + clear (); + tree = other.tree; + other.tree = nullptr; + } return *this; } @@ -181,7 +188,9 @@ public: representation. */ void set_empty (CORE_ADDR start, CORE_ADDR end_inclusive, void *obj); - void relocate (CORE_ADDR offset) override; + + /* Clear this addrmap. */ + void clear (); private: void *do_find (CORE_ADDR addr) const override; @@ -204,7 +213,7 @@ private: function, we can't keep a freelist for keys. Since mutable addrmaps are only used temporarily right now, we just leak keys from deleted nodes; they'll be freed when the obstack is freed. */ - splay_tree tree; + splay_tree tree = nullptr; /* Various helper methods. */ splay_tree_key allocate_key (CORE_ADDR addr); diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index e290d2c..a7868c3 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -412,7 +412,7 @@ amd64_canonicalize_syscall (enum amd64_syscall syscall_number) case amd64_sys_mmap: case amd64_x32_sys_mmap: - return gdb_sys_mmap2; + return gdb_sys_old_mmap; case amd64_sys_mprotect: case amd64_x32_sys_mprotect: diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index 858705e..53c8a05 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -1361,8 +1361,8 @@ arm_canonicalize_syscall (int syscall) case 86: return gdb_sys_uselib; case 87: return gdb_sys_swapon; case 88: return gdb_sys_reboot; - case 89: return gdb_old_readdir; - case 90: return gdb_old_mmap; + case 89: return gdb_sys_old_readdir; + case 90: return gdb_sys_old_mmap; case 91: return gdb_sys_munmap; case 92: return gdb_sys_truncate; case 93: return gdb_sys_ftruncate; diff --git a/gdb/contrib/common-misspellings.txt b/gdb/contrib/common-misspellings.txt deleted file mode 100644 index 5772f66..0000000 --- a/gdb/contrib/common-misspellings.txt +++ /dev/null @@ -1,30 +0,0 @@ -# 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/>. - -# This file contains additions to and overrides for -# wikipedia-common-misspellings.txt. - -# Common spelling mistakes. - -inbetween->between, in between, in-between -sofar->so far -doens't->doesn't -behavour->behavior -behaviour->behavior -arrithemetic->arithmetic -electricaly->electrically - -# Identity rules. - -thru->thru diff --git a/gdb/contrib/setup.cfg b/gdb/contrib/setup.cfg new file mode 100644 index 0000000..dbff165 --- /dev/null +++ b/gdb/contrib/setup.cfg @@ -0,0 +1,6 @@ +[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 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/dwarf2/abbrev-table-cache.h b/gdb/dwarf2/abbrev-table-cache.h index 8469948..d99fb8d 100644 --- a/gdb/dwarf2/abbrev-table-cache.h +++ b/gdb/dwarf2/abbrev-table-cache.h @@ -30,6 +30,9 @@ public: abbrev_table_cache () = default; DISABLE_COPY_AND_ASSIGN (abbrev_table_cache); + abbrev_table_cache (abbrev_table_cache &&) = default; + abbrev_table_cache &operator= (abbrev_table_cache &&) = default; + /* Find an abbrev table coming from the abbrev section SECTION at offset OFFSET. Return the table, or nullptr if it has not yet been registered. */ diff --git a/gdb/dwarf2/cooked-index-entry.c b/gdb/dwarf2/cooked-index-entry.c new file mode 100644 index 0000000..3e322f1 --- /dev/null +++ b/gdb/dwarf2/cooked-index-entry.c @@ -0,0 +1,242 @@ +/* Entry in the cooked index + + Copyright (C) 2022-2024 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/>. */ + +#include "dwarf2/cooked-index-entry.h" +#include "dwarf2/tag.h" +#include "gdbsupport/gdb-safe-ctype.h" +#include "gdbsupport/selftest.h" + +/* See cooked-index-entry.h. */ + +std::string +to_string (cooked_index_flag flags) +{ + static constexpr cooked_index_flag::string_mapping mapping[] = { + MAP_ENUM_FLAG (IS_MAIN), + MAP_ENUM_FLAG (IS_STATIC), + MAP_ENUM_FLAG (IS_LINKAGE), + MAP_ENUM_FLAG (IS_TYPE_DECLARATION), + MAP_ENUM_FLAG (IS_PARENT_DEFERRED), + }; + + return flags.to_string (mapping); +} + +/* See cooked-index-entry.h. */ + +int +cooked_index_entry::compare (const char *stra, const char *strb, + comparison_mode mode) +{ +#if defined (__GNUC__) && !defined (__clang__) && __GNUC__ <= 7 + /* Work around error with gcc 7.5.0. */ + auto munge = [] (char c) -> unsigned char +#else + auto munge = [] (char c) constexpr -> unsigned char +#endif + { + /* Treat '<' as if it ended the string. This lets something + like "func<t>" match "func<t<int>>". See the "Breakpoints in + template functions" section in the manual. */ + if (c == '<') + return '\0'; + return TOLOWER ((unsigned char) c); + }; + + unsigned char a = munge (*stra); + unsigned char b = munge (*strb); + + while (a != '\0' && b != '\0' && a == b) + { + a = munge (*++stra); + b = munge (*++strb); + } + + if (a == b) + return 0; + + /* When completing, if STRB ends earlier than STRA, consider them as + equal. */ + if (mode == COMPLETE && b == '\0') + return 0; + + return a < b ? -1 : 1; +} + +#if GDB_SELF_TEST + +namespace { + +void +test_compare () +{ + /* Convenience aliases. */ + const auto mode_compare = cooked_index_entry::MATCH; + const auto mode_sort = cooked_index_entry::SORT; + const auto mode_complete = cooked_index_entry::COMPLETE; + + SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", + mode_compare) < 0); + SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", + mode_compare) > 0); + SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", + mode_complete) < 0); + SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("name", "name<>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<>", "name", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name", "name<>", + mode_complete) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<>", "name", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", + mode_complete) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", + "name<arg<more>>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg>", + "name<arg<more>>", + mode_compare) == 0); + + SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", + mode_compare) == 0); + SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", + mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_compare) < 0); + SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_complete) < 0); + SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_compare) > 0); + SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0); + + SELF_CHECK (cooked_index_entry::compare ("func", "func<type>", + mode_sort) == 0); + SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1", + mode_sort) < 0); +} + +} /* anonymous namespace */ + +#endif /* GDB_SELF_TEST */ + +/* See cooked-index-entry.h. */ + +bool +cooked_index_entry::matches (domain_search_flags kind) const +{ + /* Just reject type declarations. */ + if ((flags & IS_TYPE_DECLARATION) != 0) + return false; + + return tag_matches_domain (tag, kind, lang); +} + +/* See cooked-index-entry.h. */ + +const char * +cooked_index_entry::full_name (struct obstack *storage, + cooked_index_full_name_flag name_flags, + const char *default_sep) const +{ + const char *local_name = ((name_flags & FOR_MAIN) != 0) ? name : canonical; + + if ((flags & IS_LINKAGE) != 0 || get_parent () == nullptr) + return local_name; + + const char *sep = default_sep; + switch (lang) + { + case language_cplus: + case language_rust: + case language_fortran: + sep = "::"; + break; + + case language_ada: + if ((name_flags & FOR_ADA_LINKAGE_NAME) != 0) + { + sep = "__"; + break; + } + [[fallthrough]]; + case language_go: + case language_d: + sep = "."; + break; + + default: + if (sep == nullptr) + return local_name; + break; + } + + /* The FOR_ADA_LINKAGE_NAME flag should only affect Ada entries, so + disable it here if we don't need it. */ + if (lang != language_ada) + name_flags &= ~FOR_ADA_LINKAGE_NAME; + + get_parent ()->write_scope (storage, sep, name_flags); + obstack_grow0 (storage, local_name, strlen (local_name)); + return (const char *) obstack_finish (storage); +} + +/* See cooked-index-entry.h. */ + +void +cooked_index_entry::write_scope (struct obstack *storage, + const char *sep, + cooked_index_full_name_flag flags) const +{ + if (get_parent () != nullptr) + get_parent ()->write_scope (storage, sep, flags); + /* When computing the Ada linkage name, the entry might not have + been canonicalized yet, so use the 'name'. */ + const char *local_name = ((flags & (FOR_MAIN | FOR_ADA_LINKAGE_NAME)) != 0 + ? name + : canonical); + obstack_grow (storage, local_name, strlen (local_name)); + obstack_grow (storage, sep, strlen (sep)); +} + +void _initialize_dwarf2_entry (); +void _initialize_dwarf2_entry () +{ +#if GDB_SELF_TEST + selftests::register_test ("cooked_index_entry::compare", test_compare); +#endif +} diff --git a/gdb/dwarf2/cooked-index-entry.h b/gdb/dwarf2/cooked-index-entry.h new file mode 100644 index 0000000..bb47e32 --- /dev/null +++ b/gdb/dwarf2/cooked-index-entry.h @@ -0,0 +1,258 @@ +/* Entry in the cooked index + + Copyright (C) 2022-2024 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/>. */ + +#ifndef GDB_DWARF2_COOKED_INDEX_ENTRY_H +#define GDB_DWARF2_COOKED_INDEX_ENTRY_H + +#include "dwarf2/parent-map.h" +#include "dwarf2/types.h" +#include "symtab.h" +#include "gdbsupport/gdb_obstack.h" +#include "quick-symbol.h" + +/* Flags that describe an entry in the index. */ +enum cooked_index_flag_enum : unsigned char +{ + /* True if this entry is the program's "main". */ + IS_MAIN = 1, + /* True if this entry represents a "static" object. */ + IS_STATIC = 2, + /* True if this entry uses the linkage name. */ + IS_LINKAGE = 4, + /* True if this entry is just for the declaration of a type, not the + definition. */ + IS_TYPE_DECLARATION = 8, + /* True is parent_entry.deferred has a value rather than parent_entry + .resolved. */ + IS_PARENT_DEFERRED = 16, + /* True if this entry was synthesized by gdb (as opposed to coming + directly from the DWARF). */ + IS_SYNTHESIZED = 32, +}; +DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag); + +/* Flags used when requesting the full name of an entry. */ +enum cooked_index_full_name_enum : unsigned char +{ + /* Set when requesting the name of "main". See the method for the + full description. */ + FOR_MAIN = 1, + /* Set when requesting the linkage name for an Ada entry. */ + FOR_ADA_LINKAGE_NAME = 2, +}; +DEF_ENUM_FLAGS_TYPE (enum cooked_index_full_name_enum, cooked_index_full_name_flag); + +/* Type representing either a resolved or deferred cooked_index_entry. */ + +union cooked_index_entry_ref +{ + cooked_index_entry_ref (parent_map::addr_type deferred_) + { + deferred = deferred_; + } + + cooked_index_entry_ref (const cooked_index_entry *resolved_) + { + resolved = resolved_; + } + + const cooked_index_entry *resolved; + parent_map::addr_type deferred; +}; + +/* Return a string representation of FLAGS. */ + +std::string to_string (cooked_index_flag flags); + +/* A cooked_index_entry represents a single item in the index. Note + that two entries can be created for the same DIE -- one using the + name, and another one using the linkage name, if any. + + This is an "open" class and the members are all directly + accessible. It is read-only after the index has been fully read + and processed. */ +struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry> +{ + cooked_index_entry (sect_offset die_offset_, enum dwarf_tag tag_, + cooked_index_flag flags_, + enum language lang_, const char *name_, + cooked_index_entry_ref parent_entry_, + dwarf2_per_cu *per_cu_) + : name (name_), + tag (tag_), + flags (flags_), + lang (lang_), + die_offset (die_offset_), + per_cu (per_cu_), + m_parent_entry (parent_entry_) + { + } + + /* Return true if this entry matches SEARCH_FLAGS. */ + bool matches (block_search_flags search_flags) const + { + /* Just reject type declarations. */ + if ((flags & IS_TYPE_DECLARATION) != 0) + return false; + + if ((search_flags & SEARCH_STATIC_BLOCK) != 0 + && (flags & IS_STATIC) != 0) + return true; + if ((search_flags & SEARCH_GLOBAL_BLOCK) != 0 + && (flags & IS_STATIC) == 0) + return true; + return false; + } + + /* Return true if this entry matches KIND. */ + bool matches (domain_search_flags kind) const; + + /* Construct the fully-qualified name of this entry and return a + pointer to it. If allocation is needed, it will be done on + STORAGE. + + FLAGS affects the result. If the FOR_MAIN flag is set, we are + computing the name of the "main" entry -- one marked + DW_AT_main_subprogram. This matters for avoiding name + canonicalization and also a related race (if "main" computation + is done during finalization). + + If the FOR_ADA_LINKAGE_NAME flag is set, then Ada-language + symbols will have their "linkage-style" name computed. The + default is source-style. + + If the language doesn't prescribe a separator, one can be + specified using DEFAULT_SEP. */ + const char *full_name (struct obstack *storage, + cooked_index_full_name_flag name_flags = 0, + const char *default_sep = nullptr) const; + + /* Comparison modes for the 'compare' function. See the function + for a description. */ + enum comparison_mode + { + MATCH, + SORT, + COMPLETE, + }; + + /* Compare two strings, case-insensitively. Return -1 if STRA is + less than STRB, 0 if they are equal, and 1 if STRA is greater. + + When comparing, '<' is considered to be less than all other + printable characters. This ensures that "t<x>" sorts before + "t1", which is necessary when looking up "t". This '<' handling + is to ensure that certain C++ lookups work correctly. It is + inexact, and applied regardless of the search language, but this + is ok because callers of this code do more precise filtering + according to their needs. This is also why using a + case-insensitive comparison works even for languages that are + case sensitive. + + MODE controls how the comparison proceeds. + + MODE==SORT is used when sorting and the only special '<' handling + that it does is to ensure that '<' sorts before all other + printable characters. This ensures that the resulting ordering + will be binary-searchable. + + MODE==MATCH is used when searching for a symbol. In this case, + STRB must always be the search name, and STRA must be the name in + the index that is under consideration. In compare mode, early + termination of STRB may match STRA -- for example, "t<int>" and + "t" will be considered to be equal. (However, if A=="t" and + B=="t<int>", then this will not consider them as equal.) + + MODE==COMPLETE is used when searching for a symbol for + completion. In this case, STRB must always be the search name, + and STRA must be the name in the index that is under + consideration. In completion mode, early termination of STRB + always results in a match. */ + static int compare (const char *stra, const char *strb, + comparison_mode mode); + + /* Compare two entries by canonical name. */ + bool operator< (const cooked_index_entry &other) const + { + return compare (canonical, other.canonical, SORT) < 0; + } + + /* Set parent entry to PARENT. */ + void set_parent (const cooked_index_entry *parent) + { + gdb_assert ((flags & IS_PARENT_DEFERRED) == 0); + m_parent_entry.resolved = parent; + } + + /* Resolve deferred parent entry to PARENT. */ + void resolve_parent (const cooked_index_entry *parent) + { + gdb_assert ((flags & IS_PARENT_DEFERRED) != 0); + flags = flags & ~IS_PARENT_DEFERRED; + m_parent_entry.resolved = parent; + } + + /* Return parent entry. */ + const cooked_index_entry *get_parent () const + { + gdb_assert ((flags & IS_PARENT_DEFERRED) == 0); + return m_parent_entry.resolved; + } + + /* Return deferred parent entry. */ + parent_map::addr_type get_deferred_parent () const + { + gdb_assert ((flags & IS_PARENT_DEFERRED) != 0); + return m_parent_entry.deferred; + } + + /* The name as it appears in DWARF. This always points into one of + the mapped DWARF sections. Note that this may be the name or the + linkage name -- two entries are created for DIEs which have both + attributes. */ + const char *name; + /* The canonical name. This may be equal to NAME. */ + const char *canonical = nullptr; + /* The DWARF tag. */ + enum dwarf_tag tag; + /* Any flags attached to this entry. */ + cooked_index_flag flags; + /* The language of this symbol. */ + ENUM_BITFIELD (language) lang : LANGUAGE_BITS; + /* The offset of this DIE. */ + sect_offset die_offset; + /* The CU from which this entry originates. */ + dwarf2_per_cu *per_cu; + +private: + + /* A helper method for full_name. Emits the full scope of this + object, followed by the separator, to STORAGE. If this entry has + a parent, its write_scope method is called first. See full_name + for a description of the FLAGS parameter. */ + void write_scope (struct obstack *storage, const char *sep, + cooked_index_full_name_flag flags) const; + + /* The parent entry. This is NULL for top-level entries. + Otherwise, it points to the parent entry, such as a namespace or + class. */ + cooked_index_entry_ref m_parent_entry; +}; + +#endif /* GDB_DWARF2_COOKED_INDEX_ENTRY_H */ diff --git a/gdb/dwarf2/cooked-index-shard.c b/gdb/dwarf2/cooked-index-shard.c new file mode 100644 index 0000000..683feb2 --- /dev/null +++ b/gdb/dwarf2/cooked-index-shard.c @@ -0,0 +1,331 @@ +/* Shards for the cooked index + + Copyright (C) 2022-2024 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/>. */ + +#include "dwarf2/cooked-index-shard.h" +#include "dwarf2/tag.h" +#include "dwarf2/index-common.h" +#include "cp-support.h" +#include "c-lang.h" +#include "ada-lang.h" + +/* Return true if a plain "main" could be the main program for this + language. Languages that are known to use some other mechanism are + excluded here. */ + +static bool +language_may_use_plain_main (enum language lang) +{ + /* No need to handle "unknown" here. */ + return (lang == language_c + || lang == language_objc + || lang == language_cplus + || lang == language_m2 + || lang == language_asm + || lang == language_opencl + || lang == language_minimal); +} + +/* See cooked-index-shard.h. */ + +cooked_index_entry * +cooked_index_shard::create (sect_offset die_offset, + enum dwarf_tag tag, + cooked_index_flag flags, + enum language lang, + const char *name, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu) +{ + if (tag == DW_TAG_module || tag == DW_TAG_namespace) + flags &= ~IS_STATIC; + else if (lang == language_cplus + && (tag == DW_TAG_class_type + || tag == DW_TAG_interface_type + || tag == DW_TAG_structure_type + || tag == DW_TAG_union_type + || tag == DW_TAG_enumeration_type + || tag == DW_TAG_enumerator)) + flags &= ~IS_STATIC; + else if (tag_is_type (tag)) + flags |= IS_STATIC; + + return new (&m_storage) cooked_index_entry (die_offset, tag, flags, + lang, name, parent_entry, + per_cu); +} + +/* See cooked-index-shard.h. */ + +cooked_index_entry * +cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag, + cooked_index_flag flags, enum language lang, + const char *name, cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu) +{ + cooked_index_entry *result = create (die_offset, tag, flags, lang, name, + parent_entry, per_cu); + m_entries.push_back (result); + + /* An explicitly-tagged main program should always override the + implicit "main" discovery. */ + if ((flags & IS_MAIN) != 0) + m_main = result; + else if ((flags & IS_PARENT_DEFERRED) == 0 + && parent_entry.resolved == nullptr + && m_main == nullptr + && language_may_use_plain_main (lang) + && strcmp (name, "main") == 0) + m_main = result; + + return result; +} + +/* See cooked-index-shard.h. */ + +void +cooked_index_shard::handle_gnat_encoded_entry + (cooked_index_entry *entry, + htab_t gnat_entries, + std::vector<cooked_index_entry *> &new_entries) +{ + /* We decode Ada names in a particular way: operators and wide + characters are left as-is. This is done to make name matching a + bit simpler; and for wide characters, it means the choice of Ada + source charset does not affect the indexer directly. */ + std::string canonical = ada_decode (entry->name, false, false, false); + if (canonical.empty ()) + { + entry->canonical = entry->name; + return; + } + std::vector<std::string_view> names = split_name (canonical.c_str (), + split_style::DOT_STYLE); + std::string_view tail = names.back (); + names.pop_back (); + + const cooked_index_entry *parent = nullptr; + for (const auto &name : names) + { + uint32_t hashval = dwarf5_djb_hash (name); + void **slot = htab_find_slot_with_hash (gnat_entries, &name, + hashval, INSERT); + /* CUs are processed in order, so we only need to check the most + recent entry. */ + cooked_index_entry *last = (cooked_index_entry *) *slot; + if (last == nullptr || last->per_cu != entry->per_cu) + { + const char *new_name = m_names.insert (name); + last = create (entry->die_offset, DW_TAG_module, + IS_SYNTHESIZED, language_ada, new_name, parent, + entry->per_cu); + last->canonical = last->name; + new_entries.push_back (last); + *slot = last; + } + + parent = last; + } + + entry->set_parent (parent); + entry->canonical = m_names.insert (tail); +} + +/* Hash a cooked index entry by name pointer value. + + We can use pointer equality here because names come from .debug_str, which + will normally be unique-ified by the linker. Also, duplicates are relatively + harmless -- they just mean a bit of extra memory is used. */ + +struct cooked_index_entry_name_ptr_hash +{ + using is_avalanching = void; + + std::uint64_t operator () (const cooked_index_entry *entry) const noexcept + { + return ankerl::unordered_dense::hash<const char *> () (entry->name); + } +}; + +/* Compare cooked index entries by name pointer value. */ + +struct cooked_index_entry_name_ptr_eq +{ + bool operator () (const cooked_index_entry *a, + const cooked_index_entry *b) const noexcept + { + return a->name == b->name; + } +}; + +/* See cooked-index-shard.h. */ + +void +cooked_index_shard::finalize (const parent_map_map *parent_maps) +{ + gdb::unordered_set<const cooked_index_entry *, + cooked_index_entry_name_ptr_hash, + cooked_index_entry_name_ptr_eq> seen_names; + + auto hash_entry = [] (const void *e) + { + const cooked_index_entry *entry = (const cooked_index_entry *) e; + return dwarf5_djb_hash (entry->canonical); + }; + + auto eq_entry = [] (const void *a, const void *b) -> int + { + const cooked_index_entry *ae = (const cooked_index_entry *) a; + const std::string_view *sv = (const std::string_view *) b; + return (strlen (ae->canonical) == sv->length () + && strncasecmp (ae->canonical, sv->data (), sv->length ()) == 0); + }; + + htab_up gnat_entries (htab_create_alloc (10, hash_entry, eq_entry, + nullptr, xcalloc, xfree)); + std::vector<cooked_index_entry *> new_gnat_entries; + + for (cooked_index_entry *entry : m_entries) + { + if ((entry->flags & IS_PARENT_DEFERRED) != 0) + { + const cooked_index_entry *new_parent + = parent_maps->find (entry->get_deferred_parent ()); + entry->resolve_parent (new_parent); + } + + /* Note that this code must be kept in sync with + language_requires_canonicalization. */ + gdb_assert (entry->canonical == nullptr); + if ((entry->flags & IS_LINKAGE) != 0) + entry->canonical = entry->name; + else if (entry->lang == language_ada) + { + /* Newer versions of GNAT emit DW_TAG_module and use a + hierarchical structure. In this case, we don't need to + do any extra work. This can be detected by looking for a + GNAT-encoded name. */ + if (strstr (entry->name, "__") == nullptr) + { + entry->canonical = entry->name; + + /* If the entry does not have a parent, then there's + nothing extra to do here -- the entry itself is + sufficient. + + However, if it does have a parent, we have to + synthesize an entry with the full name. This is + unfortunate, but it's necessary due to how some of + the Ada name-lookup code currently works. For + example, without this, ada_get_tsd_type will + fail. + + Eventually it would be good to change the Ada lookup + code, and then remove these entries (and supporting + code in cooked_index_entry::full_name). */ + if (entry->get_parent () != nullptr) + { + const char *fullname + = entry->full_name (&m_storage, FOR_ADA_LINKAGE_NAME); + cooked_index_entry *linkage = create (entry->die_offset, + entry->tag, + (entry->flags + | IS_LINKAGE + | IS_SYNTHESIZED), + language_ada, + fullname, + nullptr, + entry->per_cu); + linkage->canonical = fullname; + new_gnat_entries.push_back (linkage); + } + } + else + handle_gnat_encoded_entry (entry, gnat_entries.get (), + new_gnat_entries); + } + else if (entry->lang == language_cplus || entry->lang == language_c) + { + auto [it, inserted] = seen_names.insert (entry); + + if (inserted) + { + /* No entry with that name was present, compute the canonical + name. */ + gdb::unique_xmalloc_ptr<char> canon_name + = (entry->lang == language_cplus + ? cp_canonicalize_string (entry->name) + : c_canonicalize_name (entry->name)); + if (canon_name == nullptr) + entry->canonical = entry->name; + else + entry->canonical = m_names.insert (std::move (canon_name)); + } + else + { + /* An entry with that name was present, re-use its canonical + name. */ + entry->canonical = (*it)->canonical; + } + } + else + entry->canonical = entry->name; + } + + /* Make sure any new Ada entries end up in the results. This isn't + done when creating these new entries to avoid invalidating the + m_entries iterator used in the foreach above. */ + m_entries.insert (m_entries.end (), new_gnat_entries.begin (), + new_gnat_entries.end ()); + + m_entries.shrink_to_fit (); + std::sort (m_entries.begin (), m_entries.end (), + [] (const cooked_index_entry *a, const cooked_index_entry *b) + { + return *a < *b; + }); +} + +/* See cooked-index-shard.h. */ + +cooked_index_shard::range +cooked_index_shard::find (const std::string &name, bool completing) const +{ + struct comparator + { + cooked_index_entry::comparison_mode mode; + + bool operator() (const cooked_index_entry *entry, + const char *name) const noexcept + { + return cooked_index_entry::compare (entry->canonical, name, mode) < 0; + } + + bool operator() (const char *name, + const cooked_index_entry *entry) const noexcept + { + return cooked_index_entry::compare (entry->canonical, name, mode) > 0; + } + }; + + return std::make_from_tuple<range> + (std::equal_range (m_entries.cbegin (), m_entries.cend (), name.c_str (), + comparator { (completing + ? cooked_index_entry::COMPLETE + : cooked_index_entry::MATCH) })); +} diff --git a/gdb/dwarf2/cooked-index-shard.h b/gdb/dwarf2/cooked-index-shard.h new file mode 100644 index 0000000..eb80926 --- /dev/null +++ b/gdb/dwarf2/cooked-index-shard.h @@ -0,0 +1,134 @@ +/* Shards for the cooked index + + Copyright (C) 2022-2024 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/>. */ + +#ifndef GDB_DWARF2_COOKED_INDEX_SHARD_H +#define GDB_DWARF2_COOKED_INDEX_SHARD_H + +#include "dwarf2/cooked-index-entry.h" +#include "dwarf2/types.h" +#include "gdbsupport/gdb_obstack.h" +#include "addrmap.h" +#include "gdbsupport/iterator-range.h" +#include "gdbsupport/string-set.h" + +/* An index of interesting DIEs. This is "cooked", in contrast to a + mapped .debug_names or .gdb_index, which are "raw". An entry in + the index is of type cooked_index_entry. + + Operations on the index are described below. They are chosen to + make it relatively simple to implement the symtab "quick" + methods. */ +class cooked_index_shard +{ +public: + cooked_index_shard () = default; + DISABLE_COPY_AND_ASSIGN (cooked_index_shard); + + /* Create a new cooked_index_entry and register it with this object. + Entries are owned by this object. The new item is returned. */ + cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, + cooked_index_flag flags, enum language lang, + const char *name, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu); + + /* Install a new fixed addrmap from the given mutable addrmap. */ + void install_addrmap (addrmap_mutable *map) + { + gdb_assert (m_addrmap == nullptr); + m_addrmap = new (&m_storage) addrmap_fixed (&m_storage, map); + } + + friend class cooked_index; + + /* A simple range over part of m_entries. */ + typedef iterator_range<std::vector<cooked_index_entry *>::const_iterator> + range; + + /* Return a range of all the entries. */ + range all_entries () const + { + return { m_entries.cbegin (), m_entries.cend () }; + } + + /* Look up an entry by name. Returns a range of all matching + results. If COMPLETING is true, then a larger range, suitable + for completion, will be returned. */ + range find (const std::string &name, bool completing) const; + +private: + + /* Return the entry that is believed to represent the program's + "main". This will return NULL if no such entry is available. */ + const cooked_index_entry *get_main () const + { + return m_main; + } + + /* Look up ADDR in the address map, and return either the + corresponding CU, or nullptr if the address could not be + found. */ + dwarf2_per_cu *lookup (unrelocated_addr addr) + { + if (m_addrmap == nullptr) + return nullptr; + + return (static_cast<dwarf2_per_cu *> (m_addrmap->find ((CORE_ADDR) addr))); + } + + /* Create a new cooked_index_entry and register it with this object. + Entries are owned by this object. The new item is returned. */ + cooked_index_entry *create (sect_offset die_offset, + enum dwarf_tag tag, + cooked_index_flag flags, + enum language lang, + const char *name, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu); + + /* When GNAT emits mangled ("encoded") names in the DWARF, and does + not emit the module structure, we still need this structuring to + do lookups. This function recreates that information for an + existing entry, modifying ENTRY as appropriate. Any new entries + are added to NEW_ENTRIES. */ + void handle_gnat_encoded_entry + (cooked_index_entry *entry, htab_t gnat_entries, + std::vector<cooked_index_entry *> &new_entries); + + /* Finalize the index. This should be called a single time, when + the index has been fully populated. It enters all the entries + into the internal table and fixes up all missing parent links. + This may be invoked in a worker thread. */ + void finalize (const parent_map_map *parent_maps); + + /* Storage for the entries. */ + auto_obstack m_storage; + /* List of all entries. */ + std::vector<cooked_index_entry *> m_entries; + /* If we found an entry with 'is_main' set, store it here. */ + cooked_index_entry *m_main = nullptr; + /* The addrmap. This maps address ranges to dwarf2_per_cu objects. */ + addrmap_fixed *m_addrmap = nullptr; + /* Storage for canonical names. */ + gdb::string_set m_names; +}; + +using cooked_index_shard_up = std::unique_ptr<cooked_index_shard>; + +#endif /* GDB_DWARF2_COOKED_INDEX_SHARD_H */ diff --git a/gdb/dwarf2/cooked-index-storage.c b/gdb/dwarf2/cooked-index-storage.c deleted file mode 100644 index 9c05cf5..0000000 --- a/gdb/dwarf2/cooked-index-storage.c +++ /dev/null @@ -1,84 +0,0 @@ -/* DWARF index storage - - Copyright (C) 2022-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/>. */ - -#include "dwarf2/cooked-index-storage.h" - -/* See cooked-index-storage.h. */ - -cooked_index_storage::cooked_index_storage () - : m_shard (new cooked_index_shard) -{ -} - -/* See cooked-index-storage.h. */ - -cutu_reader * -cooked_index_storage::get_reader (dwarf2_per_cu *per_cu) -{ - auto it = m_reader_hash.find (*per_cu); - return it != m_reader_hash.end () ? it->get () : nullptr; -} - -/* See cooked-index-storage.h. */ - -cutu_reader * -cooked_index_storage::preserve (cutu_reader_up reader) -{ - m_abbrev_table_cache.add (reader->release_abbrev_table ()); - - auto [it, inserted] = m_reader_hash.insert (std::move (reader)); - gdb_assert (inserted); - - return it->get(); -} - -/* See cooked-index-storage.h. */ - -std::uint64_t -cooked_index_storage::cutu_reader_hash::operator() - (const cutu_reader_up &reader) const noexcept -{ - return (*this) (*reader->cu ()->per_cu); -} - -/* See cooked-index-storage.h. */ - -std::uint64_t -cooked_index_storage::cutu_reader_hash::operator() (const dwarf2_per_cu &per_cu) - const noexcept -{ - return per_cu.index; -} - -/* See cooked-index-storage.h. */ - -bool -cooked_index_storage::cutu_reader_eq::operator() (const cutu_reader_up &a, - const cutu_reader_up &b) const noexcept -{ - return (*this) (*a->cu ()->per_cu, b); -} - -/* See cooked-index-storage.h. */ - -bool cooked_index_storage::cutu_reader_eq::operator() - (const dwarf2_per_cu &per_cu, const cutu_reader_up &reader) const noexcept -{ - return per_cu.index == reader->cu ()->per_cu->index; -} diff --git a/gdb/dwarf2/cooked-index-storage.h b/gdb/dwarf2/cooked-index-storage.h deleted file mode 100644 index 449fbe1..0000000 --- a/gdb/dwarf2/cooked-index-storage.h +++ /dev/null @@ -1,131 +0,0 @@ -/* DWARF index storage - - Copyright (C) 2022-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/>. */ - -#ifndef GDB_DWARF2_COOKED_INDEX_STORAGE_H -#define GDB_DWARF2_COOKED_INDEX_STORAGE_H - -#include "dwarf2/abbrev-table-cache.h" -#include "dwarf2/cooked-index.h" -#include "dwarf2/types.h" - -struct cutu_reader; -struct dwarf2_per_cu; - -using cutu_reader_up = std::unique_ptr<cutu_reader>; - -/* An instance of this is created when scanning DWARF to create a - cooked index. */ - -class cooked_index_storage -{ -public: - - cooked_index_storage (); - DISABLE_COPY_AND_ASSIGN (cooked_index_storage); - - /* Return the current abbrev table_cache. */ - const abbrev_table_cache &get_abbrev_table_cache () const - { return m_abbrev_table_cache; } - - /* Return the DIE reader corresponding to PER_CU. If no such reader - has been registered, return NULL. */ - cutu_reader *get_reader (dwarf2_per_cu *per_cu); - - /* Preserve READER by storing it in the local hash table. */ - cutu_reader *preserve (cutu_reader_up reader); - - /* Add an entry to the index. The arguments describe the entry; see - cooked-index.h. The new entry is returned. */ - cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, - cooked_index_flag flags, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu) - { - return m_shard->add (die_offset, tag, flags, per_cu->lang (), - name, parent_entry, per_cu); - } - - /* Install the current addrmap into the shard being constructed, - then transfer ownership of the index to the caller. */ - cooked_index_shard_up release () - { - m_shard->install_addrmap (&m_addrmap); - return std::move (m_shard); - } - - /* Return the mutable addrmap that is currently being created. */ - addrmap_mutable *get_addrmap () - { - return &m_addrmap; - } - - /* Return the parent_map that is currently being created. */ - parent_map *get_parent_map () - { - return &m_parent_map; - } - - /* Return the parent_map that is currently being created. Ownership - is passed to the caller. */ - parent_map release_parent_map () - { - return std::move (m_parent_map); - } - -private: - /* The abbrev table cache used by this indexer. */ - abbrev_table_cache m_abbrev_table_cache; - - /* Hash function for a cutu_reader. */ - struct cutu_reader_hash - { - using is_transparent = void; - - std::uint64_t operator() (const cutu_reader_up &reader) const noexcept; - std::uint64_t operator() (const dwarf2_per_cu &per_cu) const noexcept; - }; - - /* Equality function for cutu_reader. */ - struct cutu_reader_eq - { - using is_transparent = void; - - bool operator() (const cutu_reader_up &a, - const cutu_reader_up &b) const noexcept; - - bool operator() (const dwarf2_per_cu &per_cu, - const cutu_reader_up &reader) const noexcept; - }; - - /* A hash table of cutu_reader objects. */ - gdb::unordered_set<cutu_reader_up, cutu_reader_hash, cutu_reader_eq> - m_reader_hash; - - /* The index shard that is being constructed. */ - cooked_index_shard_up m_shard; - - /* Parent map for each CU that is read. */ - parent_map m_parent_map; - - /* A writeable addrmap being constructed by this scanner. */ - addrmap_mutable m_addrmap; -}; - -#endif /* GDB_DWARF2_COOKED_INDEX_STORAGE_H */ diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c new file mode 100644 index 0000000..95ec943 --- /dev/null +++ b/gdb/dwarf2/cooked-index-worker.c @@ -0,0 +1,265 @@ +/* DWARF index storage + + Copyright (C) 2022-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/>. */ + +#include "dwarf2/cooked-index-worker.h" +#include "dwarf2/cooked-index.h" +#include "gdbsupport/thread-pool.h" +#include "run-on-main-thread.h" +#include "event-top.h" +#include "exceptions.h" + +/* See cooked-index-worker.h. */ + +cooked_index_worker_result::cooked_index_worker_result () + : m_shard (new cooked_index_shard) +{ +} + +/* See cooked-index-worker.h. */ + +cutu_reader * +cooked_index_worker_result::get_reader (dwarf2_per_cu *per_cu) +{ + auto it = m_reader_hash.find (*per_cu); + return it != m_reader_hash.end () ? it->get () : nullptr; +} + +/* See cooked-index-worker.h. */ + +cutu_reader * +cooked_index_worker_result::preserve (cutu_reader_up reader) +{ + m_abbrev_table_cache.add (reader->release_abbrev_table ()); + + auto [it, inserted] = m_reader_hash.insert (std::move (reader)); + gdb_assert (inserted); + + return it->get(); +} + +/* See cooked-index-worker.h. */ + +std::uint64_t +cooked_index_worker_result::cutu_reader_hash::operator() + (const cutu_reader_up &reader) const noexcept +{ + return (*this) (*reader->cu ()->per_cu); +} + +/* See cooked-index-worker.h. */ + +std::uint64_t +cooked_index_worker_result::cutu_reader_hash::operator() (const dwarf2_per_cu &per_cu) + const noexcept +{ + return per_cu.index; +} + +/* See cooked-index-worker.h. */ + +bool +cooked_index_worker_result::cutu_reader_eq::operator() (const cutu_reader_up &a, + const cutu_reader_up &b) const noexcept +{ + return (*this) (*a->cu ()->per_cu, b); +} + +/* See cooked-index-worker.h. */ + +bool cooked_index_worker_result::cutu_reader_eq::operator() + (const dwarf2_per_cu &per_cu, const cutu_reader_up &reader) const noexcept +{ + return per_cu.index == reader->cu ()->per_cu->index; +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker_result::emit_complaints_and_exceptions + (gdb::unordered_set<gdb_exception> &seen_exceptions) +{ + gdb_assert (is_main_thread ()); + + re_emit_complaints (m_complaints); + + /* Only show a given exception a single time. */ + for (auto &one_exc : m_exceptions) + if (seen_exceptions.insert (one_exc).second) + exception_print (gdb_stderr, one_exc); +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker::start () +{ + gdb::thread_pool::g_thread_pool->post_task ([this] () + { + try + { + do_reading (); + } + catch (const gdb_exception &exc) + { + m_failed = exc; + set (cooked_state::CACHE_DONE); + } + + bfd_thread_cleanup (); + }); +} + +/* See cooked-index-worker.h. */ + +bool +cooked_index_worker::wait (cooked_state desired_state, bool allow_quit) +{ + bool done; +#if CXX_STD_THREAD + { + std::unique_lock<std::mutex> lock (m_mutex); + + /* This may be called from a non-main thread -- this functionality + is needed for the index cache -- but in this case we require + that the desired state already have been attained. */ + gdb_assert (is_main_thread () || desired_state <= m_state); + + while (desired_state > m_state) + { + if (allow_quit) + { + std::chrono::milliseconds duration { 15 }; + if (m_cond.wait_for (lock, duration) == std::cv_status::timeout) + QUIT; + } + else + m_cond.wait (lock); + } + done = m_state == cooked_state::CACHE_DONE; + } +#else + /* Without threads, all the work is done immediately on the main + thread, and there is never anything to wait for. */ + done = desired_state == cooked_state::CACHE_DONE; +#endif /* CXX_STD_THREAD */ + + /* Only the main thread is allowed to report complaints and the + like. */ + if (!is_main_thread ()) + return false; + + if (m_reported) + return done; + m_reported = true; + + /* Emit warnings first, maybe they were emitted before an exception + (if any) was thrown. */ + m_warnings.emit (); + + if (m_failed.has_value ()) + { + /* do_reading failed -- report it. */ + exception_print (gdb_stderr, *m_failed); + m_failed.reset (); + return done; + } + + /* Only show a given exception a single time. */ + gdb::unordered_set<gdb_exception> seen_exceptions; + for (auto &one_result : m_results) + one_result.emit_complaints_and_exceptions (seen_exceptions); + + print_stats (); + + struct objfile *objfile = m_per_objfile->objfile; + dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; + cooked_index *table + = (gdb::checked_static_cast<cooked_index *> + (per_bfd->index_table.get ())); + + auto_obstack temp_storage; + enum language lang = language_unknown; + const char *main_name = table->get_main_name (&temp_storage, &lang); + if (main_name != nullptr) + set_objfile_main_name (objfile, main_name, lang); + + /* dwarf_read_debug_printf ("Done building psymtabs of %s", */ + /* objfile_name (objfile)); */ + + return done; +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker::set (cooked_state desired_state) +{ + gdb_assert (desired_state != cooked_state::INITIAL); + +#if CXX_STD_THREAD + std::lock_guard<std::mutex> guard (m_mutex); + gdb_assert (desired_state > m_state); + m_state = desired_state; + m_cond.notify_one (); +#else + /* Without threads, all the work is done immediately on the main + thread, and there is never anything to do. */ +#endif /* CXX_STD_THREAD */ +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker::write_to_cache (const cooked_index *idx, + deferred_warnings *warn) const +{ + if (idx != nullptr) + { + /* Writing to the index cache may cause a warning to be emitted. + See PR symtab/30837. This arranges to capture all such + warnings. This is safe because we know the deferred_warnings + object isn't in use by any other thread at this point. */ + scoped_restore_warning_hook defer (warn); + m_cache_store.store (); + } +} + +/* See cooked-index-worker.h. */ + +void +cooked_index_worker::done_reading () +{ + /* Only handle the scanning results here. Complaints and exceptions + can only be dealt with on the main thread. */ + std::vector<cooked_index_shard_up> shards; + + for (auto &one_result : m_results) + { + shards.push_back (one_result.release_shard ()); + m_all_parents_map.add_map (*one_result.get_parent_map ()); + } + + shards.shrink_to_fit (); + + dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; + cooked_index *table + = (gdb::checked_static_cast<cooked_index *> + (per_bfd->index_table.get ())); + table->set_contents (std::move (shards), &m_warnings, &m_all_parents_map); +} diff --git a/gdb/dwarf2/cooked-index-worker.h b/gdb/dwarf2/cooked-index-worker.h new file mode 100644 index 0000000..fbbb3b5 --- /dev/null +++ b/gdb/dwarf2/cooked-index-worker.h @@ -0,0 +1,290 @@ +/* DWARF index storage + + Copyright (C) 2022-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/>. */ + +#ifndef GDB_DWARF2_COOKED_INDEX_WORKER_H +#define GDB_DWARF2_COOKED_INDEX_WORKER_H + +#include "dwarf2/abbrev-table-cache.h" +#include "dwarf2/cooked-index-entry.h" +#include "dwarf2/cooked-index-shard.h" +#include "dwarf2/types.h" +#include "dwarf2/read.h" + +#if CXX_STD_THREAD +#include <mutex> +#include <condition_variable> +#endif /* CXX_STD_THREAD */ + +using cutu_reader_up = std::unique_ptr<cutu_reader>; + +/* An instance of this is created when scanning DWARF to create a + cooked index. This class is the result of a single task to store + results while working -- that is, it is an implementation detail of + the threads managed by cooked_index_worker. Once scanning is done, + selected parts of the state here are stored into the shard, and + then these temporary objects are destroyed. */ + +class cooked_index_worker_result +{ +public: + + cooked_index_worker_result (); + DISABLE_COPY_AND_ASSIGN (cooked_index_worker_result); + + cooked_index_worker_result (cooked_index_worker_result &&) = default; + cooked_index_worker_result &operator= (cooked_index_worker_result &&) + = default; + + /* Return the current abbrev table_cache. */ + const abbrev_table_cache &get_abbrev_table_cache () const + { return m_abbrev_table_cache; } + + /* Return the DIE reader corresponding to PER_CU. If no such reader + has been registered, return NULL. */ + cutu_reader *get_reader (dwarf2_per_cu *per_cu); + + /* Preserve READER by storing it in the local hash table. */ + cutu_reader *preserve (cutu_reader_up reader); + + /* Add an entry to the index. The arguments describe the entry; see + cooked-index.h. The new entry is returned. */ + cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, + cooked_index_flag flags, + const char *name, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu) + { + return m_shard->add (die_offset, tag, flags, per_cu->lang (), + name, parent_entry, per_cu); + } + + /* Overload that allows the language to be specified. */ + cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, + cooked_index_flag flags, enum language lang, + const char *name, + cooked_index_entry_ref parent_entry, + dwarf2_per_cu *per_cu) + { + return m_shard->add (die_offset, tag, flags, lang, + name, parent_entry, per_cu); + } + + /* Install the current addrmap into the shard being constructed, + then transfer ownership of the index to the caller. */ + cooked_index_shard_up release_shard () + { + m_shard->install_addrmap (&m_addrmap); + /* This isn't needed any more. */ + m_addrmap.clear (); + return std::move (m_shard); + } + + /* Return the mutable addrmap that is currently being created. */ + addrmap_mutable *get_addrmap () + { + return &m_addrmap; + } + + /* Return the parent_map that is currently being created. */ + parent_map *get_parent_map () + { + return &m_parent_map; + } + + /* Add an exception to the list of exceptions caught while reading. + These are passed forward and printed by the main thread. */ + void note_error (gdb_exception &&except) + { + m_exceptions.push_back (std::move (except)); + } + + /* Called when the thread using this object is done with its work. + This stores any complaints for later emission, and it clears some + data that won't be needed again. */ + void done_reading (complaint_collection &&complaints) + { + /* Hang on to the complaints. */ + m_complaints = std::move (complaints); + /* Discard things that are no longer needed. */ + m_reader_hash.clear (); + } + + /* Called to emit any stored complaints or exceptions. This can + only be called on the main thread. */ + void emit_complaints_and_exceptions + (gdb::unordered_set<gdb_exception> &seen_exceptions); + +private: + /* The abbrev table cache used by this indexer. */ + abbrev_table_cache m_abbrev_table_cache; + + /* Hash function for a cutu_reader. */ + struct cutu_reader_hash + { + using is_transparent = void; + + std::uint64_t operator() (const cutu_reader_up &reader) const noexcept; + std::uint64_t operator() (const dwarf2_per_cu &per_cu) const noexcept; + }; + + /* Equality function for cutu_reader. */ + struct cutu_reader_eq + { + using is_transparent = void; + + bool operator() (const cutu_reader_up &a, + const cutu_reader_up &b) const noexcept; + + bool operator() (const dwarf2_per_cu &per_cu, + const cutu_reader_up &reader) const noexcept; + }; + + /* A hash table of cutu_reader objects. */ + gdb::unordered_set<cutu_reader_up, cutu_reader_hash, cutu_reader_eq> + m_reader_hash; + + /* The index shard that is being constructed. */ + cooked_index_shard_up m_shard; + + /* Parent map for each CU that is read. */ + parent_map m_parent_map; + + /* A writeable addrmap being constructed by this scanner. */ + addrmap_mutable m_addrmap; + + /* The issued complaints. Only set after done_reading is + called. */ + complaint_collection m_complaints; + + /* Exceptions that we're storing to emit later. */ + std::vector<gdb_exception> m_exceptions; +}; + +/* The possible states of the index. See the explanatory comment + before cooked_index for more details. */ +enum class cooked_state +{ + /* The default state. This is not a valid argument to 'wait'. */ + INITIAL, + /* The initial scan has completed. The name of "main" is now + available (if known). The addrmaps are usable now. + Finalization has started but is not complete. */ + MAIN_AVAILABLE, + /* Finalization has completed. This means the index is fully + available for queries. */ + FINALIZED, + /* Writing to the index cache has finished. */ + CACHE_DONE, +}; + +/* An object of this type controls the scanning of the DWARF. It + schedules the worker tasks and tracks the current state. Once + scanning is done, this object is discarded. + + This is an abstract base class that defines the basic behavior of + scanners. Separate concrete implementations exist for scanning + .debug_names and .debug_info. */ + +class cooked_index_worker +{ +public: + + explicit cooked_index_worker (dwarf2_per_objfile *per_objfile) + : m_per_objfile (per_objfile), + m_cache_store (global_index_cache, per_objfile->per_bfd) + { } + virtual ~cooked_index_worker () + { } + DISABLE_COPY_AND_ASSIGN (cooked_index_worker); + + /* Start reading. */ + void start (); + + /* Wait for a particular state to be achieved. If ALLOW_QUIT is + true, then the loop will check the QUIT flag. Normally this + method may only be called from the main thread; however, it can + be called from a worker thread provided that the desired state + has already been attained. (This oddity is used by the index + cache writer.) */ + bool wait (cooked_state desired_state, bool allow_quit); + +protected: + + /* Let cooked_index call the 'set' and 'write_to_cache' methods. */ + friend class cooked_index; + + /* Set the current state. */ + void set (cooked_state desired_state); + + /* Write to the index cache. */ + void write_to_cache (const cooked_index *idx, + deferred_warnings *warn) const; + + /* Helper function that does the work of reading. This must be able + to be run in a worker thread without problems. */ + virtual void do_reading () = 0; + + /* Helper function that should be called when done reading. This + assumes that m_results is filled in, and will initialize + m_all_parents_map and end by calling + cooked_index::set_contents. */ + virtual void done_reading (); + + /* A callback that can print stats, if needed. This is called when + transitioning to the 'MAIN_AVAILABLE' state. */ + virtual void print_stats () + { } + + /* The per-objfile object. */ + dwarf2_per_objfile *m_per_objfile; + /* Result of each worker task. */ + std::vector<cooked_index_worker_result> m_results; + /* Any warnings emitted. For the time being at least, this only + needed in do_reading, not in every worker. Note that + deferred_warnings uses gdb_stderr in its constructor, and this + should only be done from the main thread. This is enforced in + the cooked_index_worker constructor. */ + deferred_warnings m_warnings; + + /* A map of all parent maps. Used during finalization to fix up + parent relationships. */ + parent_map_map m_all_parents_map; + +#if CXX_STD_THREAD + /* Current state of this object. */ + cooked_state m_state = cooked_state::INITIAL; + /* Mutex and condition variable used to synchronize. */ + std::mutex m_mutex; + std::condition_variable m_cond; +#endif /* CXX_STD_THREAD */ + /* This flag indicates whether any complaints or exceptions that + arose during scanning have been reported by 'wait'. This may + only be modified on the main thread. */ + bool m_reported = false; + /* If set, an exception occurred during reading; in this case the + scanning is stopped and this exception will later be reported by + the 'wait' method. */ + std::optional<gdb_exception> m_failed; + /* An object used to write to the index cache. */ + index_cache_store_context m_cache_store; +}; + +using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; + +#endif /* GDB_DWARF2_COOKED_INDEX_WORKER_H */ diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c index feaf9b5..a632474 100644 --- a/gdb/dwarf2/cooked-index.c +++ b/gdb/dwarf2/cooked-index.c @@ -18,24 +18,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "dwarf2/cooked-index.h" -#include "dwarf2/index-common.h" #include "dwarf2/read.h" #include "dwarf2/stringify.h" #include "dwarf2/index-cache.h" -#include "cp-support.h" -#include "c-lang.h" -#include "ada-lang.h" -#include "dwarf2/tag.h" #include "event-top.h" -#include "exceptions.h" #include "split-name.h" #include "observable.h" #include "run-on-main-thread.h" #include <algorithm> -#include "gdbsupport/gdb-safe-ctype.h" -#include "gdbsupport/selftest.h" #include "gdbsupport/task-group.h" -#include "gdbsupport/thread-pool.h" #include <chrono> #include "cli/cli-cmds.h" @@ -44,25 +35,12 @@ here, and then these are all waited for before exit proceeds. */ static gdb::unordered_set<cooked_index *> active_vectors; -/* See cooked-index.h. */ - -std::string -to_string (cooked_index_flag flags) -{ - static constexpr cooked_index_flag::string_mapping mapping[] = { - MAP_ENUM_FLAG (IS_MAIN), - MAP_ENUM_FLAG (IS_STATIC), - MAP_ENUM_FLAG (IS_LINKAGE), - MAP_ENUM_FLAG (IS_TYPE_DECLARATION), - MAP_ENUM_FLAG (IS_PARENT_DEFERRED), - }; - - return flags.to_string (mapping); -} - -/* See cooked-index.h. */ +/* Return true if LANG requires canonicalization. This is used + primarily to work around an issue computing the name of "main". + This function must be kept in sync with + cooked_index_shard::finalize. */ -bool +static bool language_requires_canonicalization (enum language lang) { return (lang == language_ada @@ -70,648 +48,6 @@ language_requires_canonicalization (enum language lang) || lang == language_cplus); } -/* Return true if a plain "main" could be the main program for this - language. Languages that are known to use some other mechanism are - excluded here. */ - -static bool -language_may_use_plain_main (enum language lang) -{ - /* No need to handle "unknown" here. */ - return (lang == language_c - || lang == language_objc - || lang == language_cplus - || lang == language_m2 - || lang == language_asm - || lang == language_opencl - || lang == language_minimal); -} - -/* See cooked-index.h. */ - -int -cooked_index_entry::compare (const char *stra, const char *strb, - comparison_mode mode) -{ -#if defined (__GNUC__) && !defined (__clang__) && __GNUC__ <= 7 - /* Work around error with gcc 7.5.0. */ - auto munge = [] (char c) -> unsigned char -#else - auto munge = [] (char c) constexpr -> unsigned char -#endif - { - /* Treat '<' as if it ended the string. This lets something - like "func<t>" match "func<t<int>>". See the "Breakpoints in - template functions" section in the manual. */ - if (c == '<') - return '\0'; - return TOLOWER ((unsigned char) c); - }; - - unsigned char a = munge (*stra); - unsigned char b = munge (*strb); - - while (a != '\0' && b != '\0' && a == b) - { - a = munge (*++stra); - b = munge (*++strb); - } - - if (a == b) - return 0; - - /* When completing, if STRB ends earlier than STRA, consider them as - equal. */ - if (mode == COMPLETE && b == '\0') - return 0; - - return a < b ? -1 : 1; -} - -#if GDB_SELF_TEST - -namespace { - -void -test_compare () -{ - /* Convenience aliases. */ - const auto mode_compare = cooked_index_entry::MATCH; - const auto mode_sort = cooked_index_entry::SORT; - const auto mode_complete = cooked_index_entry::COMPLETE; - - SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", - mode_compare) < 0); - SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", - mode_compare) > 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", - mode_complete) < 0); - SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name", "name<>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<>", "name", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name", "name<>", - mode_complete) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<>", "name", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", - mode_complete) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", - "name<arg<more>>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", - "name<arg<more>>", - mode_compare) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_compare) < 0); - SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_complete) < 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_compare) > 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("func", "func<type>", - mode_sort) == 0); - SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1", - mode_sort) < 0); -} - -} /* anonymous namespace */ - -#endif /* GDB_SELF_TEST */ - -/* See cooked-index.h. */ - -bool -cooked_index_entry::matches (domain_search_flags kind) const -{ - /* Just reject type declarations. */ - if ((flags & IS_TYPE_DECLARATION) != 0) - return false; - - return tag_matches_domain (tag, kind, lang); -} - -/* See cooked-index.h. */ - -const char * -cooked_index_entry::full_name (struct obstack *storage, - cooked_index_full_name_flag name_flags, - const char *default_sep) const -{ - const char *local_name = ((name_flags & FOR_MAIN) != 0) ? name : canonical; - - if ((flags & IS_LINKAGE) != 0 || get_parent () == nullptr) - return local_name; - - const char *sep = default_sep; - switch (lang) - { - case language_cplus: - case language_rust: - case language_fortran: - sep = "::"; - break; - - case language_ada: - if ((name_flags & FOR_ADA_LINKAGE_NAME) != 0) - { - sep = "__"; - break; - } - [[fallthrough]]; - case language_go: - case language_d: - sep = "."; - break; - - default: - if (sep == nullptr) - return local_name; - break; - } - - /* The FOR_ADA_LINKAGE_NAME flag should only affect Ada entries, so - disable it here if we don't need it. */ - if (lang != language_ada) - name_flags &= ~FOR_ADA_LINKAGE_NAME; - - get_parent ()->write_scope (storage, sep, name_flags); - obstack_grow0 (storage, local_name, strlen (local_name)); - return (const char *) obstack_finish (storage); -} - -/* See cooked-index.h. */ - -void -cooked_index_entry::write_scope (struct obstack *storage, - const char *sep, - cooked_index_full_name_flag flags) const -{ - if (get_parent () != nullptr) - get_parent ()->write_scope (storage, sep, flags); - /* When computing the Ada linkage name, the entry might not have - been canonicalized yet, so use the 'name'. */ - const char *local_name = ((flags & (FOR_MAIN | FOR_ADA_LINKAGE_NAME)) != 0 - ? name - : canonical); - obstack_grow (storage, local_name, strlen (local_name)); - obstack_grow (storage, sep, strlen (sep)); -} - -/* See cooked-index.h. */ - -cooked_index_entry * -cooked_index_shard::create (sect_offset die_offset, - enum dwarf_tag tag, - cooked_index_flag flags, - enum language lang, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu) -{ - if (tag == DW_TAG_module || tag == DW_TAG_namespace) - flags &= ~IS_STATIC; - else if (lang == language_cplus - && (tag == DW_TAG_class_type - || tag == DW_TAG_interface_type - || tag == DW_TAG_structure_type - || tag == DW_TAG_union_type - || tag == DW_TAG_enumeration_type - || tag == DW_TAG_enumerator)) - flags &= ~IS_STATIC; - else if (tag_is_type (tag)) - flags |= IS_STATIC; - - return new (&m_storage) cooked_index_entry (die_offset, tag, flags, - lang, name, parent_entry, - per_cu); -} - -/* See cooked-index.h. */ - -cooked_index_entry * -cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag, - cooked_index_flag flags, enum language lang, - const char *name, cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu) -{ - cooked_index_entry *result = create (die_offset, tag, flags, lang, name, - parent_entry, per_cu); - m_entries.push_back (result); - - /* An explicitly-tagged main program should always override the - implicit "main" discovery. */ - if ((flags & IS_MAIN) != 0) - m_main = result; - else if ((flags & IS_PARENT_DEFERRED) == 0 - && parent_entry.resolved == nullptr - && m_main == nullptr - && language_may_use_plain_main (lang) - && strcmp (name, "main") == 0) - m_main = result; - - return result; -} - -/* See cooked-index.h. */ - -void -cooked_index_shard::handle_gnat_encoded_entry - (cooked_index_entry *entry, - htab_t gnat_entries, - std::vector<cooked_index_entry *> &new_entries) -{ - /* We decode Ada names in a particular way: operators and wide - characters are left as-is. This is done to make name matching a - bit simpler; and for wide characters, it means the choice of Ada - source charset does not affect the indexer directly. */ - std::string canonical = ada_decode (entry->name, false, false, false); - if (canonical.empty ()) - { - entry->canonical = entry->name; - return; - } - std::vector<std::string_view> names = split_name (canonical.c_str (), - split_style::DOT_STYLE); - std::string_view tail = names.back (); - names.pop_back (); - - const cooked_index_entry *parent = nullptr; - for (const auto &name : names) - { - uint32_t hashval = dwarf5_djb_hash (name); - void **slot = htab_find_slot_with_hash (gnat_entries, &name, - hashval, INSERT); - /* CUs are processed in order, so we only need to check the most - recent entry. */ - cooked_index_entry *last = (cooked_index_entry *) *slot; - if (last == nullptr || last->per_cu != entry->per_cu) - { - const char *new_name = m_names.insert (name); - last = create (entry->die_offset, DW_TAG_module, - IS_SYNTHESIZED, language_ada, new_name, parent, - entry->per_cu); - last->canonical = last->name; - new_entries.push_back (last); - *slot = last; - } - - parent = last; - } - - entry->set_parent (parent); - entry->canonical = m_names.insert (tail); -} - -/* Hash a cooked index entry by name pointer value. - - We can use pointer equality here because names come from .debug_str, which - will normally be unique-ified by the linker. Also, duplicates are relatively - harmless -- they just mean a bit of extra memory is used. */ - -struct cooked_index_entry_name_ptr_hash -{ - using is_avalanching = void; - - std::uint64_t operator () (const cooked_index_entry *entry) const noexcept - { - return ankerl::unordered_dense::hash<const char *> () (entry->name); - } -}; - -/* Compare cooked index entries by name pointer value. */ - -struct cooked_index_entry_name_ptr_eq -{ - bool operator () (const cooked_index_entry *a, - const cooked_index_entry *b) const noexcept - { - return a->name == b->name; - } -}; - -/* See cooked-index.h. */ - -void -cooked_index_shard::finalize (const parent_map_map *parent_maps) -{ - gdb::unordered_set<const cooked_index_entry *, - cooked_index_entry_name_ptr_hash, - cooked_index_entry_name_ptr_eq> seen_names; - - auto hash_entry = [] (const void *e) - { - const cooked_index_entry *entry = (const cooked_index_entry *) e; - return dwarf5_djb_hash (entry->canonical); - }; - - auto eq_entry = [] (const void *a, const void *b) -> int - { - const cooked_index_entry *ae = (const cooked_index_entry *) a; - const std::string_view *sv = (const std::string_view *) b; - return (strlen (ae->canonical) == sv->length () - && strncasecmp (ae->canonical, sv->data (), sv->length ()) == 0); - }; - - htab_up gnat_entries (htab_create_alloc (10, hash_entry, eq_entry, - nullptr, xcalloc, xfree)); - std::vector<cooked_index_entry *> new_gnat_entries; - - for (cooked_index_entry *entry : m_entries) - { - if ((entry->flags & IS_PARENT_DEFERRED) != 0) - { - const cooked_index_entry *new_parent - = parent_maps->find (entry->get_deferred_parent ()); - entry->resolve_parent (new_parent); - } - - /* Note that this code must be kept in sync with - language_requires_canonicalization. */ - gdb_assert (entry->canonical == nullptr); - if ((entry->flags & IS_LINKAGE) != 0) - entry->canonical = entry->name; - else if (entry->lang == language_ada) - { - /* Newer versions of GNAT emit DW_TAG_module and use a - hierarchical structure. In this case, we don't need to - do any extra work. This can be detected by looking for a - GNAT-encoded name. */ - if (strstr (entry->name, "__") == nullptr) - { - entry->canonical = entry->name; - - /* If the entry does not have a parent, then there's - nothing extra to do here -- the entry itself is - sufficient. - - However, if it does have a parent, we have to - synthesize an entry with the full name. This is - unfortunate, but it's necessary due to how some of - the Ada name-lookup code currently works. For - example, without this, ada_get_tsd_type will - fail. - - Eventually it would be good to change the Ada lookup - code, and then remove these entries (and supporting - code in cooked_index_entry::full_name). */ - if (entry->get_parent () != nullptr) - { - const char *fullname - = entry->full_name (&m_storage, FOR_ADA_LINKAGE_NAME); - cooked_index_entry *linkage = create (entry->die_offset, - entry->tag, - (entry->flags - | IS_LINKAGE - | IS_SYNTHESIZED), - language_ada, - fullname, - nullptr, - entry->per_cu); - linkage->canonical = fullname; - new_gnat_entries.push_back (linkage); - } - } - else - handle_gnat_encoded_entry (entry, gnat_entries.get (), - new_gnat_entries); - } - else if (entry->lang == language_cplus || entry->lang == language_c) - { - auto [it, inserted] = seen_names.insert (entry); - - if (inserted) - { - /* No entry with that name was present, compute the canonical - name. */ - gdb::unique_xmalloc_ptr<char> canon_name - = (entry->lang == language_cplus - ? cp_canonicalize_string (entry->name) - : c_canonicalize_name (entry->name)); - if (canon_name == nullptr) - entry->canonical = entry->name; - else - entry->canonical = m_names.insert (std::move (canon_name)); - } - else - { - /* An entry with that name was present, re-use its canonical - name. */ - entry->canonical = (*it)->canonical; - } - } - else - entry->canonical = entry->name; - } - - /* Make sure any new Ada entries end up in the results. This isn't - done when creating these new entries to avoid invalidating the - m_entries iterator used in the foreach above. */ - m_entries.insert (m_entries.end (), new_gnat_entries.begin (), - new_gnat_entries.end ()); - - m_entries.shrink_to_fit (); - std::sort (m_entries.begin (), m_entries.end (), - [] (const cooked_index_entry *a, const cooked_index_entry *b) - { - return *a < *b; - }); -} - -/* See cooked-index.h. */ - -cooked_index_shard::range -cooked_index_shard::find (const std::string &name, bool completing) const -{ - struct comparator - { - cooked_index_entry::comparison_mode mode; - - bool operator() (const cooked_index_entry *entry, - const char *name) const noexcept - { - return cooked_index_entry::compare (entry->canonical, name, mode) < 0; - } - - bool operator() (const char *name, - const cooked_index_entry *entry) const noexcept - { - return cooked_index_entry::compare (entry->canonical, name, mode) > 0; - } - }; - - return std::make_from_tuple<range> - (std::equal_range (m_entries.cbegin (), m_entries.cend (), name.c_str (), - comparator { (completing - ? cooked_index_entry::COMPLETE - : cooked_index_entry::MATCH) })); -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::start () -{ - gdb::thread_pool::g_thread_pool->post_task ([this] () - { - try - { - do_reading (); - } - catch (const gdb_exception &exc) - { - m_failed = exc; - set (cooked_state::CACHE_DONE); - } - - bfd_thread_cleanup (); - }); -} - -/* See cooked-index.h. */ - -bool -cooked_index_worker::wait (cooked_state desired_state, bool allow_quit) -{ - bool done; -#if CXX_STD_THREAD - { - std::unique_lock<std::mutex> lock (m_mutex); - - /* This may be called from a non-main thread -- this functionality - is needed for the index cache -- but in this case we require - that the desired state already have been attained. */ - gdb_assert (is_main_thread () || desired_state <= m_state); - - while (desired_state > m_state) - { - if (allow_quit) - { - std::chrono::milliseconds duration { 15 }; - if (m_cond.wait_for (lock, duration) == std::cv_status::timeout) - QUIT; - } - else - m_cond.wait (lock); - } - done = m_state == cooked_state::CACHE_DONE; - } -#else - /* Without threads, all the work is done immediately on the main - thread, and there is never anything to wait for. */ - done = desired_state == cooked_state::CACHE_DONE; -#endif /* CXX_STD_THREAD */ - - /* Only the main thread is allowed to report complaints and the - like. */ - if (!is_main_thread ()) - return false; - - if (m_reported) - return done; - m_reported = true; - - /* Emit warnings first, maybe they were emitted before an exception - (if any) was thrown. */ - m_warnings.emit (); - - if (m_failed.has_value ()) - { - /* do_reading failed -- report it. */ - exception_print (gdb_stderr, *m_failed); - m_failed.reset (); - return done; - } - - /* Only show a given exception a single time. */ - gdb::unordered_set<gdb_exception> seen_exceptions; - for (auto &one_result : m_results) - { - re_emit_complaints (std::get<1> (one_result)); - for (auto &one_exc : std::get<2> (one_result)) - if (seen_exceptions.insert (one_exc).second) - exception_print (gdb_stderr, one_exc); - } - - print_stats (); - - struct objfile *objfile = m_per_objfile->objfile; - dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; - cooked_index *table - = (gdb::checked_static_cast<cooked_index *> - (per_bfd->index_table.get ())); - - auto_obstack temp_storage; - enum language lang = language_unknown; - const char *main_name = table->get_main_name (&temp_storage, &lang); - if (main_name != nullptr) - set_objfile_main_name (objfile, main_name, lang); - - /* dwarf_read_debug_printf ("Done building psymtabs of %s", */ - /* objfile_name (objfile)); */ - - return done; -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::set (cooked_state desired_state) -{ - gdb_assert (desired_state != cooked_state::INITIAL); - -#if CXX_STD_THREAD - std::lock_guard<std::mutex> guard (m_mutex); - gdb_assert (desired_state > m_state); - m_state = desired_state; - m_cond.notify_one (); -#else - /* Without threads, all the work is done immediately on the main - thread, and there is never anything to do. */ -#endif /* CXX_STD_THREAD */ -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::write_to_cache (const cooked_index *idx, - deferred_warnings *warn) const -{ - if (idx != nullptr) - { - /* Writing to the index cache may cause a warning to be emitted. - See PR symtab/30837. This arranges to capture all such - warnings. This is safe because we know the deferred_warnings - object isn't in use by any other thread at this point. */ - scoped_restore_warning_hook defer (warn); - m_cache_store.store (); - } -} - cooked_index::cooked_index (cooked_index_worker_up &&worker) : m_state (std::move (worker)) { @@ -994,10 +330,6 @@ void _initialize_cooked_index (); void _initialize_cooked_index () { -#if GDB_SELF_TEST - selftests::register_test ("cooked_index_entry::compare", test_compare); -#endif - add_cmd ("wait-for-index-cache", class_maintenance, maintenance_wait_for_index_cache, _("\ Wait until all pending writes to the index cache have completed.\n\ diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h index 56c84bd..c16afa0 100644 --- a/gdb/dwarf2/cooked-index.h +++ b/gdb/dwarf2/cooked-index.h @@ -21,489 +21,20 @@ #define GDB_DWARF2_COOKED_INDEX_H #include "dwarf2.h" +#include "dwarf2/cooked-index-entry.h" #include "dwarf2/types.h" #include "symtab.h" #include "hashtab.h" #include "quick-symbol.h" #include "gdbsupport/gdb_obstack.h" #include "addrmap.h" -#include "gdbsupport/iterator-range.h" #include "dwarf2/mapped-index.h" #include "dwarf2/read.h" #include "dwarf2/parent-map.h" #include "gdbsupport/range-chain.h" -#include "gdbsupport/string-set.h" #include "complaints.h" - -#if CXX_STD_THREAD -#include <mutex> -#include <condition_variable> -#endif /* CXX_STD_THREAD */ - -struct dwarf2_per_cu; -struct dwarf2_per_bfd; -struct index_cache_store_context; -struct cooked_index_entry; - -/* Flags that describe an entry in the index. */ -enum cooked_index_flag_enum : unsigned char -{ - /* True if this entry is the program's "main". */ - IS_MAIN = 1, - /* True if this entry represents a "static" object. */ - IS_STATIC = 2, - /* True if this entry uses the linkage name. */ - IS_LINKAGE = 4, - /* True if this entry is just for the declaration of a type, not the - definition. */ - IS_TYPE_DECLARATION = 8, - /* True is parent_entry.deferred has a value rather than parent_entry - .resolved. */ - IS_PARENT_DEFERRED = 16, - /* True if this entry was synthesized by gdb (as opposed to coming - directly from the DWARF). */ - IS_SYNTHESIZED = 32, -}; -DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag); - -/* Flags used when requesting the full name of an entry. */ -enum cooked_index_full_name_enum : unsigned char -{ - /* Set when requesting the name of "main". See the method for the - full description. */ - FOR_MAIN = 1, - /* Set when requesting the linkage name for an Ada entry. */ - FOR_ADA_LINKAGE_NAME = 2, -}; -DEF_ENUM_FLAGS_TYPE (enum cooked_index_full_name_enum, cooked_index_full_name_flag); - -/* Type representing either a resolved or deferred cooked_index_entry. */ - -union cooked_index_entry_ref -{ - cooked_index_entry_ref (parent_map::addr_type deferred_) - { - deferred = deferred_; - } - - cooked_index_entry_ref (const cooked_index_entry *resolved_) - { - resolved = resolved_; - } - - const cooked_index_entry *resolved; - parent_map::addr_type deferred; -}; - -/* Return a string representation of FLAGS. */ - -std::string to_string (cooked_index_flag flags); - -/* Return true if LANG requires canonicalization. This is used - primarily to work around an issue computing the name of "main". - This function must be kept in sync with - cooked_index_shard::finalize. */ - -extern bool language_requires_canonicalization (enum language lang); - -/* A cooked_index_entry represents a single item in the index. Note - that two entries can be created for the same DIE -- one using the - name, and another one using the linkage name, if any. - - This is an "open" class and the members are all directly - accessible. It is read-only after the index has been fully read - and processed. */ -struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry> -{ - cooked_index_entry (sect_offset die_offset_, enum dwarf_tag tag_, - cooked_index_flag flags_, - enum language lang_, const char *name_, - cooked_index_entry_ref parent_entry_, - dwarf2_per_cu *per_cu_) - : name (name_), - tag (tag_), - flags (flags_), - lang (lang_), - die_offset (die_offset_), - per_cu (per_cu_), - m_parent_entry (parent_entry_) - { - } - - /* Return true if this entry matches SEARCH_FLAGS. */ - bool matches (block_search_flags search_flags) const - { - /* Just reject type declarations. */ - if ((flags & IS_TYPE_DECLARATION) != 0) - return false; - - if ((search_flags & SEARCH_STATIC_BLOCK) != 0 - && (flags & IS_STATIC) != 0) - return true; - if ((search_flags & SEARCH_GLOBAL_BLOCK) != 0 - && (flags & IS_STATIC) == 0) - return true; - return false; - } - - /* Return true if this entry matches KIND. */ - bool matches (domain_search_flags kind) const; - - /* Construct the fully-qualified name of this entry and return a - pointer to it. If allocation is needed, it will be done on - STORAGE. - - FLAGS affects the result. If the FOR_MAIN flag is set, we are - computing the name of the "main" entry -- one marked - DW_AT_main_subprogram. This matters for avoiding name - canonicalization and also a related race (if "main" computation - is done during finalization). - - If the FOR_ADA_LINKAGE_NAME flag is set, then Ada-language - symbols will have their "linkage-style" name computed. The - default is source-style. - - If the language doesn't prescribe a separator, one can be - specified using DEFAULT_SEP. */ - const char *full_name (struct obstack *storage, - cooked_index_full_name_flag name_flags = 0, - const char *default_sep = nullptr) const; - - /* Comparison modes for the 'compare' function. See the function - for a description. */ - enum comparison_mode - { - MATCH, - SORT, - COMPLETE, - }; - - /* Compare two strings, case-insensitively. Return -1 if STRA is - less than STRB, 0 if they are equal, and 1 if STRA is greater. - - When comparing, '<' is considered to be less than all other - printable characters. This ensures that "t<x>" sorts before - "t1", which is necessary when looking up "t". This '<' handling - is to ensure that certain C++ lookups work correctly. It is - inexact, and applied regardless of the search language, but this - is ok because callers of this code do more precise filtering - according to their needs. This is also why using a - case-insensitive comparison works even for languages that are - case sensitive. - - MODE controls how the comparison proceeds. - - MODE==SORT is used when sorting and the only special '<' handling - that it does is to ensure that '<' sorts before all other - printable characters. This ensures that the resulting ordering - will be binary-searchable. - - MODE==MATCH is used when searching for a symbol. In this case, - STRB must always be the search name, and STRA must be the name in - the index that is under consideration. In compare mode, early - termination of STRB may match STRA -- for example, "t<int>" and - "t" will be considered to be equal. (However, if A=="t" and - B=="t<int>", then this will not consider them as equal.) - - MODE==COMPLETE is used when searching for a symbol for - completion. In this case, STRB must always be the search name, - and STRA must be the name in the index that is under - consideration. In completion mode, early termination of STRB - always results in a match. */ - static int compare (const char *stra, const char *strb, - comparison_mode mode); - - /* Compare two entries by canonical name. */ - bool operator< (const cooked_index_entry &other) const - { - return compare (canonical, other.canonical, SORT) < 0; - } - - /* Set parent entry to PARENT. */ - void set_parent (const cooked_index_entry *parent) - { - gdb_assert ((flags & IS_PARENT_DEFERRED) == 0); - m_parent_entry.resolved = parent; - } - - /* Resolve deferred parent entry to PARENT. */ - void resolve_parent (const cooked_index_entry *parent) - { - gdb_assert ((flags & IS_PARENT_DEFERRED) != 0); - flags = flags & ~IS_PARENT_DEFERRED; - m_parent_entry.resolved = parent; - } - - /* Return parent entry. */ - const cooked_index_entry *get_parent () const - { - gdb_assert ((flags & IS_PARENT_DEFERRED) == 0); - return m_parent_entry.resolved; - } - - /* Return deferred parent entry. */ - parent_map::addr_type get_deferred_parent () const - { - gdb_assert ((flags & IS_PARENT_DEFERRED) != 0); - return m_parent_entry.deferred; - } - - /* The name as it appears in DWARF. This always points into one of - the mapped DWARF sections. Note that this may be the name or the - linkage name -- two entries are created for DIEs which have both - attributes. */ - const char *name; - /* The canonical name. This may be equal to NAME. */ - const char *canonical = nullptr; - /* The DWARF tag. */ - enum dwarf_tag tag; - /* Any flags attached to this entry. */ - cooked_index_flag flags; - /* The language of this symbol. */ - ENUM_BITFIELD (language) lang : LANGUAGE_BITS; - /* The offset of this DIE. */ - sect_offset die_offset; - /* The CU from which this entry originates. */ - dwarf2_per_cu *per_cu; - -private: - - /* A helper method for full_name. Emits the full scope of this - object, followed by the separator, to STORAGE. If this entry has - a parent, its write_scope method is called first. See full_name - for a description of the FLAGS parameter. */ - void write_scope (struct obstack *storage, const char *sep, - cooked_index_full_name_flag flags) const; - - /* The parent entry. This is NULL for top-level entries. - Otherwise, it points to the parent entry, such as a namespace or - class. */ - cooked_index_entry_ref m_parent_entry; -}; - -class cooked_index; - -/* An index of interesting DIEs. This is "cooked", in contrast to a - mapped .debug_names or .gdb_index, which are "raw". An entry in - the index is of type cooked_index_entry. - - Operations on the index are described below. They are chosen to - make it relatively simple to implement the symtab "quick" - methods. */ -class cooked_index_shard -{ -public: - cooked_index_shard () = default; - DISABLE_COPY_AND_ASSIGN (cooked_index_shard); - - /* Create a new cooked_index_entry and register it with this object. - Entries are owned by this object. The new item is returned. */ - cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag, - cooked_index_flag flags, enum language lang, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu); - - /* Install a new fixed addrmap from the given mutable addrmap. */ - void install_addrmap (addrmap_mutable *map) - { - gdb_assert (m_addrmap == nullptr); - m_addrmap = new (&m_storage) addrmap_fixed (&m_storage, map); - } - - friend class cooked_index; - - /* A simple range over part of m_entries. */ - typedef iterator_range<std::vector<cooked_index_entry *>::const_iterator> - range; - - /* Return a range of all the entries. */ - range all_entries () const - { - return { m_entries.cbegin (), m_entries.cend () }; - } - - /* Look up an entry by name. Returns a range of all matching - results. If COMPLETING is true, then a larger range, suitable - for completion, will be returned. */ - range find (const std::string &name, bool completing) const; - -private: - - /* Return the entry that is believed to represent the program's - "main". This will return NULL if no such entry is available. */ - const cooked_index_entry *get_main () const - { - return m_main; - } - - /* Look up ADDR in the address map, and return either the - corresponding CU, or nullptr if the address could not be - found. */ - dwarf2_per_cu *lookup (unrelocated_addr addr) - { - if (m_addrmap == nullptr) - return nullptr; - - return (static_cast<dwarf2_per_cu *> (m_addrmap->find ((CORE_ADDR) addr))); - } - - /* Create a new cooked_index_entry and register it with this object. - Entries are owned by this object. The new item is returned. */ - cooked_index_entry *create (sect_offset die_offset, - enum dwarf_tag tag, - cooked_index_flag flags, - enum language lang, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu); - - /* When GNAT emits mangled ("encoded") names in the DWARF, and does - not emit the module structure, we still need this structuring to - do lookups. This function recreates that information for an - existing entry, modifying ENTRY as appropriate. Any new entries - are added to NEW_ENTRIES. */ - void handle_gnat_encoded_entry - (cooked_index_entry *entry, htab_t gnat_entries, - std::vector<cooked_index_entry *> &new_entries); - - /* Finalize the index. This should be called a single time, when - the index has been fully populated. It enters all the entries - into the internal table and fixes up all missing parent links. - This may be invoked in a worker thread. */ - void finalize (const parent_map_map *parent_maps); - - /* Storage for the entries. */ - auto_obstack m_storage; - /* List of all entries. */ - std::vector<cooked_index_entry *> m_entries; - /* If we found an entry with 'is_main' set, store it here. */ - cooked_index_entry *m_main = nullptr; - /* The addrmap. This maps address ranges to dwarf2_per_cu objects. */ - addrmap_fixed *m_addrmap = nullptr; - /* Storage for canonical names. */ - gdb::string_set m_names; -}; - -using cooked_index_shard_up = std::unique_ptr<cooked_index_shard>; - -/* The possible states of the index. See the explanatory comment - before cooked_index for more details. */ -enum class cooked_state -{ - /* The default state. This is not a valid argument to 'wait'. */ - INITIAL, - /* The initial scan has completed. The name of "main" is now - available (if known). The addrmaps are usable now. - Finalization has started but is not complete. */ - MAIN_AVAILABLE, - /* Finalization has completed. This means the index is fully - available for queries. */ - FINALIZED, - /* Writing to the index cache has finished. */ - CACHE_DONE, -}; - -/* An object of this type controls the scanning of the DWARF. It - schedules the worker tasks and tracks the current state. Once - scanning is done, this object is discarded. - - This is an abstract base class that defines the basic behavior of - scanners. Separate concrete implementations exist for scanning - .debug_names and .debug_info. */ - -class cooked_index_worker -{ -public: - - explicit cooked_index_worker (dwarf2_per_objfile *per_objfile) - : m_per_objfile (per_objfile), - m_cache_store (global_index_cache, per_objfile->per_bfd) - { } - virtual ~cooked_index_worker () - { } - DISABLE_COPY_AND_ASSIGN (cooked_index_worker); - - /* Start reading. */ - void start (); - - /* Wait for a particular state to be achieved. If ALLOW_QUIT is - true, then the loop will check the QUIT flag. Normally this - method may only be called from the main thread; however, it can - be called from a worker thread provided that the desired state - has already been attained. (This oddity is used by the index - cache writer.) */ - bool wait (cooked_state desired_state, bool allow_quit); - -protected: - - /* Let cooked_index call the 'set' and 'write_to_cache' methods. */ - friend class cooked_index; - - /* Set the current state. */ - void set (cooked_state desired_state); - - /* Write to the index cache. */ - void write_to_cache (const cooked_index *idx, - deferred_warnings *warn) const; - - /* Helper function that does the work of reading. This must be able - to be run in a worker thread without problems. */ - virtual void do_reading () = 0; - - /* A callback that can print stats, if needed. This is called when - transitioning to the 'MAIN_AVAILABLE' state. */ - virtual void print_stats () - { } - - /* Each thread returns a tuple holding a cooked index, any collected - complaints, a vector of errors that should be printed, and a - parent map. - - The errors are retained because GDB's I/O system is not - thread-safe. run_on_main_thread could be used, but that would - mean the messages are printed after the prompt, which looks - weird. */ - using result_type = std::tuple<cooked_index_shard_up, - complaint_collection, - std::vector<gdb_exception>, - parent_map>; - - /* The per-objfile object. */ - dwarf2_per_objfile *m_per_objfile; - /* Result of each worker task. */ - std::vector<result_type> m_results; - /* Any warnings emitted. This is not in 'result_type' because (for - the time being at least), it's only needed in do_reading, not in - every worker. Note that deferred_warnings uses gdb_stderr in its - constructor, and this should only be done from the main thread. - This is enforced in the cooked_index_worker constructor. */ - deferred_warnings m_warnings; - - /* A map of all parent maps. Used during finalization to fix up - parent relationships. */ - parent_map_map m_all_parents_map; - -#if CXX_STD_THREAD - /* Current state of this object. */ - cooked_state m_state = cooked_state::INITIAL; - /* Mutex and condition variable used to synchronize. */ - std::mutex m_mutex; - std::condition_variable m_cond; -#endif /* CXX_STD_THREAD */ - /* This flag indicates whether any complaints or exceptions that - arose during scanning have been reported by 'wait'. This may - only be modified on the main thread. */ - bool m_reported = false; - /* If set, an exception occurred during reading; in this case the - scanning is stopped and this exception will later be reported by - the 'wait' method. */ - std::optional<gdb_exception> m_failed; - /* An object used to write to the index cache. */ - index_cache_store_context m_cache_store; -}; - -using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; +#include "dwarf2/cooked-index-shard.h" +#include "dwarf2/cooked-index-worker.h" /* The main index of DIEs. @@ -515,6 +46,14 @@ using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; possible. This combination should help hide the effort from the user to the maximum possible degree. + There are a number of different objects involved in this process. + Most of them are temporary -- they are created to handle different + phases of scanning, then discarded when possible. The "steady + state" objects are index itself (cooked_index, below), which holds + the entries (cooked_index_entry), and the implementation of the + "quick" API (e.g., cooked_index_functions, though there are + other variants). + . Main Thread | Worker Threads ============================================================ . dwarf2_initialize_objfile @@ -546,6 +85,7 @@ using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; . v . maybe write to index cache . state = CACHE_DONE + . ~cooked_index_worker . . . if main thread calls... diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c index 789fdb5..1f3a235 100644 --- a/gdb/dwarf2/cooked-indexer.c +++ b/gdb/dwarf2/cooked-indexer.c @@ -18,12 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "dwarf2/cooked-indexer.h" -#include "dwarf2/cooked-index-storage.h" +#include "dwarf2/cooked-index-worker.h" #include "dwarf2/error.h" /* See cooked-indexer.h. */ -cooked_indexer::cooked_indexer (cooked_index_storage *storage, +cooked_indexer::cooked_indexer (cooked_index_worker_result *storage, dwarf2_per_cu *per_cu, enum language language) : m_index_storage (storage), m_per_cu (per_cu), diff --git a/gdb/dwarf2/cooked-indexer.h b/gdb/dwarf2/cooked-indexer.h index 93626a9..99e9fdd 100644 --- a/gdb/dwarf2/cooked-indexer.h +++ b/gdb/dwarf2/cooked-indexer.h @@ -26,7 +26,7 @@ #include <variant> struct abbrev_info; -struct cooked_index_storage; +struct cooked_index_worker_result; struct cutu_reader; struct dwarf2_per_cu; struct dwarf2_per_objfile; @@ -36,7 +36,7 @@ struct dwarf2_per_objfile; class cooked_indexer { public: - cooked_indexer (cooked_index_storage *storage, dwarf2_per_cu *per_cu, + cooked_indexer (cooked_index_worker_result *storage, dwarf2_per_cu *per_cu, enum language language); DISABLE_COPY_AND_ASSIGN (cooked_indexer); @@ -103,7 +103,7 @@ private: bool fully); /* The storage object, where the results are kept. */ - cooked_index_storage *m_index_storage; + cooked_index_worker_result *m_index_storage; /* The CU that we are reading on behalf of. This object might be asked to index one CU but to treat the results as if they come from some including CU; in this case the including CU would be diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c index edac713..96a8ad3 100644 --- a/gdb/dwarf2/read-debug-names.c +++ b/gdb/dwarf2/read-debug-names.c @@ -114,11 +114,12 @@ struct mapped_debug_names_reader gdb::unordered_map<ULONGEST, index_val> abbrev_map; - /* Even though the scanning of .debug_names and creation of the cooked index - entries is done serially, we create multiple shards so that the - finalization step can be parallelized. The shards are filled in a round - robin fashion. */ - std::vector<cooked_index_shard_up> shards; + /* Even though the scanning of .debug_names and creation of the + cooked index entries is done serially, we create multiple shards + so that the finalization step can be parallelized. The shards + are filled in a round robin fashion. It's convenient to use a + result object rather than an actual shard. */ + std::vector<cooked_index_worker_result> indices; /* Next shard to insert an entry in. */ int next_shard = 0; @@ -290,11 +291,11 @@ mapped_debug_names_reader::scan_one_entry (const char *name, if (per_cu != nullptr) { *result - = shards[next_shard]->add (die_offset, (dwarf_tag) indexval.dwarf_tag, + = indices[next_shard].add (die_offset, (dwarf_tag) indexval.dwarf_tag, flags, lang, name, nullptr, per_cu); ++next_shard; - if (next_shard == shards.size ()) + if (next_shard == indices.size ()) next_shard = 0; entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result); @@ -414,29 +415,31 @@ void cooked_index_worker_debug_names::do_reading () { complaint_interceptor complaint_handler; - std::vector<gdb_exception> exceptions; + try { m_map.scan_all_names (); } - catch (const gdb_exception &exc) + catch (gdb_exception &exc) { - exceptions.push_back (std::move (exc)); + /* Arbitrarily put all exceptions into the first result. */ + m_map.indices[0].note_error (std::move (exc)); } - m_results.emplace_back (nullptr, - complaint_handler.release (), - std::move (exceptions), - parent_map ()); - - dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; - cooked_index *table - = (gdb::checked_static_cast<cooked_index *> - (per_bfd->index_table.get ())); + bool first = true; + for (auto &iter : m_map.indices) + { + if (first) + { + iter.done_reading (complaint_handler.release ()); + first = false; + } + else + iter.done_reading ({}); + } - /* Note that this code never uses IS_PARENT_DEFERRED, so it is safe - to pass nullptr here. */ - table->set_contents (std::move (m_map.shards), &m_warnings, nullptr); + m_results = std::move (m_map.indices); + done_reading (); bfd_thread_cleanup (); } @@ -838,24 +841,26 @@ do_dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile) } per_bfd->debug_aranges.read (per_objfile->objfile); - addrmap_mutable addrmap; + + /* There is a single address map for the whole index (coming from + .debug_aranges). We only need to install it into a single shard + for it to get searched by cooked_index. So, we make the first + result object here, so we can store the addrmap, then move it + into place later. */ + cooked_index_worker_result first; deferred_warnings warnings; read_addrmap_from_aranges (per_objfile, &per_bfd->debug_aranges, - &addrmap, &warnings); + first.get_addrmap (), &warnings); warnings.emit (); const auto n_workers = std::max<std::size_t> (gdb::thread_pool::g_thread_pool->thread_count (), 1); - /* Create as many index shard as there are worker threads. */ - for (int i = 0; i < n_workers; ++i) - map.shards.emplace_back (std::make_unique<cooked_index_shard> ()); - - /* There is a single address map for the whole index (coming from - .debug_aranges). We only need to install it into a single shard for it to - get searched by cooked_index. */ - map.shards[0]->install_addrmap (&addrmap); + /* Create as many index shard as there are worker threads, + preserving the first one. */ + map.indices.push_back (std::move (first)); + map.indices.resize (n_workers); auto cidn = (std::make_unique<cooked_index_worker_debug_names> (per_objfile, std::move (map))); diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 3d7b6dd..17f06ff 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -32,7 +32,7 @@ #include "dwarf2/aranges.h" #include "dwarf2/attribute.h" #include "dwarf2/comp-unit-head.h" -#include "dwarf2/cooked-index-storage.h" +#include "dwarf2/cooked-index-worker.h" #include "dwarf2/cooked-indexer.h" #include "dwarf2/cu.h" #include "dwarf2/index-cache.h" @@ -728,7 +728,7 @@ show_dwarf_synchronous (struct ui_file *file, int from_tty, /* local function prototypes */ static void build_type_psymtabs_reader (cutu_reader *reader, - cooked_index_storage *storage); + cooked_index_worker_result *storage); static void var_decode_location (struct attribute *attr, struct symbol *sym, @@ -3289,7 +3289,7 @@ get_type_unit_group_key (struct dwarf2_cu *cu, const struct attribute *stmt_list static void process_psymtab_comp_unit (dwarf2_per_cu *this_cu, dwarf2_per_objfile *per_objfile, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { cutu_reader *reader = storage->get_reader (this_cu); if (reader == nullptr) @@ -3326,7 +3326,7 @@ process_psymtab_comp_unit (dwarf2_per_cu *this_cu, static void build_type_psymtabs_reader (cutu_reader *reader, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { struct dwarf2_cu *cu = reader->cu (); dwarf2_per_cu *per_cu = cu->per_cu; @@ -3379,7 +3379,7 @@ struct tu_abbrev_offset static void build_type_psymtabs (dwarf2_per_objfile *per_objfile, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { struct tu_stats *tu_stats = &per_objfile->per_bfd->tu_stats; abbrev_table_up abbrev_table; @@ -3476,7 +3476,7 @@ print_tu_stats (dwarf2_per_objfile *per_objfile) static void process_skeletonless_type_unit (dwo_unit *dwo_unit, dwarf2_per_objfile *per_objfile, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; @@ -3507,7 +3507,7 @@ process_skeletonless_type_unit (dwo_unit *dwo_unit, static void process_skeletonless_type_units (dwarf2_per_objfile *per_objfile, - cooked_index_storage *storage) + cooked_index_worker_result *storage) { /* Skeletonless TUs in DWP files without .gdb_index is not supported yet. */ if (get_dwp_file (per_objfile) == nullptr) @@ -3553,7 +3553,7 @@ private: /* After the last DWARF-reading task has finished, this function does the remaining work to finish the scan. */ - void done_reading (); + void done_reading () override; /* An iterator for the comp units. */ using unit_iterator = std::vector<dwarf2_per_cu_up>::iterator; @@ -3567,12 +3567,13 @@ private: /* A storage object for "leftovers" -- see the 'start' method, but essentially things not parsed during the normal CU parsing passes. */ - cooked_index_storage m_index_storage; + cooked_index_worker_result m_index_storage; }; void -cooked_index_worker_debug_info::process_cus (size_t task_number, unit_iterator first, - unit_iterator end) +cooked_index_worker_debug_info::process_cus (size_t task_number, + unit_iterator first, + unit_iterator end) { SCOPE_EXIT { bfd_thread_cleanup (); }; @@ -3580,7 +3581,7 @@ cooked_index_worker_debug_info::process_cus (size_t task_number, unit_iterator f complaint_interceptor complaint_handler; std::vector<gdb_exception> errors; - cooked_index_storage thread_storage; + cooked_index_worker_result thread_storage; for (auto inner = first; inner != end; ++inner) { dwarf2_per_cu *per_cu = inner->get (); @@ -3591,43 +3592,24 @@ cooked_index_worker_debug_info::process_cus (size_t task_number, unit_iterator f } catch (gdb_exception &except) { - errors.push_back (std::move (except)); + thread_storage.note_error (std::move (except)); } } - m_results[task_number] = result_type (thread_storage.release (), - complaint_handler.release (), - std::move (errors), - thread_storage.release_parent_map ()); + thread_storage.done_reading (complaint_handler.release ()); + m_results[task_number] = std::move (thread_storage); } void cooked_index_worker_debug_info::done_reading () { - /* Only handle the scanning results here. Complaints and exceptions - can only be dealt with on the main thread. */ - std::vector<cooked_index_shard_up> shards; - - for (auto &one_result : m_results) - { - shards.push_back (std::move (std::get<0> (one_result))); - m_all_parents_map.add_map (std::get<3> (one_result)); - } - /* This has to wait until we read the CUs, we need the list of DWOs. */ process_skeletonless_type_units (m_per_objfile, &m_index_storage); - shards.push_back (m_index_storage.release ()); - shards.shrink_to_fit (); - - m_all_parents_map.add_map (m_index_storage.release_parent_map ()); + m_results.push_back (std::move (m_index_storage)); - dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; - cooked_index *table - = (gdb::checked_static_cast<cooked_index *> - (per_bfd->index_table.get ())); - table->set_contents (std::move (shards), &m_warnings, - &m_all_parents_map); + /* Call into the base class. */ + cooked_index_worker::done_reading (); } void diff --git a/gdb/gdbarch-selftests.c b/gdb/gdbarch-selftests.c index 27b17d17..8f42557 100644 --- a/gdb/gdbarch-selftests.c +++ b/gdb/gdbarch-selftests.c @@ -127,6 +127,9 @@ register_to_value_test (struct gdbarch *gdbarch) static void register_name_test (struct gdbarch *gdbarch) { + if (selftest_skip_warning_arch (gdbarch)) + return; + scoped_mock_context<test_target_ops> mockctx (gdbarch); /* Track the number of times each register name appears. */ diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c index 8453366..7852f56 100644 --- a/gdb/go32-nat.c +++ b/gdb/go32-nat.c @@ -697,7 +697,7 @@ go32_nat_target::create_inferior (const char *exec_file, "not enough memory.\n")); /* Parse the command line and create redirections. */ - if (strpbrk (args, "<>")) + if (strpbrk (args, "<>") != nullptr) { if (redir_cmdline_parse (args, &child_cmd) == 0) args = child_cmd.command; diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index 3fb1c17..2b7bd2b 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -484,15 +484,15 @@ i386_canonicalize_syscall (int syscall) SYSCALL_MAP (settimeofday); SYSCALL_MAP_RENAME (getgroups, gdb_sys_getgroups16); SYSCALL_MAP_RENAME (setgroups, gdb_sys_setgroups16); - SYSCALL_MAP_RENAME (select, gdb_old_select); + SYSCALL_MAP_RENAME (select, gdb_sys_old_select); SYSCALL_MAP (symlink); SYSCALL_MAP_RENAME (oldlstat, gdb_sys_lstat); SYSCALL_MAP (readlink); SYSCALL_MAP (uselib); SYSCALL_MAP (swapon); SYSCALL_MAP (reboot); - SYSCALL_MAP_RENAME (readdir, gdb_old_readdir); - SYSCALL_MAP_RENAME (mmap, gdb_old_mmap); + SYSCALL_MAP_RENAME (readdir, gdb_sys_old_readdir); + SYSCALL_MAP_RENAME (mmap, gdb_sys_old_mmap); SYSCALL_MAP (munmap); SYSCALL_MAP (truncate); SYSCALL_MAP (ftruncate); diff --git a/gdb/linux-record.c b/gdb/linux-record.c index 0b2709b..2f66337 100644 --- a/gdb/linux-record.c +++ b/gdb/linux-record.c @@ -609,7 +609,7 @@ record_linux_system_call (enum gdb_syscall syscall, case gdb_sys_setgroups16: break; - case gdb_old_select: + case gdb_sys_old_select: { unsigned long sz_sel_arg = tdep->size_long + tdep->size_pointer * 4; gdb_byte *a = (gdb_byte *) alloca (sz_sel_arg); @@ -668,12 +668,12 @@ record_linux_system_call (enum gdb_syscall syscall, return 1; break; - case gdb_old_readdir: + case gdb_sys_old_readdir: if (record_mem_at_reg (regcache, tdep->arg2, tdep->size_old_dirent)) return -1; break; - case gdb_old_mmap: + case gdb_sys_old_mmap: break; case gdb_sys_munmap: diff --git a/gdb/linux-record.h b/gdb/linux-record.h index 6d6ba07..6656106 100644 --- a/gdb/linux-record.h +++ b/gdb/linux-record.h @@ -266,15 +266,15 @@ enum gdb_syscall { gdb_sys_settimeofday = 79, gdb_sys_getgroups16 = 80, gdb_sys_setgroups16 = 81, - gdb_old_select = 82, + gdb_sys_old_select = 82, gdb_sys_symlink = 83, gdb_sys_lstat = 84, gdb_sys_readlink = 85, gdb_sys_uselib = 86, gdb_sys_swapon = 87, gdb_sys_reboot = 88, - gdb_old_readdir = 89, - gdb_old_mmap = 90, + gdb_sys_old_readdir = 89, + gdb_sys_old_mmap = 90, gdb_sys_munmap = 91, gdb_sys_truncate = 92, gdb_sys_ftruncate = 93, diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c index bd42d09..031031b 100644 --- a/gdb/loongarch-linux-tdep.c +++ b/gdb/loongarch-linux-tdep.c @@ -812,7 +812,7 @@ loongarch_canonicalize_syscall (enum loongarch_syscall syscall_number) SYSCALL_MAP (clone); SYSCALL_MAP (execve); - SYSCALL_MAP_RENAME (mmap, gdb_sys_mmap2); + SYSCALL_MAP_RENAME (mmap, gdb_sys_old_mmap); SYSCALL_MAP (fadvise64); SYSCALL_MAP (swapon); diff --git a/gdb/regcache.c b/gdb/regcache.c index 5508778..ad72429 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -1911,32 +1911,13 @@ public: {} }; -/* Return true if regcache::cooked_{read,write}_test should be skipped for - GDBARCH. */ - -static bool -selftest_skiparch (struct gdbarch *gdbarch) -{ - const char *name = gdbarch_bfd_arch_info (gdbarch)->printable_name; - - /* Avoid warning: - Running selftest regcache::cooked_{read,write}_test::m68hc11. - warning: No frame soft register found in the symbol table. - Stack backtrace will not work. - We could instead capture the output and then filter out the warning, but - that seems more trouble than it's worth. */ - return (strcmp (name, "m68hc11") == 0 - || strcmp (name, "m68hc12") == 0 - || strcmp (name, "m68hc12:HCS12") == 0); -} - /* Test regcache::cooked_read gets registers from raw registers and memory instead of target to_{fetch,store}_registers. */ static void cooked_read_test (struct gdbarch *gdbarch) { - if (selftest_skiparch (gdbarch)) + if (selftest_skip_warning_arch (gdbarch)) return; scoped_mock_context<target_ops_no_register> mockctx (gdbarch); @@ -2074,7 +2055,7 @@ cooked_read_test (struct gdbarch *gdbarch) static void cooked_write_test (struct gdbarch *gdbarch) { - if (selftest_skiparch (gdbarch)) + if (selftest_skip_warning_arch (gdbarch)) return; /* Create a mock environment. A process_stratum target pushed. */ diff --git a/gdb/selftest-arch.c b/gdb/selftest-arch.c index 17eeba8..79889c0 100644 --- a/gdb/selftest-arch.c +++ b/gdb/selftest-arch.c @@ -108,5 +108,24 @@ reset () registers_changed (); reinit_frame_cache (); } + +/* See selftest-arch.h. */ + +bool +selftest_skip_warning_arch (struct gdbarch *gdbarch) +{ + const char *name = gdbarch_bfd_arch_info (gdbarch)->printable_name; + + /* Avoid warning: + Running selftest <test>::m68hc11. + warning: No frame soft register found in the symbol table. + Stack backtrace will not work. + We could instead capture the output and then filter out the warning, but + that seems more trouble than it's worth. */ + return (strcmp (name, "m68hc11") == 0 + || strcmp (name, "m68hc12") == 0 + || strcmp (name, "m68hc12:HCS12") == 0); +} + } /* namespace selftests */ #endif /* GDB_SELF_TEST */ diff --git a/gdb/selftest-arch.h b/gdb/selftest-arch.h index db11723..c6a85fa 100644 --- a/gdb/selftest-arch.h +++ b/gdb/selftest-arch.h @@ -29,6 +29,11 @@ namespace selftests extern void register_test_foreach_arch (const std::string &name, self_test_foreach_arch_function *function); + +/* Return true if GDBARCH should be skipped in some selftests to avoid + warnings. */ + +extern bool selftest_skip_warning_arch (struct gdbarch *gdbarch); } #endif /* GDB_SELFTEST_ARCH_H */ diff --git a/gdb/testsuite/lib/ada.exp b/gdb/testsuite/lib/ada.exp index 0a1231b..e1a3a23 100644 --- a/gdb/testsuite/lib/ada.exp +++ b/gdb/testsuite/lib/ada.exp @@ -181,12 +181,16 @@ proc find_ada_tool {tool} { # compiler does not appear to be GCC, this will always return false. proc gnat_version_compare {op l2} { - set gccvers [gcc_major_version] - if {$gccvers == -1} { + set gnatmake [find_gnatmake] + set gnatmake [lindex [split $gnatmake] 0] + if {[catch {exec $gnatmake --version} output]} { + return 0 + } + if {![regexp {GNATMAKE ([0-9]+(\.[0-9]+)*)} $output match version]} { return 0 } - return [version_compare [split $gccvers .] $op $l2] + return [version_compare [split $version .] $op $l2] } # Return 1 if the GNAT runtime appears to have debug info. diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 761a4f1..59967c7 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -3937,13 +3937,16 @@ gdb_caching_proc is_aarch32_target {} { return 0 } - set list {} - foreach reg \ - {r0 r1 r2 r3} { - lappend list "\tmov $reg, $reg" - } + return [gdb_can_simple_compile aarch32 { + int main (void) { + asm ("\tmov r0, r0"); + asm ("\tmov r1, r1"); + asm ("\tmov r2, r2"); + asm ("\tmov r3, r3"); - return [gdb_can_simple_compile aarch32 [join $list \n]] + return 0; + } + }] } # Return 1 if this target is an aarch64, either lp64 or ilp32. diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in index 491882e..d222028 100644 --- a/gdbserver/Makefile.in +++ b/gdbserver/Makefile.in @@ -194,6 +194,7 @@ SFILES = \ $(srcdir)/linux-loongarch-low.cc \ $(srcdir)/linux-low.cc \ $(srcdir)/linux-m68k-low.cc \ + $(srcdir)/linux-microblaze-low.cc \ $(srcdir)/linux-mips-low.cc \ $(srcdir)/linux-or1k-low.cc \ $(srcdir)/linux-ppc-low.cc \ diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv index e8dc8ef..6281cda 100644 --- a/gdbserver/configure.srv +++ b/gdbserver/configure.srv @@ -169,6 +169,13 @@ case "${gdbserver_host}" in srv_linux_regsets=yes srv_linux_thread_db=yes ;; + microblaze*-*-linux*) srv_regobj="microblaze-linux.o" + srv_tgtobj="$srv_linux_obj linux-microblaze-low.o" + srv_xmlfiles="microblaze-linux.xml" + srv_xmlfiles="${srv_xmlfiles} microblaze-core.xml" + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; mips*-*-linux*) srv_regobj="mips-linux.o" srv_regobj="${srv_regobj} mips-dsp-linux.o" srv_regobj="${srv_regobj} mips64-linux.o" diff --git a/gdbserver/linux-microblaze-low.cc b/gdbserver/linux-microblaze-low.cc new file mode 100644 index 0000000..2d97eef --- /dev/null +++ b/gdbserver/linux-microblaze-low.cc @@ -0,0 +1,245 @@ +/* GNU/Linux/Microblaze specific low level interface, for the remote server for + GDB. + Copyright (C) 1995-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/>. */ + +#include "server.h" +#include "linux-low.h" + +#include "elf/common.h" +#include "nat/gdb_ptrace.h" +#include <endian.h> + +#include <asm/ptrace.h> +#include <sys/procfs.h> +#include <sys/ptrace.h> + +#include "gdb_proc_service.h" + + +static int microblaze_regmap[] = + {PT_GPR(0), PT_GPR(1), PT_GPR(2), PT_GPR(3), + PT_GPR(4), PT_GPR(5), PT_GPR(6), PT_GPR(7), + PT_GPR(8), PT_GPR(9), PT_GPR(10), PT_GPR(11), + PT_GPR(12), PT_GPR(13), PT_GPR(14), PT_GPR(15), + PT_GPR(16), PT_GPR(17), PT_GPR(18), PT_GPR(19), + PT_GPR(20), PT_GPR(21), PT_GPR(22), PT_GPR(23), + PT_GPR(24), PT_GPR(25), PT_GPR(26), PT_GPR(27), + PT_GPR(28), PT_GPR(29), PT_GPR(30), PT_GPR(31), + PT_PC, PT_MSR, PT_EAR, PT_ESR, + PT_FSR + }; + + + +class microblaze_target : public linux_process_target +{ +public: + + const regs_info *get_regs_info () override; + + const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override; + +protected: + + void low_arch_setup () override; + + bool low_cannot_fetch_register (int regno) override; + + bool low_cannot_store_register (int regno) override; + + bool low_supports_breakpoints () override; + + CORE_ADDR low_get_pc (regcache *regcache) override; + + void low_set_pc (regcache *regcache, CORE_ADDR newpc) override; + + bool low_breakpoint_at (CORE_ADDR pc) override; +}; + +/* The singleton target ops object. */ + +static microblaze_target the_microblaze_target; + +constexpr auto microblaze_num_regs + = sizeof (microblaze_regmap) / sizeof (microblaze_regmap[0]); + +/* Defined in auto-generated file microblaze-linux-generated.c. */ +void init_registers_microblaze_linux (); +extern const target_desc *tdesc_microblaze_linux; + +bool +microblaze_target::low_supports_breakpoints () +{ + return true; +} + +bool +microblaze_target::low_cannot_store_register (int regno) +{ + if (microblaze_regmap[regno] == -1 || regno == 0) + return 1; + + return 0; +} + +bool +microblaze_target::low_cannot_fetch_register (int regno) +{ + return 0; +} + +CORE_ADDR +microblaze_target::low_get_pc (regcache *regcache) +{ + unsigned long pc; + + collect_register_by_name (regcache, "rpc", &pc); + return pc; +} + +void +microblaze_target::low_set_pc (regcache *regcache, CORE_ADDR pc) +{ + unsigned long newpc = pc; + + supply_register_by_name (regcache, "rpc", &newpc); +} + +/* dbtrap insn */ +/* brki r16, 0x18; */ +static const uint32_t microblaze_breakpoint = 0xba0c0018; +#define microblaze_breakpoint_len 4 + +const gdb_byte * +microblaze_target::sw_breakpoint_from_kind (int kind, int *size) +{ + *size = microblaze_breakpoint_len; + return reinterpret_cast<const gdb_byte *> (µblaze_breakpoint); +} + +bool +microblaze_target::low_breakpoint_at (CORE_ADDR where) +{ + uint32_t insn; + + read_memory (where, (unsigned char *) &insn, 4); + /* If necessary, recognize more trap instructions here. GDB only uses the + one. */ + return insn == microblaze_breakpoint; +} + +#ifdef HAVE_PTRACE_GETREGS + +static void +microblaze_collect_ptrace_register (struct regcache *regcache, int regno, + char *buf) +{ + memset (buf, 0, sizeof (long)); + + if (__BYTE_ORDER == __LITTLE_ENDIAN) + { + collect_register (regcache, regno, buf); + } + else if (__BYTE_ORDER == __BIG_ENDIAN) + { + int size = register_size (regcache->tdesc, regno); + + if (size < sizeof (long)) + collect_register (regcache, regno, buf + sizeof (long) - size); + else + collect_register (regcache, regno, buf); + } +} + +/* Collect GPRs from REGCACHE into BUF. */ + +static void microblaze_fill_gregset (struct regcache *regcache, void *buf) +{ + int i; + + for (i = 0; i < microblaze_num_regs; i++) + microblaze_collect_ptrace_register (regcache, i, + (char *) buf + microblaze_regmap[i]); +} + +/* Supply GPRs from BUF into REGCACHE. */ + +static void +microblaze_store_gregset (struct regcache *regcache, const void *buf) +{ + int i; + + for (i = 0; i < microblaze_num_regs; i++) + supply_register (regcache, i, (char *) buf + microblaze_regmap[i]); +} + +static struct regset_info microblaze_regsets[] = { + { PTRACE_GETREGS, PTRACE_SETREGS, NT_PRSTATUS, + sizeof (elf_gregset_t), GENERAL_REGS, + microblaze_fill_gregset, microblaze_store_gregset + }, + NULL_REGSET +}; +#endif /* HAVE_PTRACE_GETREGS */ + +static struct usrregs_info microblaze_usrregs_info = + { + microblaze_num_regs, + microblaze_regmap, + }; + +#ifdef HAVE_PTRACE_GETREGS +static struct regsets_info microblaze_regsets_info = + { + microblaze_regsets, /* regsets */ + 0, /* num_regsets */ + nullptr /* disabled_regsets */ + }; +#endif /* HAVE_PTRACE_GETREGS */ + +static struct regs_info microblaze_regs_info = + { + nullptr, /* regset_bitmap */ + µblaze_usrregs_info, +#ifdef HAVE_PTRACE_GETREGS + µblaze_regsets_info +#endif /* HAVE_PTRACE_GETREGS */ + }; + +const regs_info * +microblaze_target::get_regs_info () +{ + return µblaze_regs_info; +} + +void +microblaze_target::low_arch_setup () +{ + current_process ()->tdesc = tdesc_microblaze_linux; +} + +linux_process_target *the_linux_target = &the_microblaze_target; + +void +initialize_low_arch () +{ + init_registers_microblaze_linux (); +#ifdef HAVE_PTRACE_GETREGS + initialize_regsets_info (µblaze_regsets_info); +#endif /* HAVE_PTRACE_GETREGS */ +} diff --git a/gdbserver/setup.cfg b/gdbserver/setup.cfg deleted file mode 100644 index 08646b8..0000000 --- a/gdbserver/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[codespell] -# Skip ChangeLogs and generated files. -skip = ChangeLog*,configure -ignore-words = gdb/contrib/codespell-ignore-words.txt diff --git a/gdbsupport/common-inferior.cc b/gdbsupport/common-inferior.cc index 4b86829..d2fd348 100644 --- a/gdbsupport/common-inferior.cc +++ b/gdbsupport/common-inferior.cc @@ -59,7 +59,7 @@ escape_characters (const char *arg, const char *special) #ifdef __MINGW32__ bool quoted = false; - if (strpbrk (argv[i], special)) + if (strpbrk (arg, special) != nullptr) { quoted = true; result += quote; diff --git a/gdbsupport/setup.cfg b/gdbsupport/setup.cfg deleted file mode 100644 index e3e9298..0000000 --- a/gdbsupport/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[codespell] -# Skip ChangeLogs and generated files. -skip = ChangeLog*,Makefile.in,configure -ignore-words = gdb/contrib/codespell-ignore-words.txt diff --git a/ld/pe-dll.c b/ld/pe-dll.c index 4e72f1b..de1cfaf 100644 --- a/ld/pe-dll.c +++ b/ld/pe-dll.c @@ -381,6 +381,7 @@ static const autofilter_entry_type autofilter_liblist[] = { STRING_COMMA_LEN ("libmsvcrt") }, { STRING_COMMA_LEN ("libmsvcrt-os") }, { STRING_COMMA_LEN ("libucrt") }, + { STRING_COMMA_LEN ("libucrtapp") }, { STRING_COMMA_LEN ("libucrtbase") }, { STRING_COMMA_LEN ("libpthread") }, { STRING_COMMA_LEN ("libwinpthread") }, |