diff options
Diffstat (limited to 'gdb')
126 files changed, 3052 insertions, 1382 deletions
diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 9f21695..285a00b 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -492,7 +492,6 @@ SELFTESTS_SRCS = \ unittests/ui-file-selftests.c \ unittests/unique_xmalloc_ptr_char.c \ unittests/unpack-selftests.c \ - unittests/utils-selftests.c \ unittests/vec-utils-selftests.c \ unittests/xml-utils-selftests.c @@ -1098,7 +1097,6 @@ COMMON_SFILES = \ dwarf2/ada-imported.c \ dwarf2/aranges.c \ dwarf2/attribute.c \ - dwarf2/comp-unit-head.c \ dwarf2/cooked-index.c \ dwarf2/cooked-index-entry.c \ dwarf2/cooked-index-shard.c \ @@ -1123,6 +1121,7 @@ COMMON_SFILES = \ dwarf2/read-gdb-index.c \ dwarf2/section.c \ dwarf2/stringify.c \ + dwarf2/unit-head.c \ extract-store-integer.c \ eval.c \ event-top.c \ @@ -56,12 +56,28 @@ maintenance check psymtabs maintenance check symtabs Renamed from maintenance check-symtabs +maintenance canonicalize + Show the canonical form of a C++ name. + set riscv numeric-register-names on|off show riscv numeric-register-names Controls whether GDB refers to risc-v registers by their numeric names (e.g 'x1') or their abi names (e.g. 'ra'). Defaults to 'off', matching the old behaviour (abi names). +set style emoji on|off|auto +show style emoji + Controls whether GDB can display emoji. The default is "auto", + which means emoji will be displayed in some situations when + the host charset is UTF-8. + +set style warning-prefix STRING +set style error-prefix STRING + These commands control the prefix that is printed before warnings + and errors, respectively. This functionality is intended for use + with emoji display, and so the prefixes are only displayed if emoji + styling is enabled. + info linker-namespaces info linker-namespaces [[N]] Print information about the given linker namespace (identified as N), @@ -74,6 +90,18 @@ info sharedlibrary command are now for the full memory range allocated to the shared library. +info threads [-gid] [-stopped] [-running] [ID]... + If no threads match the given ID(s) or filter options, GDB now prints + + No threads matched. + + without printing the provided arguments. The newly added '-stopped' + option makes GDB list the stopped threads only. Similarly, + '-running' makes GDB list the running threads only. If both options + are given together, both stopped and running threads are listed. + These new flags can be useful to get a reduced list when there is a + large number of threads. + * GDB-internal Thread Local Storage (TLS) support ** Linux targets for the x86_64, aarch64, ppc64, s390x, and riscv @@ -17,7 +17,7 @@ Unpacking and Installation -- quick overview 'gdb-VERSION.tar.gz', where VERSION is the version of GDB. The GDB debugger sources, the generic GNU include -files, the BFD ("binary file description") library, the readline +files, the BFD ("Binary File Descriptor") library, the readline library, and other libraries all have directories of their own underneath the gdb-VERSION directory. The idea is that a variety of GNU tools can share a common copy of these things. Be aware of variation diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index b6cdcf0..a194ac8 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -2599,8 +2599,8 @@ aarch64_linux_fill_memtag_section (struct gdbarch *gdbarch, asection *osec) static_cast<int> (memtag_type::allocation))) { warning (_("Failed to read MTE tags from memory range [%s,%s)."), - phex_nz (start_address, sizeof (start_address)), - phex_nz (end_address, sizeof (end_address))); + phex_nz (start_address), + phex_nz (end_address)); return false; } diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index bf5b5d3..8d54e59 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -4236,7 +4236,7 @@ aarch64_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value) CORE_ADDR tag = value_as_address (tag_value); - return string_printf ("0x%s", phex_nz (tag, sizeof (tag))); + return string_printf ("0x%s", phex_nz (tag)); } /* See aarch64-tdep.h. */ diff --git a/gdb/auto-load.c b/gdb/auto-load.c index 4fc5b08..f33a8d0 100644 --- a/gdb/auto-load.c +++ b/gdb/auto-load.c @@ -41,6 +41,7 @@ #include <algorithm> #include "gdbsupport/pathstuff.h" #include "cli/cli-style.h" +#include "gdbsupport/selftest.h" /* The section to look in for auto-loaded scripts (in file formats that support sections). @@ -162,6 +163,67 @@ show_auto_load_dir (struct ui_file *file, int from_tty, value); } +/* Substitute all occurrences of string FROM by string TO in STRING. + STRING will be updated in place as needed. FROM needs to be + delimited by IS_DIR_SEPARATOR or DIRNAME_SEPARATOR (or be located + at the start or end of STRING. */ + +static void +substitute_path_component (std::string &string, std::string_view from, + std::string_view to) +{ + for (size_t s = 0;;) + { + s = string.find (from, s); + if (s == std::string::npos) + break; + + if ((s == 0 || IS_DIR_SEPARATOR (string[s - 1]) + || string[s - 1] == DIRNAME_SEPARATOR) + && (s + from.size () == string.size () + || IS_DIR_SEPARATOR (string[s + from.size ()]) + || string[s + from.size ()] == DIRNAME_SEPARATOR)) + { + string.replace (s, from.size (), to); + s += to.size (); + } + else + s++; + } +} + +#if GDB_SELF_TEST + +namespace selftests { +namespace subst_path { + +static void +test_substitute_path_component () +{ + auto test = [] (std::string s, const char *from, const char *to, + const char *expected) + { + substitute_path_component (s, from, to); + SELF_CHECK (s == expected); + }; + + test ("/abc/$def/g", "abc", "xyz", "/xyz/$def/g"); + test ("abc/$def/g", "abc", "xyz", "xyz/$def/g"); + test ("/abc/$def/g", "$def", "xyz", "/abc/xyz/g"); + test ("/abc/$def/g", "g", "xyz", "/abc/$def/xyz"); + test ("/abc/$def/g", "ab", "xyz", "/abc/$def/g"); + test ("/abc/$def/g", "def", "xyz", "/abc/$def/g"); + test ("/abc/$def/g", "abc", "abc", "/abc/$def/g"); + test ("/abc/$def/g", "abc", "", "//$def/g"); + test ("/abc/$def/g", "abc/$def", "xyz", "/xyz/g"); + test ("/abc/$def/abc", "abc", "xyz", "/xyz/$def/xyz"); +} + +} +} + +#endif /* GDB_SELF_TEST */ + /* Directory list safe to hold auto-loaded files. It is not checked for absolute paths but they are strongly recommended. It is initialized by _initialize_auto_load. */ @@ -178,16 +240,15 @@ static std::vector<gdb::unique_xmalloc_ptr<char>> auto_load_safe_path_vec; static std::vector<gdb::unique_xmalloc_ptr<char>> auto_load_expand_dir_vars (const char *string) { - char *s = xstrdup (string); - substitute_path_component (&s, "$datadir", gdb_datadir.c_str ()); - substitute_path_component (&s, "$debugdir", debug_file_directory.c_str ()); + std::string s = string; + substitute_path_component (s, "$datadir", gdb_datadir.c_str ()); + substitute_path_component (s, "$debugdir", debug_file_directory.c_str ()); - if (debug_auto_load && strcmp (s, string) != 0) - auto_load_debug_printf ("Expanded $-variables to \"%s\".", s); + if (debug_auto_load && s != string) + auto_load_debug_printf ("Expanded $-variables to \"%s\".", s.c_str ()); std::vector<gdb::unique_xmalloc_ptr<char>> dir_vec - = dirnames_to_char_ptr_vec (s); - xfree(s); + = dirnames_to_char_ptr_vec (s.c_str ()); return dir_vec; } @@ -1650,4 +1711,9 @@ When non-zero, debugging output for files of 'set auto-load ...'\n\ is displayed."), NULL, show_debug_auto_load, &setdebuglist, &showdebuglist); + +#if GDB_SELF_TEST + selftests::register_test ("substitute_path_component", + selftests::subst_path::test_substitute_path_component); +#endif } diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 23e5051..f44ac45 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -10289,7 +10289,7 @@ masked_watchpoint::print_recreate (struct ui_file *fp) const } gdb_printf (fp, " %s mask 0x%s", exp_string.get (), - phex (hw_wp_mask, sizeof (CORE_ADDR))); + phex (hw_wp_mask)); print_recreate_thread (fp); } diff --git a/gdb/bsd-uthread.c b/gdb/bsd-uthread.c index 51dafed..341aea9 100644 --- a/gdb/bsd-uthread.c +++ b/gdb/bsd-uthread.c @@ -536,7 +536,7 @@ bsd_uthread_target::pid_to_str (ptid_t ptid) if (ptid.tid () != 0) return string_printf ("process %d, thread 0x%s", ptid.pid (), - phex_nz (ptid.tid (), sizeof (ULONGEST))); + phex_nz (ptid.tid ())); return normal_pid_to_str (ptid); } diff --git a/gdb/bt-utils.c b/gdb/bt-utils.c index 8e78245..922402e 100644 --- a/gdb/bt-utils.c +++ b/gdb/bt-utils.c @@ -40,6 +40,17 @@ gdb_internal_backtrace_set_cmd (const char *args, int from_tty, #endif } +/* See bt-utils.h. */ + +void +sig_write (const char *msg) +{ + if (gdb_stderr == nullptr || gdb_stderr->fd () == -1) + std::ignore = ::write (2, msg, strlen (msg)); + else + gdb_stderr->write_async_safe (msg, strlen (msg)); +} + #ifdef GDB_PRINT_INTERNAL_BACKTRACE #ifdef GDB_PRINT_INTERNAL_BACKTRACE_USING_LIBBACKTRACE @@ -53,11 +64,6 @@ libbacktrace_error (void *data, const char *errmsg, int errnum) if (errnum < 0) return; - const auto sig_write = [] (const char *msg) -> void - { - gdb_stderr->write_async_safe (msg, strlen (msg)); - }; - sig_write ("error creating backtrace: "); sig_write (errmsg); if (errnum > 0) @@ -77,11 +83,6 @@ static int libbacktrace_print (void *data, uintptr_t pc, const char *filename, int lineno, const char *function) { - const auto sig_write = [] (const char *msg) -> void - { - gdb_stderr->write_async_safe (msg, strlen (msg)); - }; - /* Buffer to print addresses and line numbers into. An 8-byte address with '0x' prefix and a null terminator requires 20 characters. This also feels like it should be enough to represent line numbers in most @@ -128,16 +129,14 @@ gdb_internal_backtrace_1 () static void gdb_internal_backtrace_1 () { - const auto sig_write = [] (const char *msg) -> void - { - gdb_stderr->write_async_safe (msg, strlen (msg)); - }; - /* Allow up to 25 frames of backtrace. */ void *buffer[25]; int frames = backtrace (buffer, ARRAY_SIZE (buffer)); - backtrace_symbols_fd (buffer, frames, gdb_stderr->fd ()); + int fd = ((gdb_stderr == nullptr || gdb_stderr->fd () == -1) + ? 2 + : gdb_stderr->fd ()); + backtrace_symbols_fd (buffer, frames, fd); if (frames == ARRAY_SIZE (buffer)) sig_write (_("Backtrace might be incomplete.\n")); } @@ -171,17 +170,9 @@ gdb_internal_backtrace () return; #ifdef GDB_PRINT_INTERNAL_BACKTRACE - const auto sig_write = [] (const char *msg) -> void - { - gdb_stderr->write_async_safe (msg, strlen (msg)); - }; - sig_write (str_backtrace); - if (gdb_stderr->fd () > -1) - gdb_internal_backtrace_1 (); - else - sig_write (str_backtrace_unavailable); + gdb_internal_backtrace_1 (); sig_write ("---------------------\n"); #endif diff --git a/gdb/bt-utils.h b/gdb/bt-utils.h index ed381d8..c2fbe97 100644 --- a/gdb/bt-utils.h +++ b/gdb/bt-utils.h @@ -75,4 +75,9 @@ extern void gdb_internal_backtrace_set_cmd (const char *args, int from_tty, extern void gdb_internal_backtrace_init_str (); +/* Print MSG to gdb_stderr or stderr in a way that is safe to do from an + interrupt handler. */ + +extern void sig_write (const char *msg); + #endif /* GDB_BT_UTILS_H */ diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c index 30c7afb..e644127 100644 --- a/gdb/cli/cli-style.c +++ b/gdb/cli/cli-style.c @@ -23,6 +23,7 @@ #include "cli/cli-style.h" #include "source-cache.h" #include "observable.h" +#include "charset.h" /* True if styling is enabled. */ @@ -42,6 +43,10 @@ bool source_styling = true; bool disassembler_styling = true; +/* User-settable variable controlling emoji output. */ + +static auto_boolean emoji_styling = AUTO_BOOLEAN_AUTO; + /* Names of intensities; must correspond to ui_file_style::intensity. */ static const char * const cli_intensities[] = { @@ -410,6 +415,85 @@ show_style_disassembler (struct ui_file *file, int from_tty, gdb_printf (file, _("Disassembler output styling is disabled.\n")); } +/* Implement 'show style emoji'. */ + +static void +show_emoji_styling (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + if (emoji_styling == AUTO_BOOLEAN_TRUE) + gdb_printf (file, _("CLI emoji styling is enabled.\n")); + else if (emoji_styling == AUTO_BOOLEAN_FALSE) + gdb_printf (file, _("CLI emoji styling is disabled.\n")); + else + gdb_printf (file, _("CLI emoji styling is automatic (currently %s).\n"), + emojis_ok () ? _("enabled") : _("disabled")); +} + +/* See cli-style.h. */ + +bool +emojis_ok () +{ + if (!cli_styling || emoji_styling == AUTO_BOOLEAN_FALSE) + return false; + if (emoji_styling == AUTO_BOOLEAN_TRUE) + return true; + return strcmp (host_charset (), "UTF-8") == 0; +} + +/* See cli-style.h. */ + +void +no_emojis () +{ + emoji_styling = AUTO_BOOLEAN_FALSE; +} + +/* Emoji warning prefix. */ +static std::string warning_prefix = "⚠️ "; + +/* Implement 'show style warning-prefix'. */ + +static void +show_warning_prefix (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + gdb_printf (file, _("Warning prefix is \"%s\".\n"), + warning_prefix.c_str ()); +} + +/* See cli-style.h. */ + +void +print_warning_prefix (ui_file *file) +{ + if (emojis_ok ()) + gdb_puts (warning_prefix.c_str (), file); +} + +/* Emoji error prefix. */ +static std::string error_prefix = "❌️ "; + +/* Implement 'show style error-prefix'. */ + +static void +show_error_prefix (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + gdb_printf (file, _("Error prefix is \"%s\".\n"), + error_prefix.c_str ()); +} + +/* See cli-style.h. */ + +void +print_error_prefix (ui_file *file) +{ + if (emojis_ok ()) + gdb_puts (error_prefix.c_str (), file); +} + void _initialize_cli_style (); void _initialize_cli_style () @@ -431,6 +515,13 @@ If enabled, output to the terminal is styled."), set_style_enabled, show_style_enabled, &style_set_list, &style_show_list); + add_setshow_auto_boolean_cmd ("emoji", no_class, &emoji_styling, _("\ +Set whether emoji output is enabled."), _("\ +Show whether emoji output is enabled."), _("\ +If enabled, emojis may be displayed."), + nullptr, show_emoji_styling, + &style_set_list, &style_show_list); + add_setshow_boolean_cmd ("sources", no_class, &source_styling, _("\ Set whether source code styling is enabled."), _("\ Show whether source code styling is enabled."), _("\ @@ -627,4 +718,23 @@ coming from your source code."), &style_disasm_set_list); add_alias_cmd ("symbol", function_prefix_cmds.show, no_class, 0, &style_disasm_show_list); + + add_setshow_string_cmd ("warning-prefix", no_class, + &warning_prefix, + _("Set the warning prefix text."), + _("Show the warning prefix text."), + _("\ +The warning prefix text is displayed before any warning, when\n\ +emoji output is enabled."), + nullptr, show_warning_prefix, + &style_set_list, &style_show_list); + add_setshow_string_cmd ("error-prefix", no_class, + &error_prefix, + _("Set the error prefix text."), + _("Show the error prefix text."), + _("\ +The error prefix text is displayed before any error, when\n\ +emoji output is enabled."), + nullptr, show_error_prefix, + &style_set_list, &style_show_list); } diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h index 77f4ac2..b1a950a 100644 --- a/gdb/cli/cli-style.h +++ b/gdb/cli/cli-style.h @@ -190,4 +190,18 @@ private: bool m_old_value; }; +/* Return true if emoji styling is allowed. */ +extern bool emojis_ok (); + +/* Disable emoji styling. This is here so that Windows can disable + emoji when the console is in use. It shouldn't be called + elsewhere. */ +extern void no_emojis (); + +/* Print the warning prefix, if desired. */ +extern void print_warning_prefix (ui_file *file); + +/* Print the error prefix, if desired. */ +extern void print_error_prefix (ui_file *file); + #endif /* GDB_CLI_CLI_STYLE_H */ diff --git a/gdb/config.in b/gdb/config.in index 86ff67d..426947e 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -722,9 +722,6 @@ /* The size of `unsigned __int128', as computed by sizeof. */ #undef SIZEOF_UNSIGNED___INT128 -/* The size of `void *', as computed by sizeof. */ -#undef SIZEOF_VOID_P - /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at runtime. diff --git a/gdb/configure b/gdb/configure index c029c30..5ee1982 100755 --- a/gdb/configure +++ b/gdb/configure @@ -761,8 +761,6 @@ TARGET_OBS AMD_DBGAPI_LIBS AMD_DBGAPI_CFLAGS HAVE_GSTACK -ENABLE_BFD_64_BIT_FALSE -ENABLE_BFD_64_BIT_TRUE subdirs GDB_DATADIR DEBUGDIR @@ -934,7 +932,6 @@ with_relocated_sources with_auto_load_dir with_auto_load_safe_path enable_targets -enable_64_bit_bfd with_amd_dbgapi enable_tui enable_gdbtk @@ -1646,7 +1643,6 @@ Optional Features: --disable-nls do not use Native Language Support --enable-targets=TARGETS alternative target configurations - --enable-64-bit-bfd 64-bit support (on hosts with narrower word sizes) --enable-tui enable full-screen terminal user interface (TUI) --enable-gdbtk enable gdbtk graphical user interface (GUI) --enable-profiling enable profiling of GDB @@ -11503,7 +11499,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11506 "configure" +#line 11502 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11609,7 +11605,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11612 "configure" +#line 11608 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -24879,70 +24875,40 @@ fi -# Check whether --enable-64-bit-bfd was given. -if test "${enable_64_bit_bfd+set}" = set; then : - enableval=$enable_64_bit_bfd; case $enableval in #( - yes|no) : - ;; #( - *) : - as_fn_error $? "bad value ${enableval} for 64-bit-bfd option" "$LINENO" 5 ;; #( - *) : - ;; -esac -else - enable_64_bit_bfd=no -fi - - -if test "x$enable_64_bit_bfd" = "xno"; then : - # The cast to long int works around a bug in the HP C Compiler -# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects -# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. -# This bug is HP SR number 8606223364. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5 -$as_echo_n "checking size of void *... " >&6; } -if ${ac_cv_sizeof_void_p+:} false; then : - $as_echo_n "(cached) " >&6 -else - if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then : - -else - if test "$ac_cv_type_void_p" = yes; then - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (void *) -See \`config.log' for more details" "$LINENO" 5; } - else - ac_cv_sizeof_void_p=0 - fi -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 -$as_echo "$ac_cv_sizeof_void_p" >&6; } - - - -cat >>confdefs.h <<_ACEOF -#define SIZEOF_VOID_P $ac_cv_sizeof_void_p +# See whether 64-bit bfd lib has been enabled. +OLD_CPPFLAGS=$CPPFLAGS +# Put the old CPPFLAGS last, in case the user's CPPFLAGS point somewhere +# with bfd, with -I/foo/include. We always want our bfd. +CPPFLAGS="-I${srcdir}/../include -I../bfd -I${srcdir}/../bfd $CPPFLAGS" +# Note we cannot cache the result of this check because BFD64 may change +# when a secondary target has been added or removed and we have no access +# to this information here. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether BFD is 64-bit" >&5 +$as_echo_n "checking whether BFD is 64-bit... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include "bfd.h" +int +main () +{ +#ifdef BFD64 +HAVE_BFD64 +#endif + ; + return 0; +} _ACEOF - - - if test "x$ac_cv_sizeof_void_p" = "x8"; then : - enable_64_bit_bfd=yes -fi - -fi - - if test "x$enable_64_bit_bfd" = "xyes"; then - ENABLE_BFD_64_BIT_TRUE= - ENABLE_BFD_64_BIT_FALSE='#' +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "HAVE_BFD64" >/dev/null 2>&1; then : + have_64_bit_bfd=yes else - ENABLE_BFD_64_BIT_TRUE='#' - ENABLE_BFD_64_BIT_FALSE= + have_64_bit_bfd=no fi +rm -f conftest* - +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_64_bit_bfd" >&5 +$as_echo "$have_64_bit_bfd" >&6; } +CPPFLAGS=$OLD_CPPFLAGS # Provide defaults for some variables set by the per-host and per-target # configuration. @@ -24992,7 +24958,7 @@ fi done # Check whether this target needs 64-bit CORE_ADDR - if test x${enable_64_bit_bfd} = xno; then + if test x${have_64_bit_bfd} = xno; then . ${srcdir}/../bfd/config.bfd fi @@ -25005,19 +24971,10 @@ fi done if test x${all_targets} = xtrue; then - if test x${enable_64_bit_bfd} = xyes; then + if test x${have_64_bit_bfd} = xyes; then TARGET_OBS='$(ALL_TARGET_OBS) $(ALL_64_TARGET_OBS)' else - case ${host} in - mips*) - # If all targets were requested, but 64 bit bfd is not enabled, - # the build will fail. See PR 28684. - as_fn_error $? "--enable-targets=all requires --enable-64-bit-bfd" "$LINENO" 5 - ;; - *) - TARGET_OBS='$(ALL_TARGET_OBS)' - ;; - esac + TARGET_OBS='$(ALL_TARGET_OBS)' fi fi @@ -33946,10 +33903,6 @@ if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${ENABLE_BFD_64_BIT_TRUE}" && test -z "${ENABLE_BFD_64_BIT_FALSE}"; then - as_fn_error $? "conditional \"ENABLE_BFD_64_BIT\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi if test -z "${HAVE_PYTHON_TRUE}" && test -z "${HAVE_PYTHON_FALSE}"; then as_fn_error $? "conditional \"HAVE_PYTHON\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/gdb/configure.ac b/gdb/configure.ac index 7ba799b..5870a61 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -241,7 +241,7 @@ do done # Check whether this target needs 64-bit CORE_ADDR - if test x${enable_64_bit_bfd} = xno; then + if test x${have_64_bit_bfd} = xno; then . ${srcdir}/../bfd/config.bfd fi @@ -254,19 +254,10 @@ do done if test x${all_targets} = xtrue; then - if test x${enable_64_bit_bfd} = xyes; then + if test x${have_64_bit_bfd} = xyes; then TARGET_OBS='$(ALL_TARGET_OBS) $(ALL_64_TARGET_OBS)' else - case ${host} in - mips*) - # If all targets were requested, but 64 bit bfd is not enabled, - # the build will fail. See PR 28684. - AC_MSG_ERROR([--enable-targets=all requires --enable-64-bit-bfd]) - ;; - *) - TARGET_OBS='$(ALL_TARGET_OBS)' - ;; - esac + TARGET_OBS='$(ALL_TARGET_OBS)' fi fi diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 72cdcee..e9b3068 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -85,7 +85,7 @@ hppa*-*-*) i[34567]86-*-*) cpu_obs="${i386_tobjs}" - if test "x$enable_64_bit_bfd" = "xyes"; then + if test "x$have_64_bit_bfd" = "xyes"; then cpu_obs="${amd64_tobjs} ${cpu_obs}" fi ;; @@ -284,7 +284,7 @@ hppa*-*-openbsd*) i[34567]86-*-darwin*) # Target: Darwin/i386 gdb_target_obs="i386-darwin-tdep.o solib-darwin.o" - if test "x$enable_64_bit_bfd" = "xyes"; then + if test "x$have_64_bit_bfd" = "xyes"; then # Target: GNU/Linux x86-64 gdb_target_obs="amd64-darwin-tdep.o ${gdb_target_obs}" fi @@ -319,7 +319,7 @@ i[34567]86-*-linux*) linux-tdep.o linux-record.o \ arch/i386-linux-tdesc.o \ arch/x86-linux-tdesc-features.o" - if test "x$enable_64_bit_bfd" = "xyes"; then + if test "x$have_64_bit_bfd" = "xyes"; then # Target: GNU/Linux x86-64 gdb_target_obs="amd64-linux-tdep.o \ arch/amd64-linux-tdesc.o ${gdb_target_obs}" @@ -580,7 +580,7 @@ sparc-*-linux*) sparc-linux-tdep.o solib-svr4.o symfile-mem.o \ linux-tdep.o \ ravenscar-thread.o sparc-ravenscar-thread.o" - if test "x$enable_64_bit_bfd" = "xyes"; then + if test "x$have_64_bit_bfd" = "xyes"; then # Target: GNU/Linux UltraSPARC gdb_target_obs="sparc64-tdep.o \ sparc64-linux-tdep.o ${gdb_target_obs}" diff --git a/gdb/contrib/codespell-log.sh b/gdb/contrib/codespell-log.sh new file mode 100755 index 0000000..10780f8 --- /dev/null +++ b/gdb/contrib/codespell-log.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# Copyright (C) 2025 Free Software Foundation, Inc. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Script to be used as pre-commit commit-msg hook to spell-check the commit +# log using codespell. +# +# Using codespell directly as a pre-commit commit-msg hook has the drawback +# that: +# - if codespell fails, the commit fails +# - if the commit log mentions a typo correction, it'll require a +# codespell:ignore annotation. +# +# This script works around these problems by treating codespell output as a +# hint, and ignoring codespell exit status. +# +# Implementation note: rather than using codespell directly, this script uses +# pre-commit to call codespell, because it allows us to control the codespell +# version that is used. + +# Exit on error. +set -e + +# Initialize temporary file names. +cfg="" +output="" + +cleanup() +{ + for f in "$cfg" "$output"; do + if [ "$f" != "" ]; then + rm -f "$f" + fi + done +} + +# Schedule cleanup. +trap cleanup EXIT + +# Create temporary files. +cfg=$(mktemp) +output=$(mktemp) + +gen_cfg () +{ + cat > "$1" <<EOF +repos: +- repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + name: codespell-log-internal + stages: [manual] + args: [--config, gdb/contrib/setup.cfg] +EOF +} + +# Generate pre-commit configuration file. +gen_cfg "$cfg" + +# Setup pre-commit command to run. +cmd=(pre-commit \ + run \ + -c "$cfg" \ + codespell \ + --hook-stage manual \ + --files "$@") + +# Run pre-commit command. +if "${cmd[@]}" \ + > "$output" \ + 2>&1; then + # Command succeeded quietly, we're done. + exit 0 +fi + +# Command failed quietly, now show the output. +# +# Simply doing "cat $output" doesn't produce colored output, so we just +# run the command again, that should be fast enough. +# +# Ignore codespell exit status. +"${cmd[@]}" || true diff --git a/gdb/contrib/license-check-new-files.sh b/gdb/contrib/license-check-new-files.sh new file mode 100755 index 0000000..710afa1 --- /dev/null +++ b/gdb/contrib/license-check-new-files.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2025 Free Software Foundation, Inc. +# +# This file is part of GDB. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This program requires the python modules GitPython (git) and scancode-toolkit. +# It builds a list of all the newly added files to the repository and scans +# each file for a license, printing it to the terminal. If "--skip" is used, +# it will only output non-"common" licenses, e.g., omitting "GPL-3.0-or-later". +# This makes it a little bit easier to detect any possible new licenses. +# +# Example: +# bash$ cd /path/to/binutils-gdb/gdb +# bash$ ./contrib/license-check-new-files.sh -s gdb-15-branchpoint gdb-16-branchpoint +# Scanning directories gdb*/... +# gdb/contrib/common-misspellings.txt: no longer in repo? +# gdb/contrib/spellcheck.sh: no longer in repo? +# gdbsupport/unordered_dense.h: MIT + +import os +import sys +import argparse +from pathlib import PurePath +from git import Repo +from scancode import api + +# A list of "common" licenses. If "--skip" is used, any file +# with a license in this list will be omitted from the output. +COMMON_LICENSES = ["GPL-2.0-or-later", "GPL-3.0-or-later"] + +# Default list of directories to scan. Default scans are limited to +# gdb-specific git directories because much of the rest of binutils-gdb +# is actually owned by other projects/packages. +DEFAULT_SCAN_DIRS = "gdb*" + + +# Get the commit object associated with the string commit CSTR +# from the git repository REPO. +# +# Returns the object or prints an error and exits. +def get_commit(repo, cstr): + try: + return repo.commit(cstr) + except: + print(f'unknown commit "{cstr}"') + sys.exit(2) + + +# Uses scancode-toolkit package to scan FILE's licenses. +# Returns the full license dict from scancode on success or +# propagates any exceptions. +def get_licenses_for_file(file): + return api.get_licenses(file) + + +# Helper function to print FILE to the terminal if skipping +# common licenses. +def skip_print_file(skip, file): + if skip: + print(f"{file}: ", end="") + + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument("from_commit") + parser.add_argument("to_commit") + parser.add_argument( + "-s", "--skip", help="skip common licenses in output", action="store_true" + ) + parser.add_argument( + "-p", + "--paths", + help=f'paths to scan (default is "{DEFAULT_SCAN_DIRS}")', + type=str, + default=DEFAULT_SCAN_DIRS, + ) + args = parser.parse_args() + + # Commit boundaries to search for new files + from_commit = args.from_commit + to_commit = args.to_commit + + # Get the list of new files from git. Try the current directory, + # looping up to the root attempting to find a valid git repository. + path = PurePath(os.getcwd()) + paths = list(path.parents) + paths.insert(0, path) + for dir in paths: + try: + repo = Repo(dir) + break + except: + pass + + if dir == path.parents[-1]: + print(f'not a git repository (or any parent up to mount point "{dir}")') + sys.exit(2) + + # Get from/to commits + fc = get_commit(repo, from_commit) + tc = get_commit(repo, to_commit) + + # Loop over new files + paths = [str(dir) for dir in args.paths.split(",")] + print(f'Scanning directories {",".join(f"{s}/" for s in paths)}...') + for file in fc.diff(tc, paths=paths).iter_change_type("A"): + filename = file.a_path + if not args.skip: + print(f"checking licenses for {filename}... ", end="", flush=True) + try: + f = dir.joinpath(dir, filename).as_posix() + lic = get_licenses_for_file(f) + if len(lic["license_clues"]) > 1: + print("multiple licenses detected") + elif ( + not args.skip + or lic["detected_license_expression_spdx"] not in COMMON_LICENSES + ): + skip_print_file(args.skip, filename) + print(f"{lic['detected_license_expression_spdx']}") + except OSError: + # Likely hit a file that was added to the repo and subsequently removed. + skip_print_file(args.skip, filename) + print("no longer in repo?") + except KeyboardInterrupt: + print("interrupted") + break + except Exception as e: + # If scanning fails, there is little we can do but print an error. + skip_print_file(args.skip, filename) + print(e) + + +if __name__ == "__main__": + main(sys.argv) diff --git a/gdb/contrib/setup.cfg b/gdb/contrib/setup.cfg index 670a850..d6be386 100644 --- a/gdb/contrib/setup.cfg +++ b/gdb/contrib/setup.cfg @@ -7,3 +7,12 @@ ignore-words = gdb/contrib/codespell-ignore-words.txt # Ignore all URLs. uri-ignore-words-list = * + +# This codespell issue ( +# https://github.com/codespell-project/codespell/issues/3381 ) reports the +# need to have support for ignoring blocks of code. +# A suggestion there is to use ignore-multiline-regex, which does work, but +# has a bug: using -w drops all newlines in the updated files ( +# https://github.com/codespell-project/codespell/issues/3642 ). +# Consequently, disabled. To be enabled when the bug is fixed. +#ignore-multiline-regex = codespell:ignore-begin.*codespell:ignore-end diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y index 10f6e2d..d4ab98c 100644 --- a/gdb/cp-name-parser.y +++ b/gdb/cp-name-parser.y @@ -2047,6 +2047,11 @@ cp_demangled_name_to_comp (const char *demangled_name, auto result = std::make_unique<demangle_parse_info> (); cpname_state state (demangled_name, result.get ()); + /* Note that we can't set yydebug here, as is done in the other + parsers. Bison implements yydebug as a global, even with a pure + parser, and this parser is run from worker threads. So, changing + yydebug causes TSan reports. If you need to debug this parser, + debug gdb and set the global from the outer gdb. */ if (yyparse (&state)) { if (state.global_errmsg && errmsg) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 362e6e7..05f5502 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -3807,7 +3807,7 @@ Thread 1 "main" received signal SIGINT, Interrupt. @table @code @anchor{info_threads} @kindex info threads -@item info threads @r{[}-gid@r{]} @r{[}@var{thread-id-list}@r{]} +@item info threads @r{[}-gid@r{]} @r{[}-stopped@r{]} @r{[}-running@r{]} @r{[}@var{thread-id-list}@r{]} Display information about one or more threads. With no arguments displays information about all threads. You can specify the list of @@ -3857,6 +3857,14 @@ If you're debugging multiple inferiors, @value{GDBN} displays thread IDs using the qualified @var{inferior-num}.@var{thread-num} format. Otherwise, only @var{thread-num} is shown. +If you specify the @samp{-stopped} option, @value{GDBN} filters the +output of the command to print the stopped threads only. Similarly, +if you specify the @samp{-running} option, @value{GDBN} filters the +output to print the running threads only. These options can be +helpful to reduce the output list if there is a large number of +threads. If you specify both options, @value{GDBN} prints both +stopped and running threads. + If you specify the @samp{-gid} option, @value{GDBN} displays a column indicating each thread's global thread ID: @@ -27974,6 +27982,19 @@ value, then @value{GDBN} will change this to @samp{off} at startup. @item show style enabled Show the current state of styling. +@item set style emoji @samp{auto|on|off} +Enable or disable the use of emoji. On most hosts, the default is +@samp{auto}, meaning that emoji will only be used if the host +character set is @samp{UTF-8}; however, on Windows the default is +@samp{off} when using the console. Note that disabling styling as a +whole will also prevent emoji display. + +Currently, emoji are printed whenever @value{GDBN} reports an error or +a warning. + +@item show style emoji +Show the current state of emoji output. + @item set style sources @samp{on|off} Enable or disable source code styling. This affects whether source code, such as the output of the @code{list} command, is styled. The @@ -27990,6 +28011,17 @@ then it will be used. @item show style sources Show the current state of source code styling. +@item set style warning-prefix +@itemx show style warning-prefix +@itemx set style error-prefix +@itemx show style error-prefix + +These commands control the prefix that is printed before warnings and +errors, respectively. This functionality is intended for use with +emoji display, and so the prefixes are only displayed if emoji styling +is enabled. The defaults are the warning sign emoji for warnings, and +and the cross mark emoji for errors. + @item set style tui-current-position @samp{on|off} Enable or disable styling of the source and assembly code highlighted by the TUI's current position indicator. The default is @samp{off}. @@ -41632,6 +41664,13 @@ into remote agent bytecodes and display them as a disassembled list. This command is useful for debugging the agent version of dynamic printf (@pxref{Dynamic Printf}). +@kindex maint canonicalize +@item maint canonicalize @var{name} +Print the canonical form of @var{name}, a C@t{++} name. Because a +C@t{++} name may have multiple possible spellings, @value{GDBN} +computes a canonical form of a name for internal use. For example, +@code{short int} and @code{short} are two ways to name the same type. + @kindex maint info breakpoints @anchor{maint info breakpoints} @item maint info breakpoints diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi index c6808ef..c6d889f 100644 --- a/gdb/doc/guile.texi +++ b/gdb/doc/guile.texi @@ -3899,6 +3899,9 @@ Return string to change terminal's color to this. If @var{is_foreground} is @code{#t}, then the returned sequence will change foreground color. Otherwise, the returned sequence will change background color. + +If styling is currently disabled (@pxref{Output Styling,,@kbd{set style +enabled}}), then this procedure will return an empty string. @end deffn When color is initialized, its color space must be specified. The diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index afbd62f..7bb6503 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -7101,6 +7101,9 @@ Returns string to change terminal's color to this. If @var{is_foreground} is @code{True}, then the returned sequence will change foreground color. Otherwise, the returned sequence will change background color. + +If styling is currently disabled (@pxref{Output Styling,,@kbd{set style +enabled}}), then this method will return an empty string. @end defun When color is initialized, its color space must be specified. The diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c index da51a8c..5d7fc46 100644 --- a/gdb/dwarf2/cooked-index-worker.c +++ b/gdb/dwarf2/cooked-index-worker.c @@ -20,6 +20,7 @@ #include "dwarf2/cooked-index-worker.h" #include "dwarf2/cooked-index.h" #include "gdbsupport/thread-pool.h" +#include "maint.h" #include "run-on-main-thread.h" #include "event-top.h" #include "exceptions.h" @@ -244,8 +245,12 @@ cooked_index_worker::write_to_cache (const cooked_index *idx) void cooked_index_worker::done_reading () { - for (auto &one_result : m_results) - m_all_parents_map.add_map (*one_result.get_parent_map ()); + { + scoped_time_it time_it ("DWARF add parent map"); + + for (auto &one_result : m_results) + m_all_parents_map.add_map (*one_result.get_parent_map ()); + } dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; cooked_index *table diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c index 7948ffa..2c7e31e 100644 --- a/gdb/dwarf2/cooked-index.c +++ b/gdb/dwarf2/cooked-index.c @@ -21,6 +21,7 @@ #include "dwarf2/read.h" #include "dwarf2/stringify.h" #include "event-top.h" +#include "maint.h" #include "observable.h" #include "run-on-main-thread.h" #include "gdbsupport/task-group.h" @@ -101,7 +102,11 @@ cooked_index::set_contents () { auto this_shard = shard.get (); const parent_map_map *parent_maps = m_state->get_parent_map_map (); - finalizers.add_task ([=] () { this_shard->finalize (parent_maps); }); + finalizers.add_task ([=] () + { + scoped_time_it time_it ("DWARF finalize worker"); + this_shard->finalize (parent_maps); + }); } finalizers.start (); diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c index b8b66cf..c093984 100644 --- a/gdb/dwarf2/cooked-indexer.c +++ b/gdb/dwarf2/cooked-indexer.c @@ -89,7 +89,7 @@ cooked_indexer::ensure_cu_exists (cutu_reader *reader, /* Lookups for type unit references are always in the CU, and cross-CU references will crash. */ if (reader->cu ()->per_cu->is_dwz == is_dwz - && reader->cu ()->header.offset_in_cu_p (sect_off)) + && reader->cu ()->header.offset_in_unit_p (sect_off)) return reader; dwarf2_per_objfile *per_objfile = reader->cu ()->per_objfile; @@ -109,20 +109,20 @@ cooked_indexer::ensure_cu_exists (cutu_reader *reader, cutu_reader *result = m_index_storage->get_reader (per_cu); if (result == nullptr) { - cutu_reader new_reader (*per_cu, *per_objfile, nullptr, nullptr, false, - language_minimal, - &m_index_storage->get_abbrev_table_cache ()); - - if (new_reader.is_dummy () || new_reader.top_level_die () == nullptr - || !new_reader.top_level_die ()->has_children) + const abbrev_table_cache &abbrev_table_cache + = m_index_storage->get_abbrev_table_cache (); + auto new_reader + = std::make_unique<cutu_reader> (*per_cu, *per_objfile, nullptr, + nullptr, false, language_minimal, + &abbrev_table_cache); + + if (new_reader->is_dummy ()) return nullptr; - auto copy = std::make_unique<cutu_reader> (std::move (new_reader)); - result = m_index_storage->preserve (std::move (copy)); + result = m_index_storage->preserve (std::move (new_reader)); } - if (result->is_dummy () || result->top_level_die () == nullptr - || !result->top_level_die ()->has_children) + if (result->is_dummy ()) return nullptr; if (for_scanning) diff --git a/gdb/dwarf2/cu.h b/gdb/dwarf2/cu.h index 5683291..9f76789 100644 --- a/gdb/dwarf2/cu.h +++ b/gdb/dwarf2/cu.h @@ -21,12 +21,14 @@ #define GDB_DWARF2_CU_H #include "buildsym.h" -#include "dwarf2/comp-unit-head.h" +#include "dwarf2/unit-head.h" #include <optional> #include "language.h" #include "gdbsupport/unordered_set.h" #include "dwarf2/die.h" +struct field_info; + /* Type used for delaying computation of method physnames. See comments for compute_delayed_physnames. */ struct delayed_method_info @@ -99,8 +101,17 @@ struct dwarf2_cu void add_dependence (dwarf2_per_cu *ref_per_cu) { m_dependencies.emplace (ref_per_cu); } + /* Find the DIE at section offset SECT_OFF. + + Return nullptr if not found. */ + die_info *find_die (sect_offset sect_off) const + { + auto it = die_hash.find (sect_off); + return it != die_hash.end () ? *it : nullptr; + } + /* The header of the compilation unit. */ - struct comp_unit_head header; + struct unit_head header; /* Base address of this compilation unit. */ std::optional<unrelocated_addr> base_address; @@ -390,6 +401,15 @@ public: .debug_str_offsets section (8 or 4, depending on the address size). */ std::optional<ULONGEST> str_offsets_base; + /* We may encounter a DIE where a property refers to a field in some + outer type. For example, in Ada, an array length may depend on a + field in some outer record. In this case, we need to be able to + stash a pointer to the 'struct field' into the appropriate + dynamic property baton. However, the fields aren't created until + the type has been fully processed, so we need a temporary + back-link to this object. */ + struct field_info *field_info = nullptr; + /* Mark used when releasing cached dies. */ bool m_mark : 1; diff --git a/gdb/dwarf2/dwz.c b/gdb/dwarf2/dwz.c index 583103b..59fe8e4 100644 --- a/gdb/dwarf2/dwz.c +++ b/gdb/dwarf2/dwz.c @@ -56,35 +56,37 @@ dwz_file::read_string (struct objfile *objfile, LONGEST str_offset) /* A helper function to find the sections for a .dwz file. */ static void -locate_dwz_sections (struct objfile *objfile, bfd *abfd, asection *sectp, - dwz_file *dwz_file) +locate_dwz_sections (objfile *objfile, dwz_file &dwz_file) { - dwarf2_section_info *sect = nullptr; + for (asection *sec : gdb_bfd_sections (dwz_file.dwz_bfd)) + { + dwarf2_section_info *sect = nullptr; - /* Note that we only support the standard ELF names, because .dwz + /* Note that we only support the standard ELF names, because .dwz is ELF-only (at the time of writing). */ - if (dwarf2_elf_names.abbrev.matches (sectp->name)) - sect = &dwz_file->abbrev; - else if (dwarf2_elf_names.info.matches (sectp->name)) - sect = &dwz_file->info; - else if (dwarf2_elf_names.str.matches (sectp->name)) - sect = &dwz_file->str; - else if (dwarf2_elf_names.line.matches (sectp->name)) - sect = &dwz_file->line; - else if (dwarf2_elf_names.macro.matches (sectp->name)) - sect = &dwz_file->macro; - else if (dwarf2_elf_names.gdb_index.matches (sectp->name)) - sect = &dwz_file->gdb_index; - else if (dwarf2_elf_names.debug_names.matches (sectp->name)) - sect = &dwz_file->debug_names; - else if (dwarf2_elf_names.types.matches (sectp->name)) - sect = &dwz_file->types; - - if (sect != nullptr) - { - sect->s.section = sectp; - sect->size = bfd_section_size (sectp); - sect->read (objfile); + if (dwarf2_elf_names.abbrev.matches (sec->name)) + sect = &dwz_file.abbrev; + else if (dwarf2_elf_names.info.matches (sec->name)) + sect = &dwz_file.info; + else if (dwarf2_elf_names.str.matches (sec->name)) + sect = &dwz_file.str; + else if (dwarf2_elf_names.line.matches (sec->name)) + sect = &dwz_file.line; + else if (dwarf2_elf_names.macro.matches (sec->name)) + sect = &dwz_file.macro; + else if (dwarf2_elf_names.gdb_index.matches (sec->name)) + sect = &dwz_file.gdb_index; + else if (dwarf2_elf_names.debug_names.matches (sec->name)) + sect = &dwz_file.debug_names; + else if (dwarf2_elf_names.types.matches (sec->name)) + sect = &dwz_file.types; + + if (sect != nullptr) + { + sect->s.section = sec; + sect->size = bfd_section_size (sec); + sect->read (objfile); + } } } @@ -392,9 +394,7 @@ dwz_file::read_dwz_file (dwarf2_per_objfile *per_objfile) dwz_file_up result (new dwz_file (std::move (dwz_bfd))); - for (asection *sec : gdb_bfd_sections (result->dwz_bfd)) - locate_dwz_sections (per_objfile->objfile, result->dwz_bfd.get (), - sec, result.get ()); + locate_dwz_sections (per_objfile->objfile, *result); gdb_bfd_record_inclusion (per_bfd->obfd, result->dwz_bfd.get ()); bfd_cache_close (result->dwz_bfd.get ()); diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c index de162b7..4652306 100644 --- a/gdb/dwarf2/line-header.c +++ b/gdb/dwarf2/line-header.c @@ -17,7 +17,7 @@ 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/comp-unit-head.h" +#include "dwarf2/unit-head.h" #include "dwarf2/leb.h" #include "dwarf2/line-header.h" #include "dwarf2/read.h" @@ -95,7 +95,7 @@ dwarf2_statement_list_fits_in_line_number_section_complaint (void) static LONGEST read_checked_initial_length_and_offset (bfd *abfd, const gdb_byte *buf, - const struct comp_unit_head *cu_header, + const struct unit_head *cu_header, unsigned int *bytes_read, unsigned int *offset_size) { @@ -253,11 +253,10 @@ read_formatted_entries (dwarf2_per_objfile *per_objfile, bfd *abfd, /* See line-header.h. */ line_header_up -dwarf_decode_line_header (sect_offset sect_off, bool is_dwz, - dwarf2_per_objfile *per_objfile, - struct dwarf2_section_info *section, - const struct comp_unit_head *cu_header, - const char *comp_dir) +dwarf_decode_line_header (sect_offset sect_off, bool is_dwz, + dwarf2_per_objfile *per_objfile, + struct dwarf2_section_info *section, + const unit_head *cu_header, const char *comp_dir) { const gdb_byte *line_ptr; unsigned int bytes_read, offset_size; diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h index 36385b6..e6f9ea9 100644 --- a/gdb/dwarf2/line-header.h +++ b/gdb/dwarf2/line-header.h @@ -218,7 +218,7 @@ file_entry::include_dir (const line_header *lh) const extern line_header_up dwarf_decode_line_header (sect_offset sect_off, bool is_dwz, dwarf2_per_objfile *per_objfile, - struct dwarf2_section_info *section, const struct comp_unit_head *cu_header, + struct dwarf2_section_info *section, const struct unit_head *cu_header, const char *comp_dir); #endif /* GDB_DWARF2_LINE_HEADER_H */ diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c index e1a5fdd..fa1b4eb 100644 --- a/gdb/dwarf2/loc.c +++ b/gdb/dwarf2/loc.c @@ -1732,11 +1732,10 @@ dwarf2_evaluate_property (const dynamic_prop *prop, *value = prop->const_val (); return true; - case PROP_ADDR_OFFSET: + case PROP_FIELD: { const dwarf2_property_baton *baton = prop->baton (); const struct property_addr_info *pinfo; - struct value *val; for (pinfo = addr_stack; pinfo != NULL; pinfo = pinfo->next) { @@ -1747,14 +1746,40 @@ dwarf2_evaluate_property (const dynamic_prop *prop, } if (pinfo == NULL) error (_("cannot find reference address for offset property")); - if (pinfo->valaddr.data () != NULL) - val = value_from_contents - (baton->offset_info.type, - pinfo->valaddr.data () + baton->offset_info.offset); - else - val = value_at (baton->offset_info.type, - pinfo->addr + baton->offset_info.offset); - *value = value_as_address (val); + + struct field resolved_field = *baton->field; + resolve_dynamic_field (resolved_field, pinfo, initial_frame); + + /* Storage for memory if we need to read it. */ + gdb::byte_vector memory; + const gdb_byte *bytes = pinfo->valaddr.data (); + if (bytes == nullptr) + { + int bitpos = resolved_field.loc_bitpos (); + int bitsize = resolved_field.bitsize (); + if (bitsize == 0) + bitsize = check_typedef (resolved_field.type ())->length () * 8; + + /* Read just the minimum number of bytes needed to satisfy + unpack_field_as_long. So, update the resolved field's + starting offset to remove any unnecessary leading + bytes. */ + int byte_offset = bitpos / 8; + + bitpos %= 8; + resolved_field.set_loc_bitpos (bitpos); + + /* Make sure to include any remaining bit offset in the + size computation, in case the value straddles a + byte. */ + int byte_length = align_up (bitsize + bitpos, 8) / 8; + memory.resize (byte_length); + + read_memory (pinfo->addr + byte_offset, memory.data (), + byte_length); + bytes = memory.data (); + } + *value = unpack_field_as_long (bytes, &resolved_field); return true; } diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h index 30c528b..ebe5c31 100644 --- a/gdb/dwarf2/loc.h +++ b/gdb/dwarf2/loc.h @@ -99,7 +99,7 @@ struct property_addr_info /* If not NULL, a pointer to the info for the object containing the object described by this node. */ - struct property_addr_info *next; + const property_addr_info *next; }; /* Converts a dynamic property into a static one. FRAME is the frame in which @@ -167,6 +167,9 @@ struct dwarf2_locexpr_baton directly. */ bool is_reference; + /* True if this object is actually a dwarf2_field_location_baton. */ + bool is_field_location; + /* The objfile that was used when creating this. */ dwarf2_per_objfile *per_objfile; @@ -175,6 +178,23 @@ struct dwarf2_locexpr_baton dwarf2_per_cu *per_cu; }; +/* If the DWARF location for a field used DW_AT_bit_size, then an + object of this type is created to represent the field location. + This is then used to apply the bit offset after computing the + field's byte offset. Objects of this type always set the + 'is_field_location' member in dwarf2_locexpr_baton. See also + apply_bit_offset_to_field. */ + +struct dwarf2_field_location_baton : public dwarf2_locexpr_baton +{ + /* The bit offset, coming from DW_AT_bit_offset. */ + LONGEST bit_offset; + + /* The DW_AT_byte_size of the field. If no explicit byte size was + specified, this is 0. */ + LONGEST explicit_byte_size; +}; + struct dwarf2_loclist_baton { /* The initial base address for the location list, based on the compilation @@ -202,23 +222,6 @@ struct dwarf2_loclist_baton unsigned char dwarf_version; }; -/* The baton used when a dynamic property is an offset to a parent - type. This can be used, for instance, then the bound of an array - inside a record is determined by the value of another field inside - that record. */ - -struct dwarf2_offset_baton -{ - /* The offset from the parent type where the value of the property - is stored. In the example provided above, this would be the offset - of the field being used as the array bound. */ - LONGEST offset; - - /* The type of the object whose property is dynamic. In the example - provided above, this would the array's index type. */ - struct type *type; -}; - /* A dynamic property is either expressed as a single location expression or a location list. If the property is an indirection, pointing to another die, keep track of the targeted type in PROPERTY_TYPE. @@ -241,8 +244,8 @@ struct dwarf2_property_baton /* Location list to be evaluated in the context of PROPERTY_TYPE. */ struct dwarf2_loclist_baton loclist; - /* The location is an offset to PROPERTY_TYPE. */ - struct dwarf2_offset_baton offset_info; + /* The location is stored in a field of PROPERTY_TYPE. */ + struct field *field; }; }; diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 2523ca8..891e10f 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -31,7 +31,7 @@ #include "dwarf2/abbrev.h" #include "dwarf2/aranges.h" #include "dwarf2/attribute.h" -#include "dwarf2/comp-unit-head.h" +#include "dwarf2/unit-head.h" #include "dwarf2/cooked-index-worker.h" #include "dwarf2/cooked-indexer.h" #include "dwarf2/cu.h" @@ -52,6 +52,7 @@ #include "event-top.h" #include "exceptions.h" #include "gdbsupport/task-group.h" +#include "maint.h" #include "symtab.h" #include "gdbtypes.h" #include "objfiles.h" @@ -279,7 +280,7 @@ struct dwo_sections struct dwarf2_section_info str; struct dwarf2_section_info str_offsets; /* In the case of a virtual DWO file, these two are unused. */ - struct dwarf2_section_info info; + std::vector<dwarf2_section_info> infos; std::vector<dwarf2_section_info> types; }; @@ -697,6 +698,12 @@ struct field_info we're reading. */ std::vector<variant_part_builder> variant_parts; + /* All the field batons that must be updated. This is only used + when a type's property depends on a field of this structure; for + example in Ada when an array's length may come from a field of an + enclosing record. */ + gdb::unordered_map<dwarf2_property_baton *, sect_offset> field_batons; + /* Return the total number of fields (including baseclasses). */ int nfields () const { @@ -733,9 +740,6 @@ show_dwarf_synchronous (struct ui_file *file, int from_tty, /* local function prototypes */ -static void build_type_psymtabs_reader (cutu_reader *reader, - cooked_index_worker_result *storage); - static void var_decode_location (struct attribute *attr, struct symbol *sym, struct dwarf2_cu *cu); @@ -748,9 +752,9 @@ static unrelocated_addr read_addr_index (struct dwarf2_cu *cu, static sect_offset read_abbrev_offset (dwarf2_per_objfile *per_objfile, dwarf2_section_info *, sect_offset); -static const char *read_indirect_string - (dwarf2_per_objfile *per_objfile, bfd *, const gdb_byte *, - const struct comp_unit_head *, unsigned int *); +static const char *read_indirect_string (dwarf2_per_objfile *per_objfile, bfd *, + const gdb_byte *, const unit_head *, + unsigned int *); static unrelocated_addr read_addr_index_from_leb128 (struct dwarf2_cu *, const gdb_byte *, @@ -1324,7 +1328,7 @@ dwarf2_per_bfd::locate_sections (asection *sectp, bfd_size_type size = sectp->size; warning (_("Discarding section %s which has an invalid size (%s) " "[in module %s]"), - bfd_section_name (sectp), phex_nz (size, sizeof (size)), + bfd_section_name (sectp), phex_nz (size), this->filename ()); } else if (names.info.matches (sectp->name)) @@ -2391,109 +2395,6 @@ read_abbrev_offset (dwarf2_per_objfile *per_objfile, return (sect_offset) read_offset (abfd, info_ptr, offset_size); } -/* A helper for create_dwo_debug_types_hash_table. Read types from SECTION - and fill them into DWO_FILE's type unit hash table. It will process only - type units, therefore DW_UT_type. */ - -void -cutu_reader::create_dwo_debug_type_hash_table (dwarf2_per_bfd *per_bfd, - dwo_file *dwo_file, - dwarf2_section_info *section, - rcuh_kind section_kind) -{ - struct dwarf2_section_info *abbrev_section; - bfd *abfd; - const gdb_byte *info_ptr, *end_ptr; - - abbrev_section = &dwo_file->sections.abbrev; - - dwarf_read_debug_printf ("Reading %s for %s", - section->get_name (), - abbrev_section->get_file_name ()); - - info_ptr = section->buffer; - - if (info_ptr == NULL) - return; - - /* We can't set abfd until now because the section may be empty or - not present, in which case the bfd is unknown. */ - abfd = section->get_bfd_owner (); - - /* We don't use cutu_reader here because we don't need to read - any dies: the signature is in the header. */ - - end_ptr = info_ptr + section->size; - while (info_ptr < end_ptr) - { - const gdb_byte *ptr = info_ptr; - struct comp_unit_head header; - unsigned int length; - - sect_offset sect_off = (sect_offset) (ptr - section->buffer); - - /* Initialize it due to a false compiler warning. */ - header.signature = -1; - header.type_cu_offset_in_tu = (cu_offset) -1; - - /* We need to read the type's signature in order to build the hash - table, but we don't need anything else just yet. */ - - ptr = read_and_check_comp_unit_head (&header, section, abbrev_section, - ptr, section_kind); - - length = header.get_length_with_initial (); - - /* Skip dummy type units. */ - if (ptr >= info_ptr + length - || peek_abbrev_code (abfd, ptr) == 0 - || (header.unit_type != DW_UT_type - && header.unit_type != DW_UT_split_type)) - { - info_ptr += length; - continue; - } - - dwo_unit *dwo_tu = OBSTACK_ZALLOC (&per_bfd->obstack, dwo_unit); - dwo_tu->dwo_file = dwo_file; - dwo_tu->signature = header.signature; - dwo_tu->type_offset_in_tu = header.type_cu_offset_in_tu; - dwo_tu->section = section; - dwo_tu->sect_off = sect_off; - dwo_tu->length = length; - - auto [it, inserted] = dwo_file->tus.emplace (dwo_tu); - if (!inserted) - complaint (_("debug type entry at offset %s is duplicate to" - " the entry at offset %s, signature %s"), - sect_offset_str (sect_off), - sect_offset_str ((*it)->sect_off), - hex_string (header.signature)); - - dwarf_read_debug_printf_v (" offset %s, signature %s", - sect_offset_str (sect_off), - hex_string (header.signature)); - - info_ptr += length; - } -} - -/* Create the hash table of all entries in the .debug_types - (or .debug_types.dwo) section(s). - DWO_FILE is a pointer to the DWO file object. - - Note: This function processes DWO files only, not DWP files. */ - -void -cutu_reader::create_dwo_debug_types_hash_table - (dwarf2_per_bfd *per_bfd, dwo_file *dwo_file, - gdb::array_view<dwarf2_section_info> type_sections) -{ - for (dwarf2_section_info §ion : type_sections) - create_dwo_debug_type_hash_table (per_bfd, dwo_file, §ion, - rcuh_kind::TYPE); -} - /* Add an entry for signature SIG to per_bfd->signatured_types. */ static signatured_type_set::iterator @@ -2795,9 +2696,9 @@ cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit, { signatured_type *sig_type = (struct signatured_type *) per_cu; - m_info_ptr = read_and_check_comp_unit_head (&cu->header, section, - dwo_abbrev_section, - m_info_ptr, rcuh_kind::TYPE); + m_info_ptr = read_and_check_unit_head (&cu->header, section, + dwo_abbrev_section, m_info_ptr, + ruh_kind::TYPE); /* This is not an assert because it can be caused by bad debug info. */ if (sig_type->signature != cu->header.signature) @@ -2814,7 +2715,7 @@ cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit, /* For DWOs coming from DWP files, we don't know the CU length nor the type's offset in the TU until now. */ dwo_unit->length = cu->header.get_length_with_initial (); - dwo_unit->type_offset_in_tu = cu->header.type_cu_offset_in_tu; + dwo_unit->type_offset_in_tu = cu->header.type_offset_in_tu; /* Establish the type offset that can be used to lookup the type. For DWO files, we don't know it until now. */ @@ -2823,10 +2724,9 @@ cutu_reader::read_cutu_die_from_dwo (dwarf2_cu *cu, dwo_unit *dwo_unit, } else { - m_info_ptr - = read_and_check_comp_unit_head (&cu->header, section, - dwo_abbrev_section, m_info_ptr, - rcuh_kind::COMPILE); + m_info_ptr = read_and_check_unit_head (&cu->header, section, + dwo_abbrev_section, m_info_ptr, + ruh_kind::COMPILE); gdb_assert (dwo_unit->sect_off == cu->header.sect_off); /* For DWOs coming from DWP files, we don't know the CU length until now. */ @@ -3044,26 +2944,26 @@ cutu_reader::cutu_reader (dwarf2_per_cu &this_cu, } /* Get the header. */ - if (to_underlying (cu->header.first_die_cu_offset) != 0 && !rereading_dwo_cu) + if (to_underlying (cu->header.first_die_offset_in_unit) != 0 + && !rereading_dwo_cu) { /* We already have the header, there's no need to read it in again. */ - m_info_ptr += to_underlying (cu->header.first_die_cu_offset); + m_info_ptr += to_underlying (cu->header.first_die_offset_in_unit); } else { if (this_cu.is_debug_types) { - m_info_ptr - = read_and_check_comp_unit_head (&cu->header, section, - abbrev_section, m_info_ptr, - rcuh_kind::TYPE); + m_info_ptr = read_and_check_unit_head (&cu->header, section, + abbrev_section, m_info_ptr, + ruh_kind::TYPE); /* Since per_cu is the first member of struct signatured_type, we can go from a pointer to one to a pointer to the other. */ sig_type = (struct signatured_type *) &this_cu; gdb_assert (sig_type->signature == cu->header.signature); gdb_assert (sig_type->type_offset_in_tu - == cu->header.type_cu_offset_in_tu); + == cu->header.type_offset_in_tu); gdb_assert (this_cu.sect_off == cu->header.sect_off); /* LENGTH has not been set yet for type units if we're @@ -3076,10 +2976,9 @@ cutu_reader::cutu_reader (dwarf2_per_cu &this_cu, } else { - m_info_ptr - = read_and_check_comp_unit_head (&cu->header, section, - abbrev_section, m_info_ptr, - rcuh_kind::COMPILE); + m_info_ptr = read_and_check_unit_head (&cu->header, section, + abbrev_section, m_info_ptr, + ruh_kind::COMPILE); gdb_assert (this_cu.sect_off == cu->header.sect_off); this_cu.set_length (cu->header.get_length_with_initial ()); @@ -3211,11 +3110,11 @@ cutu_reader::cutu_reader (dwarf2_per_cu &this_cu, m_info_ptr = section->buffer + to_underlying (this_cu.sect_off); const gdb_byte *begin_info_ptr = m_info_ptr; - m_info_ptr = read_and_check_comp_unit_head (&m_new_cu->header, section, - abbrev_section, m_info_ptr, - (this_cu.is_debug_types - ? rcuh_kind::TYPE - : rcuh_kind::COMPILE)); + m_info_ptr = read_and_check_unit_head (&m_new_cu->header, section, + abbrev_section, m_info_ptr, + (this_cu.is_debug_types + ? ruh_kind::TYPE + : ruh_kind::COMPILE)); m_new_cu->str_offsets_base = parent_cu.str_offsets_base; m_new_cu->addr_base = parent_cu.addr_base; @@ -3289,33 +3188,130 @@ get_type_unit_group_key (struct dwarf2_cu *cu, const struct attribute *stmt_list return {cu->dwo_unit, static_cast<sect_offset> (line_offset)}; } -/* Subroutine of dwarf2_build_psymtabs_hard to simplify it. - Process compilation unit THIS_CU for a psymtab. */ +/* A subclass of cooked_index_worker that handles scanning + .debug_info. */ -static void -process_psymtab_comp_unit (dwarf2_per_cu *this_cu, - dwarf2_per_objfile *per_objfile, - cooked_index_worker_result *storage) +class cooked_index_worker_debug_info : public cooked_index_worker +{ +public: + cooked_index_worker_debug_info (dwarf2_per_objfile *per_objfile) + : cooked_index_worker (per_objfile) + { + gdb_assert (is_main_thread ()); + + struct objfile *objfile = per_objfile->objfile; + dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; + + dwarf_read_debug_printf ("Building psymtabs of objfile %s ...", + objfile_name (objfile)); + + per_bfd->map_info_sections (objfile); + } + +private: + void do_reading () override; + + /* Print collected type unit statistics. */ + + void print_tu_stats (dwarf2_per_objfile *per_objfile) + { + struct tu_stats *tu_stats = &per_objfile->per_bfd->tu_stats; + + dwarf_read_debug_printf ("Type unit statistics:"); + dwarf_read_debug_printf (" %d TUs", tu_stats->nr_tus); + dwarf_read_debug_printf (" %d uniq abbrev tables", + tu_stats->nr_uniq_abbrev_tables); + dwarf_read_debug_printf (" %d symtabs from stmt_list entries", + tu_stats->nr_symtabs); + dwarf_read_debug_printf (" %d symtab sharers", + tu_stats->nr_symtab_sharers); + dwarf_read_debug_printf (" %d type units without a stmt_list", + tu_stats->nr_stmt_less_type_units); + dwarf_read_debug_printf (" %d all_type_units reallocs", + tu_stats->nr_all_type_units_reallocs); + } + + void print_stats () override + { + if (dwarf_read_debug > 0) + print_tu_stats (m_per_objfile); + + if (dwarf_read_debug > 1) + { + dwarf_read_debug_printf_v ("Final m_all_parents_map:"); + m_all_parents_map.dump (m_per_objfile->per_bfd); + } + } + + /* After the last DWARF-reading task has finished, this function + does the remaining work to finish the scan. */ + void done_reading () override; + + /* An iterator for the comp units. */ + using unit_iterator = std::vector<dwarf2_per_cu_up>::iterator; + + /* Process a batch of CUs. This may be called multiple times in + separate threads. TASK_NUMBER indicates which task this is -- + the result is stored in that slot of M_RESULTS. */ + void process_units (size_t task_number, unit_iterator first, + unit_iterator end); + + /* Process unit THIS_CU. */ + void process_unit (dwarf2_per_cu *this_cu, dwarf2_per_objfile *per_objfile, + cooked_index_worker_result *storage); + + /* Process all type units existing in PER_OBJFILE::PER_BFD::ALL_UNITS. */ + void process_type_units (dwarf2_per_objfile *per_objfile, + cooked_index_worker_result *storage); + + /* Process the type unit wrapped in READER. */ + void process_type_unit (cutu_reader *reader, + cooked_index_worker_result *storage); + + /* Process all type units of all DWO files. + + This is needed in case a TU was emitted without its skeleton. + Note: This can't be done until we know what all the DWO files are. */ + void process_skeletonless_type_units (dwarf2_per_objfile *per_objfile, + cooked_index_worker_result *storage); + + /* Process the type unit represented by DWO_UNIT. */ + void process_skeletonless_type_unit (dwo_unit *dwo_unit, + dwarf2_per_objfile *per_objfile, + cooked_index_worker_result *storage); + + /* A storage object for "leftovers" -- see the 'start' method, but + essentially things not parsed during the normal CU parsing + passes. */ + cooked_index_worker_result m_index_storage; +}; + +void +cooked_index_worker_debug_info::process_unit + (dwarf2_per_cu *this_cu, dwarf2_per_objfile *per_objfile, + cooked_index_worker_result *storage) { cutu_reader *reader = storage->get_reader (this_cu); if (reader == nullptr) { - cutu_reader new_reader (*this_cu, *per_objfile, nullptr, nullptr, false, - language_minimal, - &storage->get_abbrev_table_cache ()); + const abbrev_table_cache &abbrev_table_cache + = storage->get_abbrev_table_cache (); + auto new_reader = std::make_unique<cutu_reader> (*this_cu, *per_objfile, + nullptr, nullptr, false, + language_minimal, + &abbrev_table_cache); - if (new_reader.cu () == nullptr || new_reader.is_dummy ()) + if (new_reader->is_dummy ()) return; - auto copy = std::make_unique<cutu_reader> (std::move (new_reader)); - reader = storage->preserve (std::move (copy)); + reader = storage->preserve (std::move (new_reader)); } - if (reader->top_level_die () == nullptr || reader->is_dummy ()) + if (reader->is_dummy ()) return; if (this_cu->is_debug_types) - build_type_psymtabs_reader (reader, storage); + process_type_unit (reader, storage); else if (reader->top_level_die ()->tag != DW_TAG_partial_unit) { bool nope = false; @@ -3328,11 +3324,9 @@ process_psymtab_comp_unit (dwarf2_per_cu *this_cu, } } -/* Reader function for build_type_psymtabs. */ - -static void -build_type_psymtabs_reader (cutu_reader *reader, - cooked_index_worker_result *storage) +void +cooked_index_worker_debug_info::process_type_unit + (cutu_reader *reader, cooked_index_worker_result *storage) { struct dwarf2_cu *cu = reader->cu (); dwarf2_per_cu *per_cu = cu->per_cu; @@ -3366,26 +3360,9 @@ struct tu_abbrev_offset sect_offset abbrev_offset; }; -/* Efficiently read all the type units. - - The efficiency is because we sort TUs by the abbrev table they use and - only read each abbrev table once. In one program there are 200K TUs - sharing 8K abbrev tables. - - The main purpose of this function is to support building the - dwarf2_per_objfile->per_bfd->type_unit_groups table. - TUs typically share the DW_AT_stmt_list of the CU they came from, so we - can collapse the search space by grouping them by stmt_list. - The savings can be significant, in the same program from above the 200K TUs - share 8K stmt_list tables. - - FUNC is expected to call get_type_unit_group, which will create the - struct type_unit_group if necessary and add it to - dwarf2_per_objfile->per_bfd->type_unit_groups. */ - -static void -build_type_psymtabs (dwarf2_per_objfile *per_objfile, - cooked_index_worker_result *storage) +void +cooked_index_worker_debug_info::process_type_units + (dwarf2_per_objfile *per_objfile, cooked_index_worker_result *storage) { struct tu_stats *tu_stats = &per_objfile->per_bfd->tu_stats; abbrev_table_up abbrev_table; @@ -3451,38 +3428,14 @@ build_type_psymtabs (dwarf2_per_objfile *per_objfile, abbrev_table.get (), nullptr, false, language_minimal); if (!reader.is_dummy ()) - build_type_psymtabs_reader (&reader, storage); + process_type_unit (&reader, storage); } } -/* Print collected type unit statistics. */ - -static void -print_tu_stats (dwarf2_per_objfile *per_objfile) -{ - struct tu_stats *tu_stats = &per_objfile->per_bfd->tu_stats; - - dwarf_read_debug_printf ("Type unit statistics:"); - dwarf_read_debug_printf (" %d TUs", tu_stats->nr_tus); - dwarf_read_debug_printf (" %d uniq abbrev tables", - tu_stats->nr_uniq_abbrev_tables); - dwarf_read_debug_printf (" %d symtabs from stmt_list entries", - tu_stats->nr_symtabs); - dwarf_read_debug_printf (" %d symtab sharers", - tu_stats->nr_symtab_sharers); - dwarf_read_debug_printf (" %d type units without a stmt_list", - tu_stats->nr_stmt_less_type_units); - dwarf_read_debug_printf (" %d all_type_units reallocs", - tu_stats->nr_all_type_units_reallocs); -} - -/* Traversal function for process_skeletonless_type_units. - Read a TU in a DWO file and build partial symbols for it. */ - -static void -process_skeletonless_type_unit (dwo_unit *dwo_unit, - dwarf2_per_objfile *per_objfile, - cooked_index_worker_result *storage) +void +cooked_index_worker_debug_info::process_skeletonless_type_unit + (dwo_unit *dwo_unit, dwarf2_per_objfile *per_objfile, + cooked_index_worker_result *storage) { dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; @@ -3504,17 +3457,15 @@ process_skeletonless_type_unit (dwo_unit *dwo_unit, cutu_reader reader (**sig_type_it, *per_objfile, nullptr, nullptr, false, language_minimal); if (!reader.is_dummy ()) - build_type_psymtabs_reader (&reader, storage); + process_type_unit (&reader, storage); } -/* Scan all TUs of DWO files, verifying we've processed them. - This is needed in case a TU was emitted without its skeleton. - Note: This can't be done until we know what all the DWO files are. */ - -static void -process_skeletonless_type_units (dwarf2_per_objfile *per_objfile, - cooked_index_worker_result *storage) +void +cooked_index_worker_debug_info::process_skeletonless_type_units + (dwarf2_per_objfile *per_objfile, cooked_index_worker_result *storage) { + scoped_time_it time_it ("DWARF skeletonless type units"); + /* Skeletonless TUs in DWP files without .gdb_index is not supported yet. */ if (per_objfile->per_bfd->dwp_file == nullptr) for (const dwo_file_up &file : per_objfile->per_bfd->dwo_files) @@ -3522,64 +3473,10 @@ process_skeletonless_type_units (dwarf2_per_objfile *per_objfile, process_skeletonless_type_unit (unit, per_objfile, storage); } -/* A subclass of cooked_index_worker that handles scanning - .debug_info. */ - -class cooked_index_worker_debug_info : public cooked_index_worker -{ -public: - cooked_index_worker_debug_info (dwarf2_per_objfile *per_objfile) - : cooked_index_worker (per_objfile) - { - gdb_assert (is_main_thread ()); - - struct objfile *objfile = per_objfile->objfile; - dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; - - dwarf_read_debug_printf ("Building psymtabs of objfile %s ...", - objfile_name (objfile)); - - per_bfd->map_info_sections (objfile); - } - -private: - - void do_reading () override; - - void print_stats () override - { - if (dwarf_read_debug > 0) - print_tu_stats (m_per_objfile); - if (dwarf_read_debug > 1) - { - dwarf_read_debug_printf_v ("Final m_all_parents_map:"); - m_all_parents_map.dump (m_per_objfile->per_bfd); - } - } - - /* After the last DWARF-reading task has finished, this function - does the remaining work to finish the scan. */ - void done_reading () override; - - /* An iterator for the comp units. */ - using unit_iterator = std::vector<dwarf2_per_cu_up>::iterator; - - /* Process a batch of CUs. This may be called multiple times in - separate threads. TASK_NUMBER indicates which task this is -- - the result is stored in that slot of M_RESULTS. */ - void process_cus (size_t task_number, unit_iterator first, - unit_iterator end); - - /* A storage object for "leftovers" -- see the 'start' method, but - essentially things not parsed during the normal CU parsing - passes. */ - 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_units (size_t task_number, + unit_iterator first, + unit_iterator end) { SCOPE_EXIT { bfd_thread_cleanup (); }; @@ -3594,7 +3491,7 @@ cooked_index_worker_debug_info::process_cus (size_t task_number, try { - process_psymtab_comp_unit (per_cu, m_per_objfile, &thread_storage); + process_unit (per_cu, m_per_objfile, &thread_storage); } catch (gdb_exception &except) { @@ -3624,7 +3521,7 @@ cooked_index_worker_debug_info::do_reading () dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; create_all_units (m_per_objfile); - build_type_psymtabs (m_per_objfile, &m_index_storage); + process_type_units (m_per_objfile, &m_index_storage); if (!per_bfd->debug_aranges.empty ()) read_addrmap_from_aranges (m_per_objfile, &per_bfd->debug_aranges, @@ -3677,7 +3574,8 @@ cooked_index_worker_debug_info::do_reading () gdb_assert (iter != last); workers.add_task ([this, task_count, iter, last] () { - process_cus (task_count, iter, last); + scoped_time_it time_it ("DWARF indexing worker"); + process_units (task_count, iter, last); }); ++task_count; @@ -3694,7 +3592,7 @@ read_comp_units_from_section (dwarf2_per_objfile *per_objfile, struct dwarf2_section_info *abbrev_section, unsigned int is_dwz, signatured_type_set &sig_types, - rcuh_kind section_kind) + ruh_kind section_kind) { const gdb_byte *info_ptr; struct objfile *objfile = per_objfile->objfile; @@ -3714,9 +3612,9 @@ read_comp_units_from_section (dwarf2_per_objfile *per_objfile, sect_offset sect_off = (sect_offset) (info_ptr - section->buffer); - comp_unit_head cu_header; - read_and_check_comp_unit_head (&cu_header, section, abbrev_section, - info_ptr, section_kind); + unit_head cu_header; + read_and_check_unit_head (&cu_header, section, abbrev_section, info_ptr, + section_kind); unsigned int length = cu_header.get_length_with_initial (); @@ -3729,7 +3627,7 @@ read_comp_units_from_section (dwarf2_per_objfile *per_objfile, = per_bfd->allocate_signatured_type (section, sect_off, length, is_dwz, cu_header.signature); signatured_type *sig_ptr = sig_type.get (); - sig_type->type_offset_in_tu = cu_header.type_cu_offset_in_tu; + sig_type->type_offset_in_tu = cu_header.type_offset_in_tu; this_cu.reset (sig_type.release ()); auto inserted = sig_types.emplace (sig_ptr).second; @@ -3771,17 +3669,17 @@ create_all_units (dwarf2_per_objfile *per_objfile) for (dwarf2_section_info §ion : per_objfile->per_bfd->infos) read_comp_units_from_section (per_objfile, §ion, &per_objfile->per_bfd->abbrev, 0, sig_types, - rcuh_kind::COMPILE); + ruh_kind::COMPILE); for (dwarf2_section_info §ion : per_objfile->per_bfd->types) read_comp_units_from_section (per_objfile, §ion, &per_objfile->per_bfd->abbrev, 0, sig_types, - rcuh_kind::TYPE); + ruh_kind::TYPE); dwz_file *dwz = per_objfile->per_bfd->get_dwz_file (); if (dwz != NULL) { read_comp_units_from_section (per_objfile, &dwz->info, &dwz->abbrev, 1, - sig_types, rcuh_kind::COMPILE); + sig_types, ruh_kind::COMPILE); if (!dwz->types.empty ()) { @@ -6313,79 +6211,131 @@ lookup_dwo_file (dwarf2_per_bfd *per_bfd, const char *dwo_name, return it != per_bfd->dwo_files.end () ? it->get() : nullptr; } -/* Create the dwo_units for the CUs in a DWO_FILE. - Note: This function processes DWO files only, not DWP files. */ - void -cutu_reader::create_dwo_cus_hash_table (dwarf2_cu *cu, dwo_file &dwo_file) +cutu_reader::create_dwo_unit_hash_tables (dwo_file &dwo_file, + dwarf2_cu &skeleton_cu, + dwarf2_section_info §ion, + ruh_kind section_kind) { - dwarf2_per_objfile *per_objfile = cu->per_objfile; - dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; - const gdb_byte *info_ptr, *end_ptr; - auto §ion = dwo_file.sections.info; + dwarf2_per_objfile &per_objfile = *skeleton_cu.per_objfile; + dwarf2_per_bfd &per_bfd = *per_objfile.per_bfd; - info_ptr = section.buffer; + const gdb_byte *info_ptr = section.buffer; if (info_ptr == NULL) return; - dwarf_read_debug_printf ("Reading %s for %s:", - section.get_name (), + dwarf_read_debug_printf ("Reading %s for %s:", section.get_name (), section.get_file_name ()); - end_ptr = info_ptr + section.size; + const gdb_byte *end_ptr = info_ptr + section.size; + while (info_ptr < end_ptr) { sect_offset sect_off = (sect_offset) (info_ptr - section.buffer); + unit_head header; + dwarf2_section_info *abbrev_section = &dwo_file.sections.abbrev; + const gdb_byte *info_ptr_post_header + = read_and_check_unit_head (&header, §ion, abbrev_section, + info_ptr, section_kind); - /* The length of the CU gets set by the cutu_reader just below. */ - dwarf2_per_cu per_cu (per_bfd, §ion, sect_off, 0 /* length */, - false /* is_dwz */); - cutu_reader reader (per_cu, *per_objfile, language_minimal, - *cu, dwo_file); - - info_ptr += per_cu.length (); + unsigned int length = header.get_length_with_initial (); + info_ptr += length; - if (reader.is_dummy()) + /* Skip dummy units. */ + if (info_ptr_post_header >= info_ptr + || peek_abbrev_code (section.get_bfd_owner (), + info_ptr_post_header) == 0) continue; - /* DWARF 5 .debug_info.dwo sections may contain some type units. Skip - everything that is not a compile unit. */ - if (const auto ut = reader.cu ()->header.unit_type; - ut != DW_UT_compile && ut != DW_UT_split_compile) + if (header.unit_type != DW_UT_compile + && header.unit_type != DW_UT_split_compile + && header.unit_type != DW_UT_type + && header.unit_type != DW_UT_split_type) continue; - std::optional<ULONGEST> signature - = lookup_dwo_id (reader.cu (), reader.top_level_die ()); - if (!signature.has_value ()) + ULONGEST signature; + + /* For type units (all DWARF versions) and DWARF 5 compile units, the + signature/DWO ID is already available in the header. For compile + units in DWARF < 5, we need to read the DW_AT_GNU_dwo_id attribute + from the top-level DIE. + + For DWARF < 5 compile units, the unit type will be set to DW_UT_compile + by read_and_check_comp_unit_head. */ + if (header.version < 5 && header.unit_type == DW_UT_compile) { - complaint (_(DWARF_ERROR_PREFIX - "debug entry at offset %s is missing its dwo_id" - " [in module %s]"), - sect_offset_str (sect_off), - dwo_file.dwo_name.c_str ()); - continue; + /* The length of the CU is not necessary. */ + dwarf2_per_cu per_cu (&per_bfd, §ion, sect_off, length, + false /* is_dwz */); + cutu_reader reader (per_cu, per_objfile, language_minimal, + skeleton_cu, dwo_file); + + std::optional<ULONGEST> opt_signature + = lookup_dwo_id (reader.cu (), reader.top_level_die ()); + + if (!opt_signature.has_value ()) + { + complaint (_ (DWARF_ERROR_PREFIX + "debug entry at offset %s is missing its dwo_id" + " [in module %s]"), + sect_offset_str (sect_off), + dwo_file.dwo_name.c_str ()); + continue; + } + + signature = *opt_signature; } + else + signature = header.signature; - dwo_unit *dwo_unit = OBSTACK_ZALLOC (&per_bfd->obstack, struct dwo_unit); + dwo_unit *dwo_unit = OBSTACK_ZALLOC (&per_bfd.obstack, struct dwo_unit); + /* Set the fields common to compile and type units. */ dwo_unit->dwo_file = &dwo_file; - dwo_unit->signature = *signature; + dwo_unit->signature = signature; dwo_unit->section = §ion; dwo_unit->sect_off = sect_off; - dwo_unit->length = per_cu.length (); + dwo_unit->length = length; + + switch (header.unit_type) + { + case DW_UT_compile: + case DW_UT_split_compile: + { + dwarf_read_debug_printf (" compile unit at offset %s, dwo_id %s", + sect_offset_str (sect_off), + hex_string (dwo_unit->signature)); + + auto [it, inserted] = dwo_file.cus.emplace (dwo_unit); + if (!inserted) + complaint (_("debug cu entry at offset %s is duplicate to" + " the entry at offset %s, signature %s"), + sect_offset_str (sect_off), + sect_offset_str ((*it)->sect_off), + hex_string (dwo_unit->signature)); + break; + } + + case DW_UT_type: + case DW_UT_split_type: + { + dwo_unit->type_offset_in_tu = header.type_offset_in_tu; - dwarf_read_debug_printf (" offset %s, dwo_id %s", - sect_offset_str (sect_off), - hex_string (dwo_unit->signature)); + dwarf_read_debug_printf (" type unit at offset %s, signature %s", + sect_offset_str (sect_off), + hex_string (dwo_unit->signature)); - auto [it, inserted] = dwo_file.cus.emplace (dwo_unit); - if (!inserted) - complaint (_("debug cu entry at offset %s is duplicate to" - " the entry at offset %s, signature %s"), - sect_offset_str (sect_off), - sect_offset_str ((*it)->sect_off), - hex_string (dwo_unit->signature)); + auto [it, inserted] = dwo_file.tus.emplace (dwo_unit); + if (!inserted) + complaint (_("debug type entry at offset %s is duplicate to" + " the entry at offset %s, signature %s"), + sect_offset_str (sect_off), + sect_offset_str ((*it)->sect_off), + hex_string (header.signature)); + break; + } + } } } @@ -7562,47 +7512,66 @@ cutu_reader::open_dwo_file (dwarf2_per_bfd *per_bfd, const char *file_name, size of each of the DWO debugging sections we are interested in. */ void -cutu_reader::locate_dwo_sections (struct objfile *objfile, bfd *abfd, - asection *sectp, dwo_sections *dwo_sections) +cutu_reader::locate_dwo_sections (objfile *objfile, dwo_file &dwo_file) { const struct dwop_section_names *names = &dwop_section_names; + dwo_sections &dwo_sections = dwo_file.sections; + bool complained_about_macro_already = false; + + for (asection *sec : gdb_bfd_sections (dwo_file.dbfd)) + { + struct dwarf2_section_info *dw_sect = nullptr; + + if (names->abbrev_dwo.matches (sec->name)) + dw_sect = &dwo_sections.abbrev; + else if (names->info_dwo.matches (sec->name)) + dw_sect = &dwo_sections.infos.emplace_back (dwarf2_section_info {}); + else if (names->line_dwo.matches (sec->name)) + dw_sect = &dwo_sections.line; + else if (names->loc_dwo.matches (sec->name)) + dw_sect = &dwo_sections.loc; + else if (names->loclists_dwo.matches (sec->name)) + dw_sect = &dwo_sections.loclists; + else if (names->macinfo_dwo.matches (sec->name)) + dw_sect = &dwo_sections.macinfo; + else if (names->macro_dwo.matches (sec->name)) + { + /* gcc versions <= 13 generate multiple .debug_macro.dwo sections with + some unresolved links between them. It's not usable, so do as if + there were not there. */ + if (!complained_about_macro_already) + { + if (dwo_sections.macro.s.section == nullptr) + dw_sect = &dwo_sections.macro; + else + { + warning (_("Multiple .debug_macro.dwo sections found in " + "%s, ignoring them."), dwo_file.dbfd->filename); - struct dwarf2_section_info *dw_sect = nullptr; - - if (names->abbrev_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->abbrev; - else if (names->info_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->info; - else if (names->line_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->line; - else if (names->loc_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->loc; - else if (names->loclists_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->loclists; - else if (names->macinfo_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->macinfo; - else if (names->macro_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->macro; - else if (names->rnglists_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->rnglists; - else if (names->str_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->str; - else if (names->str_offsets_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->str_offsets; - else if (names->types_dwo.matches (sectp->name)) - { - struct dwarf2_section_info type_section; - - memset (&type_section, 0, sizeof (type_section)); - dwo_sections->types.push_back (type_section); - dw_sect = &dwo_sections->types.back (); - } + dwo_sections.macro = dwarf2_section_info {}; + complained_about_macro_already = true; + } + } + } + else if (names->rnglists_dwo.matches (sec->name)) + dw_sect = &dwo_sections.rnglists; + else if (names->str_dwo.matches (sec->name)) + dw_sect = &dwo_sections.str; + else if (names->str_offsets_dwo.matches (sec->name)) + dw_sect = &dwo_sections.str_offsets; + else if (names->types_dwo.matches (sec->name)) + dw_sect = &dwo_sections.types.emplace_back (dwarf2_section_info {}); + + if (dw_sect != nullptr) + { + /* Make sure we don't overwrite a section info that has been filled in + already. */ + gdb_assert (!dw_sect->readin); - if (dw_sect != nullptr) - { - dw_sect->s.section = sectp; - dw_sect->size = bfd_section_size (sectp); - dw_sect->read (objfile); + dw_sect->s.section = sec; + dw_sect->size = bfd_section_size (sec); + dw_sect->read (objfile); + } } } @@ -7615,7 +7584,6 @@ cutu_reader::open_and_init_dwo_file (dwarf2_cu *cu, const char *dwo_name, const char *comp_dir) { dwarf2_per_objfile *per_objfile = cu->per_objfile; - dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; gdb_bfd_ref_ptr dbfd = open_dwo_file (per_objfile->per_bfd, dwo_name, comp_dir); @@ -7631,19 +7599,18 @@ cutu_reader::open_and_init_dwo_file (dwarf2_cu *cu, const char *dwo_name, dwo_file->comp_dir = comp_dir; dwo_file->dbfd = std::move (dbfd); - for (asection *sec : gdb_bfd_sections (dwo_file->dbfd)) - this->locate_dwo_sections (per_objfile->objfile, dwo_file->dbfd.get (), sec, - &dwo_file->sections); + this->locate_dwo_sections (per_objfile->objfile, *dwo_file); - create_dwo_cus_hash_table (cu, *dwo_file); + /* There is normally just one .debug_info.dwo section in a DWO file. But when + building with -fdebug-types-section, gcc produces multiple .debug_info.dwo + sections. One for each produced type unit and one for the compile unit. + This is not expected, but we can easily enough deal with what gcc + produces. This behavior has been observed with gcc 14.2.1. */ + for (dwarf2_section_info §ion : dwo_file->sections.infos) + create_dwo_unit_hash_tables (*dwo_file, *cu, section, ruh_kind::COMPILE); - if (cu->header.version < 5) - create_dwo_debug_types_hash_table (per_bfd, dwo_file.get (), - dwo_file->sections.types); - else - create_dwo_debug_type_hash_table (per_bfd, dwo_file.get (), - &dwo_file->sections.info, - rcuh_kind::COMPILE); + for (dwarf2_section_info §ion : dwo_file->sections.types) + create_dwo_unit_hash_tables (*dwo_file, *cu, section, ruh_kind::TYPE); dwarf_read_debug_printf ("DWO file found: %s", dwo_name); @@ -7679,6 +7646,10 @@ dwarf2_locate_common_dwp_sections (struct objfile *objfile, bfd *abfd, if (dw_sect != nullptr) { + /* Make sure we don't overwrite a section info that has been filled in + already. */ + gdb_assert (!dw_sect->readin); + dw_sect->s.section = sectp; dw_sect->size = bfd_section_size (sectp); dw_sect->read (objfile); @@ -7724,6 +7695,10 @@ dwarf2_locate_v2_dwp_sections (struct objfile *objfile, bfd *abfd, if (dw_sect != nullptr) { + /* Make sure we don't overwrite a section info that has been filled in + already. */ + gdb_assert (!dw_sect->readin); + dw_sect->s.section = sectp; dw_sect->size = bfd_section_size (sectp); dw_sect->read (objfile); @@ -7767,6 +7742,10 @@ dwarf2_locate_v5_dwp_sections (struct objfile *objfile, bfd *abfd, if (dw_sect != nullptr) { + /* Make sure we don't overwrite a section info that has been filled in + already. */ + gdb_assert (!dw_sect->readin); + dw_sect->s.section = sectp; dw_sect->size = bfd_section_size (sectp); dw_sect->read (objfile); @@ -8784,7 +8763,8 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) struct dwarf2_locexpr_baton *dlbaton; struct dwarf_block *block = attr->as_block (); - dlbaton = XOBNEW (&objfile->objfile_obstack, struct dwarf2_locexpr_baton); + dlbaton = OBSTACK_ZALLOC (&objfile->objfile_obstack, + struct dwarf2_locexpr_baton); dlbaton->data = block->data; dlbaton->size = block->size; dlbaton->per_objfile = per_objfile; @@ -8883,7 +8863,7 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) parameter->kind = CALL_SITE_PARAMETER_PARAM_OFFSET; sect_offset sect_off = origin->get_ref_die_offset (); - if (!cu->header.offset_in_cu_p (sect_off)) + if (!cu->header.offset_in_unit_p (sect_off)) { /* As DW_OP_GNU_parameter_ref uses CU-relative offset this binding can be done only inside one CU. Such referenced DIE @@ -9251,7 +9231,7 @@ dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag, { dwarf2_per_objfile *per_objfile = cu->per_objfile; struct objfile *objfile = per_objfile->objfile; - struct comp_unit_head *cu_header = &cu->header; + unit_head *cu_header = &cu->header; bfd *obfd = objfile->obfd.get (); unsigned int addr_size = cu_header->addr_size; CORE_ADDR mask = ~(~(CORE_ADDR)1 << (addr_size * 8 - 1)); @@ -9907,54 +9887,6 @@ dwarf2_access_attribute (struct die_info *die, struct dwarf2_cu *cu) } } -/* Look for DW_AT_data_member_location or DW_AT_data_bit_offset. Set - *OFFSET to the byte offset. If the attribute was not found return - 0, otherwise return 1. If it was found but could not properly be - handled, set *OFFSET to 0. */ - -static int -handle_member_location (struct die_info *die, struct dwarf2_cu *cu, - LONGEST *offset) -{ - struct attribute *attr; - - attr = dwarf2_attr (die, DW_AT_data_member_location, cu); - if (attr != NULL) - { - *offset = 0; - CORE_ADDR temp; - - /* Note that we do not check for a section offset first here. - This is because DW_AT_data_member_location is new in DWARF 4, - so if we see it, we can assume that a constant form is really - a constant and not a section offset. */ - if (attr->form_is_constant ()) - *offset = attr->unsigned_constant ().value_or (0); - else if (attr->form_is_section_offset ()) - dwarf2_complex_location_expr_complaint (); - else if (attr->form_is_block () - && decode_locdesc (attr->as_block (), cu, &temp)) - { - *offset = temp; - } - else - dwarf2_complex_location_expr_complaint (); - - return 1; - } - else - { - attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu); - if (attr != nullptr) - { - *offset = attr->unsigned_constant ().value_or (0); - return 1; - } - } - - return 0; -} - /* Look for DW_AT_data_member_location or DW_AT_data_bit_offset and store the results in FIELD. */ @@ -9967,6 +9899,25 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu, attr = dwarf2_attr (die, DW_AT_data_member_location, cu); if (attr != NULL) { + bool has_bit_offset = false; + LONGEST bit_offset = 0; + LONGEST anonymous_size = 0; + + attribute *attr2 = dwarf2_attr (die, DW_AT_bit_offset, cu); + if (attr2 != nullptr && attr2->form_is_constant ()) + { + has_bit_offset = true; + bit_offset = attr2->unsigned_constant ().value_or (0); + attr2 = dwarf2_attr (die, DW_AT_byte_size, cu); + if (attr2 != nullptr && attr2->form_is_constant ()) + { + /* The size of the anonymous object containing + the bit field is explicit, so use the + indicated size (in bytes). */ + anonymous_size = attr2->unsigned_constant ().value_or (0); + } + } + if (attr->form_is_constant ()) { LONGEST offset = attr->unsigned_constant ().value_or (0); @@ -9984,9 +9935,9 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu, } field->set_loc_bitpos (offset * bits_per_byte); + if (has_bit_offset) + apply_bit_offset_to_field (*field, bit_offset, anonymous_size); } - else if (attr->form_is_section_offset ()) - dwarf2_complex_location_expr_complaint (); else if (attr->form_is_block ()) { CORE_ADDR offset; @@ -9996,9 +9947,20 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu, { dwarf2_per_objfile *per_objfile = cu->per_objfile; struct objfile *objfile = per_objfile->objfile; - struct dwarf2_locexpr_baton *dlbaton - = XOBNEW (&objfile->objfile_obstack, - struct dwarf2_locexpr_baton); + struct dwarf2_locexpr_baton *dlbaton; + if (has_bit_offset) + { + dwarf2_field_location_baton *flbaton + = OBSTACK_ZALLOC (&objfile->objfile_obstack, + dwarf2_field_location_baton); + flbaton->is_field_location = true; + flbaton->bit_offset = bit_offset; + flbaton->explicit_byte_size = anonymous_size; + dlbaton = flbaton; + } + else + dlbaton = OBSTACK_ZALLOC (&objfile->objfile_obstack, + struct dwarf2_locexpr_baton); dlbaton->data = attr->as_block ()->data; dlbaton->size = attr->as_block ()->size; /* When using this baton, we want to compute the address @@ -10012,7 +9974,8 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu, } } else - dwarf2_complex_location_expr_complaint (); + complaint (_("Unsupported form %s for DW_AT_data_member_location"), + dwarf_form_name (attr->form)); } else { @@ -10028,8 +9991,6 @@ static void dwarf2_add_field (struct field_info *fip, struct die_info *die, struct dwarf2_cu *cu) { - struct objfile *objfile = cu->per_objfile->objfile; - struct gdbarch *gdbarch = objfile->arch (); struct nextfield *new_field; struct attribute *attr; struct field *fp; @@ -10093,50 +10054,6 @@ dwarf2_add_field (struct field_info *fip, struct die_info *die, /* Get bit offset of field. */ handle_member_location (die, cu, fp); - attr = dwarf2_attr (die, DW_AT_bit_offset, cu); - if (attr != nullptr && attr->form_is_constant ()) - { - ULONGEST bit_offset = attr->unsigned_constant ().value_or (0); - if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) - { - /* For big endian bits, the DW_AT_bit_offset gives the - additional bit offset from the MSB of the containing - anonymous object to the MSB of the field. We don't - have to do anything special since we don't need to - know the size of the anonymous object. */ - fp->set_loc_bitpos (fp->loc_bitpos () + bit_offset); - } - else - { - /* For little endian bits, compute the bit offset to the - MSB of the anonymous object, subtract off the number of - bits from the MSB of the field to the MSB of the - object, and then subtract off the number of bits of - the field itself. The result is the bit offset of - the LSB of the field. */ - int anonymous_size; - - attr = dwarf2_attr (die, DW_AT_byte_size, cu); - if (attr != nullptr && attr->form_is_constant ()) - { - /* The size of the anonymous object containing - the bit field is explicit, so use the - indicated size (in bytes). */ - anonymous_size = attr->unsigned_constant ().value_or (0); - } - else - { - /* The size of the anonymous object containing - the bit field must be inferred from the type - attribute of the data member containing the - bit field. */ - anonymous_size = fp->type ()->length (); - } - fp->set_loc_bitpos (fp->loc_bitpos () - + anonymous_size * bits_per_byte - - bit_offset - fp->bitsize ()); - } - } /* Get name of field. */ fieldname = dwarf2_name (die, cu); @@ -11315,6 +11232,56 @@ handle_struct_member_die (struct die_info *child_die, struct type *type, handle_variant (child_die, type, fi, template_args, cu); } +/* Create a property baton for a field of the struct type currently + being processed. OFFSET is the DIE offset of the field in the + structure. If OFFSET is found among the fields that have already + been seen, then a new property baton is allocated on the objfile + obstack and returned. The baton isn't fully filled in -- it will + be post-processed once the fields are finally created; see + update_field_batons. If OFFSET is not found, NULL is returned. */ + +static dwarf2_property_baton * +find_field_create_baton (dwarf2_cu *cu, sect_offset offset) +{ + field_info *fi = cu->field_info; + /* Defensive programming in case we see unusual DWARF. */ + if (fi == nullptr) + return nullptr; + for (const auto &fld : fi->fields) + if (fld.offset == offset) + { + dwarf2_property_baton *result + = XOBNEW (&cu->per_objfile->objfile->objfile_obstack, + struct dwarf2_property_baton); + fi->field_batons[result] = offset; + return result; + } + return nullptr; +} + +/* Update all the stored field property batons. FI is the field info + for the structure being created. TYPE is the corresponding struct + type with its fields already filled in. This fills in the correct + field for each baton that was stored while processing this + type. */ + +static void +update_field_batons (field_info *fi, struct type *type) +{ + int n_bases = fi->baseclasses.size (); + for (auto &[baton, offset] : fi->field_batons) + { + for (int i = 0; i < fi->fields.size (); ++i) + { + if (fi->fields[i].offset == offset) + { + baton->field = &type->field (n_bases + i); + break; + } + } + } +} + /* Finish creating a structure or union type, including filling in its members and creating a symbol for it. This function also handles Fortran namelist variables, their items or members and creating a symbol for @@ -11336,6 +11303,9 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) struct field_info fi; std::vector<struct symbol *> template_args; + scoped_restore save_field_info + = make_scoped_restore (&cu->field_info, &fi); + for (die_info *child_die : die->children ()) handle_struct_member_die (child_die, type, &fi, &template_args, cu); @@ -11357,7 +11327,11 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) /* Attach fields and member functions to the type. */ if (fi.nfields () > 0) - dwarf2_attach_fields_to_type (&fi, type, cu); + { + dwarf2_attach_fields_to_type (&fi, type, cu); + update_field_batons (&fi, type); + } + if (!fi.fnfieldlists.empty ()) { dwarf2_attach_fn_fields_to_type (&fi, type, cu); @@ -12308,7 +12282,8 @@ mark_common_block_symbol_computed (struct symbol *sym, gdb_assert (member_loc->form_is_block () || member_loc->form_is_constant ()); - baton = XOBNEW (&objfile->objfile_obstack, struct dwarf2_locexpr_baton); + baton = OBSTACK_ZALLOC (&objfile->objfile_obstack, + struct dwarf2_locexpr_baton); baton->per_objfile = per_objfile; baton->per_cu = cu->per_cu; gdb_assert (baton->per_cu); @@ -12614,7 +12589,7 @@ static struct type * read_tag_pointer_type (struct die_info *die, struct dwarf2_cu *cu) { struct gdbarch *gdbarch = cu->per_objfile->objfile->arch (); - struct comp_unit_head *cu_header = &cu->header; + unit_head *cu_header = &cu->header; struct type *type; struct attribute *attr_byte_size; struct attribute *attr_address_class; @@ -12729,7 +12704,7 @@ static struct type * read_tag_reference_type (struct die_info *die, struct dwarf2_cu *cu, enum type_code refcode) { - struct comp_unit_head *cu_header = &cu->header; + unit_head *cu_header = &cu->header; struct type *type, *target_type; struct attribute *attr; @@ -13211,7 +13186,7 @@ read_typedef (struct die_info *die, struct dwarf2_cu *cu) a given gmp_mpz given an attribute. */ static void -get_mpz (struct dwarf2_cu *cu, gdb_mpz *value, struct attribute *attr) +get_mpz_for_rational (dwarf2_cu *cu, gdb_mpz *value, attribute *attr) { /* GCC will sometimes emit a 16-byte constant value as a DWARF location expression that pushes an implicit value. */ @@ -13245,10 +13220,11 @@ get_mpz (struct dwarf2_cu *cu, gdb_mpz *value, struct attribute *attr) ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE, true); } - else if (attr->form_is_strictly_unsigned ()) - *value = gdb_mpz (attr->as_unsigned ()); else - *value = gdb_mpz (attr->signed_constant ().value_or (1)); + { + /* Rational constants for Ada are always unsigned. */ + *value = gdb_mpz (attr->unsigned_constant ().value_or (1)); + } } /* Assuming DIE is a rational DW_TAG_constant, read the DIE's @@ -13277,8 +13253,8 @@ get_dwarf2_rational_constant (struct die_info *die, struct dwarf2_cu *cu, if (num_attr == nullptr || denom_attr == nullptr) return; - get_mpz (cu, numerator, num_attr); - get_mpz (cu, denominator, denom_attr); + get_mpz_for_rational (cu, numerator, num_attr); + get_mpz_for_rational (cu, denominator, denom_attr); } /* Same as get_dwarf2_rational_constant, but extracting an unsigned @@ -13634,7 +13610,6 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu) struct type *type; struct attribute *attr; ULONGEST encoding = 0; - int bits = 0; const char *name; attr = dwarf2_attr (die, DW_AT_encoding, cu); @@ -13644,9 +13619,33 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu) if (value.has_value ()) encoding = *value; } + attr = dwarf2_attr (die, DW_AT_byte_size, cu); + std::optional<ULONGEST> byte_size; + if (attr != nullptr) + byte_size = attr->unsigned_constant (); + attr = dwarf2_attr (die, DW_AT_bit_size, cu); + std::optional<ULONGEST> bit_size; if (attr != nullptr) - bits = attr->unsigned_constant ().value_or (0) * TARGET_CHAR_BIT; + bit_size = attr->unsigned_constant (); + + attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu); + std::optional<ULONGEST> bit_offset; + if (attr != nullptr) + bit_offset = attr->unsigned_constant (); + + int bits = 0; + if (byte_size.has_value ()) + bits = TARGET_CHAR_BIT * *byte_size; + else if (bit_size.has_value ()) + bits = align_up (*bit_size, 8); + else + { + /* No size, so arrange for an error type. */ + complaint (_("DW_TAG_base_type has neither bit- nor byte-size")); + encoding = (ULONGEST) -1; + } + name = dwarf2_full_name (nullptr, die, cu); if (!name) complaint (_("DW_AT_name missing from DW_TAG_base_type")); @@ -13792,35 +13791,21 @@ read_base_type (struct die_info *die, struct dwarf2_cu *cu) type->set_endianity_is_not_default (not_default); - if (TYPE_SPECIFIC_FIELD (type) == TYPE_SPECIFIC_INT) + /* If both a byte size and bit size were provided, then that means + that not every bit in the object contributes to the value. */ + if (TYPE_SPECIFIC_FIELD (type) == TYPE_SPECIFIC_INT + && byte_size.has_value () + && bit_size.has_value ()) { - attr = dwarf2_attr (die, DW_AT_bit_size, cu); - if (attr != nullptr && attr->form_is_constant ()) + /* DWARF says: If this attribute is omitted a default data bit + offset of zero is assumed. */ + ULONGEST offset = bit_offset.value_or (0); + + /* Only use the attributes if they make sense together. */ + if (*bit_size + offset <= 8 * type->length ()) { - unsigned real_bit_size = attr->unsigned_constant ().value_or (0); - if (real_bit_size >= 0 && real_bit_size <= 8 * type->length ()) - { - attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu); - /* Only use the attributes if they make sense together. */ - std::optional<ULONGEST> bit_offset; - if (attr == nullptr) - bit_offset = 0; - else if (attr->form_is_constant ()) - { - bit_offset = attr->unsigned_constant (); - if (bit_offset.has_value () - && *bit_offset + real_bit_size > 8 * type->length ()) - bit_offset.reset (); - } - if (bit_offset.has_value ()) - { - TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_size - = real_bit_size; - if (attr != nullptr) - TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_offset - = *bit_offset; - } - } + TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_size = *bit_size; + TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_offset = offset; } } @@ -13961,17 +13946,14 @@ attr_to_dynamic_prop (const struct attribute *attr, struct die_info *die, case DW_AT_data_member_location: case DW_AT_data_bit_offset: { - LONGEST offset; - - if (!handle_member_location (target_die, target_cu, &offset)) + baton = find_field_create_baton (cu, target_die->sect_off); + if (baton == nullptr) return 0; - baton = XOBNEW (obstack, struct dwarf2_property_baton); baton->property_type = read_type_die (target_die->parent, - target_cu); - baton->offset_info.offset = offset; - baton->offset_info.type = die_type (target_die, target_cu); - prop->set_addr_offset (baton); + target_cu); + baton->field = nullptr; + prop->set_field (baton); break; } } @@ -14905,7 +14887,7 @@ cutu_reader::read_attribute_value (attribute *attr, unsigned form, { dwarf2_per_objfile *per_objfile = m_cu->per_objfile; struct objfile *objfile = per_objfile->objfile; - struct comp_unit_head *cu_header = &m_cu->header; + unit_head *cu_header = &m_cu->header; unsigned int bytes_read; struct dwarf_block *blk; @@ -15213,8 +15195,7 @@ read_indirect_string_at_offset (dwarf2_per_objfile *per_objfile, static const char * read_indirect_string (dwarf2_per_objfile *per_objfile, bfd *abfd, - const gdb_byte *buf, - const struct comp_unit_head *cu_header, + const gdb_byte *buf, const unit_head *cu_header, unsigned int *bytes_read_ptr) { LONGEST str_offset = cu_header->read_offset (abfd, buf, bytes_read_ptr); @@ -15238,7 +15219,7 @@ dwarf2_per_objfile::read_line_string (const gdb_byte *buf, const char * dwarf2_per_objfile::read_line_string (const gdb_byte *buf, - const struct comp_unit_head *cu_header, + const unit_head *cu_header, unsigned int *bytes_read_ptr) { bfd *abfd = objfile->obfd.get (); @@ -15371,9 +15352,16 @@ read_str_index (struct dwarf2_cu *cu, " in CU at offset %s [in module %s]"), form_name, str_section->get_name (), sect_offset_str (cu->header.sect_off), objf_name); - info_ptr = (str_offsets_section->buffer - + str_offsets_base - + str_index * offset_size); + + ULONGEST str_offsets_offset = str_offsets_base + str_index * offset_size; + if (str_offsets_offset >= str_offsets_section->size) + error (_(DWARF_ERROR_PREFIX + "Offset from %s pointing outside of %s section in CU at offset %s" + " [in module %s]"), + form_name, str_offsets_section->get_name (), + sect_offset_str (cu->header.sect_off), objf_name); + info_ptr = str_offsets_section->buffer + str_offsets_offset; + if (offset_size == 4) str_offset = bfd_get_32 (abfd, info_ptr); else @@ -16570,7 +16558,7 @@ var_decode_location (struct attribute *attr, struct symbol *sym, struct dwarf2_cu *cu) { struct objfile *objfile = cu->per_objfile->objfile; - struct comp_unit_head *cu_header = &cu->header; + unit_head *cu_header = &cu->header; /* NOTE drow/2003-01-30: There used to be a comment and some special code here to turn a symbol with DW_AT_external and a @@ -17241,7 +17229,7 @@ dwarf2_const_value_attr (const struct attribute *attr, struct type *type, { dwarf2_per_objfile *per_objfile = cu->per_objfile; struct objfile *objfile = per_objfile->objfile; - struct comp_unit_head *cu_header = &cu->header; + unit_head *cu_header = &cu->header; struct dwarf_block *blk; enum bfd_endian byte_order = (bfd_big_endian (objfile->obfd.get ()) ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE); @@ -17265,7 +17253,7 @@ dwarf2_const_value_attr (const struct attribute *attr, struct type *type, /* Symbols of this form are reasonably rare, so we just piggyback on the existing location code rather than writing a new implementation of symbol_computed_ops. */ - *baton = XOBNEW (obstack, struct dwarf2_locexpr_baton); + *baton = OBSTACK_ZALLOC (obstack, struct dwarf2_locexpr_baton); (*baton)->per_objfile = per_objfile; (*baton)->per_cu = cu->per_cu; gdb_assert ((*baton)->per_cu); @@ -18217,18 +18205,18 @@ follow_die_offset (sect_offset sect_off, int offset_in_dwz, "source CU contains target offset: %d", sect_offset_str (source_cu->per_cu->sect_off), sect_offset_str (sect_off), - source_cu->header.offset_in_cu_p (sect_off)); + source_cu->header.offset_in_unit_p (sect_off)); if (source_cu->per_cu->is_debug_types) { /* .debug_types CUs cannot reference anything outside their CU. If they need to, they have to reference a signatured type via DW_FORM_ref_sig8. */ - if (!source_cu->header.offset_in_cu_p (sect_off)) + if (!source_cu->header.offset_in_unit_p (sect_off)) return NULL; } else if (offset_in_dwz != source_cu->per_cu->is_dwz - || !source_cu->header.offset_in_cu_p (sect_off)) + || !source_cu->header.offset_in_unit_p (sect_off)) { dwarf2_per_cu *target_per_cu = dwarf2_find_containing_comp_unit (sect_off, offset_in_dwz, @@ -18260,8 +18248,7 @@ follow_die_offset (sect_offset sect_off, int offset_in_dwz, *ref_cu = target_cu; - auto it = target_cu->die_hash.find (sect_off); - return it != target_cu->die_hash.end () ? *it : nullptr; + return target_cu->find_die (sect_off); } /* Follow reference attribute ATTR of SRC_DIE. @@ -18629,8 +18616,8 @@ follow_die_sig_1 (struct die_info *src_die, struct signatured_type *sig_type, gdb_assert (sig_cu != NULL); gdb_assert (to_underlying (sig_type->type_offset_in_section) != 0); - if (auto die_it = sig_cu->die_hash.find (sig_type->type_offset_in_section); - die_it != sig_cu->die_hash.end ()) + if (die_info *die = sig_cu->find_die (sig_type->type_offset_in_section); + die != nullptr) { /* For .gdb_index version 7 keep track of included TUs. http://sourceware.org/bugzilla/show_bug.cgi?id=15021. */ @@ -18639,7 +18626,7 @@ follow_die_sig_1 (struct die_info *src_die, struct signatured_type *sig_type, (*ref_cu)->per_cu->imported_symtabs.push_back (sig_cu->per_cu); *ref_cu = sig_cu; - return *die_it; + return die; } return NULL; @@ -19217,7 +19204,8 @@ dwarf2_symbol_mark_computed (const struct attribute *attr, struct symbol *sym, { struct dwarf2_locexpr_baton *baton; - baton = XOBNEW (&objfile->objfile_obstack, struct dwarf2_locexpr_baton); + baton = OBSTACK_ZALLOC (&objfile->objfile_obstack, + struct dwarf2_locexpr_baton); baton->per_objfile = per_objfile; baton->per_cu = cu->per_cu; gdb_assert (baton->per_cu); @@ -19249,7 +19237,7 @@ dwarf2_symbol_mark_computed (const struct attribute *attr, struct symbol *sym, /* See read.h. */ -const comp_unit_head * +const unit_head * dwarf2_per_cu::get_header () const { if (!m_header_read_in) @@ -19257,8 +19245,7 @@ dwarf2_per_cu::get_header () const const gdb_byte *info_ptr = this->section->buffer + to_underlying (this->sect_off); - read_comp_unit_head (&m_header, info_ptr, this->section, - rcuh_kind::COMPILE); + read_unit_head (&m_header, info_ptr, this->section, ruh_kind::COMPILE); m_header_read_in = true; } @@ -19287,7 +19274,7 @@ dwarf2_per_cu::offset_size () const int dwarf2_per_cu::ref_addr_size () const { - const comp_unit_head *header = this->get_header (); + const unit_head *header = this->get_header (); if (header->version == 2) return header->addr_size; diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index f3e043c..aaac5e7 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -22,7 +22,7 @@ #include <queue> #include "dwarf2/abbrev.h" -#include "dwarf2/comp-unit-head.h" +#include "dwarf2/unit-head.h" #include "dwarf2/file-and-dir.h" #include "dwarf2/index-cache.h" #include "dwarf2/mapped-index.h" @@ -233,14 +233,14 @@ public: /* Backlink to the owner of this. */ dwarf2_per_bfd *per_bfd; - /* DWARF header of this CU. Note that dwarf2_cu reads its own version of the - header, which may differ from this one, since it may pass rcuh_kind::TYPE - to read_comp_unit_head, whereas for dwarf2_per_cu we always pass - rcuh_kind::COMPILE. + /* DWARF header of this unit. Note that dwarf2_cu reads its own version of + the header, which may differ from this one, since it may pass + rch_kind::TYPE to read_unit_head, whereas for dwarf2_per_cu we always pass + ruh_kind::COMPILE. Don't access this field directly, use the get_header method instead. It should be private, but we can't make it private at the moment. */ - mutable comp_unit_head m_header; + mutable unit_head m_header; /* The file and directory for this CU. This is cached so that we don't need to re-examine the DWO in some situations. This may be @@ -271,7 +271,7 @@ public: std::vector<dwarf2_per_cu *> imported_symtabs; /* Get the header of this per_cu, reading it if necessary. */ - const comp_unit_head *get_header () const; + const unit_head *get_header () const; /* Return the address size given in the compilation unit header for this CU. */ @@ -812,7 +812,7 @@ struct dwarf2_per_objfile BUF is assumed to be in a compilation unit described by CU_HEADER. Return *BYTES_READ_PTR count of bytes read from BUF. */ const char *read_line_string (const gdb_byte *buf, - const struct comp_unit_head *cu_header, + const struct unit_head *unit_header, unsigned int *bytes_read_ptr); /* Return pointer to string at .debug_line_str offset as read from BUF. @@ -934,8 +934,6 @@ public: DISABLE_COPY_AND_ASSIGN (cutu_reader); - cutu_reader (cutu_reader &&) = default; - /* Return true if either: - the unit is empty (just a header without any DIE) @@ -1031,19 +1029,11 @@ private: dwo_file_up open_and_init_dwo_file (dwarf2_cu *cu, const char *dwo_name, const char *comp_dir); - void locate_dwo_sections (struct objfile *objfile, bfd *abfd, asection *sectp, - struct dwo_sections *dwo_sections); - - void create_dwo_cus_hash_table (dwarf2_cu *cu, dwo_file &dwo_file); - - void create_dwo_debug_types_hash_table - (dwarf2_per_bfd *per_bfd, dwo_file *dwo_file, - gdb::array_view<dwarf2_section_info> type_sections); + void locate_dwo_sections (objfile *objfile, dwo_file &dwo_file); - void create_dwo_debug_type_hash_table (dwarf2_per_bfd *per_bfd, - dwo_file *dwo_file, - dwarf2_section_info *section, - rcuh_kind section_kind); + void create_dwo_unit_hash_tables (dwo_file &dwo_file, dwarf2_cu &skeleton_cu, + dwarf2_section_info §ion, + ruh_kind section_kind); dwo_unit *lookup_dwo_cutu (dwarf2_cu *cu, const char *dwo_name, const char *comp_dir, ULONGEST signature, diff --git a/gdb/dwarf2/comp-unit-head.c b/gdb/dwarf2/unit-head.c index a35d664..0c7614f 100644 --- a/gdb/dwarf2/comp-unit-head.c +++ b/gdb/dwarf2/unit-head.c @@ -24,118 +24,117 @@ 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/comp-unit-head.h" +#include "dwarf2/unit-head.h" #include "dwarf2/leb.h" #include "dwarf2/section.h" #include "dwarf2/stringify.h" #include "dwarf2/error.h" -/* See comp-unit-head.h. */ +/* See unit-head.h. */ const gdb_byte * -read_comp_unit_head (struct comp_unit_head *cu_header, - const gdb_byte *info_ptr, - struct dwarf2_section_info *section, - rcuh_kind section_kind) +read_unit_head (struct unit_head *header, const gdb_byte *info_ptr, + struct dwarf2_section_info *section, ruh_kind section_kind) { int signed_addr; unsigned int bytes_read; const char *filename = section->get_file_name (); bfd *abfd = section->get_bfd_owner (); - cu_header->set_length (read_initial_length (abfd, info_ptr, &bytes_read)); - cu_header->initial_length_size = bytes_read; - cu_header->offset_size = (bytes_read == 4) ? 4 : 8; + header->set_length (read_initial_length (abfd, info_ptr, &bytes_read)); + header->initial_length_size = bytes_read; + header->offset_size = (bytes_read == 4) ? 4 : 8; info_ptr += bytes_read; unsigned version = read_2_bytes (abfd, info_ptr); if (version < 2 || version > 5) error (_(DWARF_ERROR_PREFIX - "wrong version in compilation unit header " + "wrong version in unit header " "(is %d, should be 2, 3, 4 or 5) [in module %s]"), version, filename); - cu_header->version = version; + header->version = version; info_ptr += 2; - if (cu_header->version < 5) + if (header->version < 5) switch (section_kind) { - case rcuh_kind::COMPILE: - cu_header->unit_type = DW_UT_compile; + case ruh_kind::COMPILE: + header->unit_type = DW_UT_compile; break; - case rcuh_kind::TYPE: - cu_header->unit_type = DW_UT_type; + case ruh_kind::TYPE: + header->unit_type = DW_UT_type; break; default: - internal_error (_("read_comp_unit_head: invalid section_kind")); + internal_error (_("read_unit_head: invalid section_kind")); } else { - cu_header->unit_type = static_cast<enum dwarf_unit_type> - (read_1_byte (abfd, info_ptr)); + header->unit_type + = static_cast<enum dwarf_unit_type> (read_1_byte (abfd, info_ptr)); info_ptr += 1; - switch (cu_header->unit_type) + switch (header->unit_type) { case DW_UT_compile: case DW_UT_partial: case DW_UT_skeleton: case DW_UT_split_compile: - if (section_kind != rcuh_kind::COMPILE) + if (section_kind != ruh_kind::COMPILE) error (_(DWARF_ERROR_PREFIX - "wrong unit_type in compilation unit header " + "wrong unit_type in unit header " "(is %s, should be %s) [in module %s]"), - dwarf_unit_type_name (cu_header->unit_type), + dwarf_unit_type_name (header->unit_type), dwarf_unit_type_name (DW_UT_type), filename); break; case DW_UT_type: case DW_UT_split_type: - section_kind = rcuh_kind::TYPE; + section_kind = ruh_kind::TYPE; break; default: error (_(DWARF_ERROR_PREFIX - "wrong unit_type in compilation unit header " + "wrong unit_type in unit header " "(is %#04x, should be one of: %s, %s, %s, %s or %s) " "[in module %s]"), - cu_header->unit_type, dwarf_unit_type_name (DW_UT_compile), + header->unit_type, dwarf_unit_type_name (DW_UT_compile), dwarf_unit_type_name (DW_UT_skeleton), dwarf_unit_type_name (DW_UT_split_compile), dwarf_unit_type_name (DW_UT_type), dwarf_unit_type_name (DW_UT_split_type), filename); } - cu_header->addr_size = read_1_byte (abfd, info_ptr); + header->addr_size = read_1_byte (abfd, info_ptr); info_ptr += 1; } - cu_header->abbrev_sect_off - = (sect_offset) cu_header->read_offset (abfd, info_ptr, &bytes_read); + header->abbrev_sect_off + = (sect_offset) header->read_offset (abfd, info_ptr, &bytes_read); info_ptr += bytes_read; - if (cu_header->version < 5) + if (header->version < 5) { - cu_header->addr_size = read_1_byte (abfd, info_ptr); + header->addr_size = read_1_byte (abfd, info_ptr); info_ptr += 1; } signed_addr = bfd_get_sign_extend_vma (abfd); if (signed_addr < 0) - internal_error (_("read_comp_unit_head: dwarf from non elf file")); - cu_header->signed_addr_p = signed_addr; + internal_error (_("read_unit_head: dwarf from non elf file")); + header->signed_addr_p = signed_addr; - bool header_has_signature = section_kind == rcuh_kind::TYPE - || cu_header->unit_type == DW_UT_skeleton - || cu_header->unit_type == DW_UT_split_compile; + bool header_has_signature = + (section_kind == ruh_kind::TYPE + || header->unit_type == DW_UT_skeleton + || header->unit_type == DW_UT_split_compile); if (header_has_signature) { - cu_header->signature = read_8_bytes (abfd, info_ptr); + header->signature = read_8_bytes (abfd, info_ptr); info_ptr += 8; } - if (section_kind == rcuh_kind::TYPE) + if (section_kind == ruh_kind::TYPE) { LONGEST type_offset; - type_offset = cu_header->read_offset (abfd, info_ptr, &bytes_read); + type_offset = header->read_offset (abfd, info_ptr, &bytes_read); info_ptr += bytes_read; - cu_header->type_cu_offset_in_tu = (cu_offset) type_offset; - if (to_underlying (cu_header->type_cu_offset_in_tu) != type_offset) + header->type_offset_in_tu = (cu_offset) type_offset; + if (to_underlying (header->type_offset_in_tu) != type_offset) error (_(DWARF_ERROR_PREFIX - "Too big type_offset in compilation unit " + "Too big type_offset in unit " "header (is %s) [in module %s]"), plongest (type_offset), filename); } @@ -143,60 +142,56 @@ read_comp_unit_head (struct comp_unit_head *cu_header, return info_ptr; } -/* Subroutine of read_and_check_comp_unit_head and - read_and_check_type_unit_head to simplify them. +/* Subroutine of read_and_check_unit_head to to simplify it. Perform various error checking on the header. */ static void -error_check_comp_unit_head (comp_unit_head *header, - dwarf2_section_info *section, - dwarf2_section_info *abbrev_section) +error_check_unit_head (unit_head *header, dwarf2_section_info *section, + dwarf2_section_info *abbrev_section) { const char *filename = section->get_file_name (); if (to_underlying (header->abbrev_sect_off) >= abbrev_section->size) error (_(DWARF_ERROR_PREFIX - "bad offset (%s) in compilation unit header " + "bad offset (%s) in unit header " "(offset %s + 6) [in module %s]"), sect_offset_str (header->abbrev_sect_off), - sect_offset_str (header->sect_off), - filename); + sect_offset_str (header->sect_off), filename); /* Cast to ULONGEST to use 64-bit arithmetic when possible to avoid potential 32-bit overflow. */ if (((ULONGEST) header->sect_off + header->get_length_with_initial ()) > section->size) error (_(DWARF_ERROR_PREFIX - "bad length (0x%x) in compilation unit header " + "bad length (0x%x) in unit header " "(offset %s + 0) [in module %s]"), header->get_length_without_initial (), sect_offset_str (header->sect_off), filename); } -/* See comp-unit-head.h. */ +/* See unit-head.h. */ const gdb_byte * -read_and_check_comp_unit_head (comp_unit_head *header, - dwarf2_section_info *section, - dwarf2_section_info *abbrev_section, - const gdb_byte *info_ptr, rcuh_kind section_kind) +read_and_check_unit_head (unit_head *header, dwarf2_section_info *section, + dwarf2_section_info *abbrev_section, + const gdb_byte *info_ptr, ruh_kind section_kind) { - const gdb_byte *beg_of_comp_unit = info_ptr; + const gdb_byte *beg_of_unit = info_ptr; - header->sect_off = (sect_offset) (beg_of_comp_unit - section->buffer); + header->sect_off = (sect_offset) (beg_of_unit - section->buffer); - info_ptr = read_comp_unit_head (header, info_ptr, section, section_kind); + info_ptr = read_unit_head (header, info_ptr, section, section_kind); - header->first_die_cu_offset = (cu_offset) (info_ptr - beg_of_comp_unit); + header->first_die_offset_in_unit = (cu_offset) (info_ptr - beg_of_unit); - error_check_comp_unit_head (header, section, abbrev_section); + error_check_unit_head (header, section, abbrev_section); return info_ptr; } unrelocated_addr -comp_unit_head::read_address (bfd *abfd, const gdb_byte *buf, - unsigned int *bytes_read) const +unit_head::read_address (bfd *abfd, const gdb_byte *buf, + unsigned int *bytes_read) const { ULONGEST retval = 0; diff --git a/gdb/dwarf2/comp-unit-head.h b/gdb/dwarf2/unit-head.h index ea09153..6272888 100644 --- a/gdb/dwarf2/comp-unit-head.h +++ b/gdb/dwarf2/unit-head.h @@ -24,18 +24,19 @@ 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_COMP_UNIT_HEAD_H -#define GDB_DWARF2_COMP_UNIT_HEAD_H +#ifndef GDB_DWARF2_UNIT_HEAD_H +#define GDB_DWARF2_UNIT_HEAD_H #include "dwarf2.h" #include "dwarf2/leb.h" #include "dwarf2/types.h" struct dwarf2_per_objfile; +struct dwarf2_section_info; -/* The data in a compilation unit header, after target2host - translation, looks like this. */ -struct comp_unit_head +/* The data in a unit header, after target2host translation, looks like + this. */ +struct unit_head { private: unsigned int m_length = 0; @@ -53,16 +54,16 @@ public: enum dwarf_unit_type unit_type {}; - /* Offset to first die in this cu from the start of the cu. - This will be the first byte following the compilation unit header. */ - cu_offset first_die_cu_offset {}; + /* Offset to first die in this unit from the start of the unit. + This will be the first byte following the unit header. */ + cu_offset first_die_offset_in_unit {}; - /* Offset to the first byte of this compilation unit header in the - .debug_info section, for resolving relative reference dies. */ + /* Offset to the first byte of this unit header in the containing section, + for resolving relative reference dies. */ sect_offset sect_off {}; /* For types, offset in the type's DIE of the type defined by this TU. */ - cu_offset type_cu_offset_in_tu {}; + cu_offset type_offset_in_tu {}; /* 64-bit signature of this unit. For type units, it denotes the signature of the type (DW_UT_type in DWARF 4, additionally DW_UT_split_type in DWARF 5). @@ -75,22 +76,22 @@ public: m_length = length; } - /* Return the total length of the CU described by this header, including the + /* Return the total length of the unit described by this header, including the initial length field. */ unsigned int get_length_with_initial () const { return m_length + initial_length_size; } - /* Return the total length of the CU described by this header, excluding the + /* Return the total length of the unit described by this header, excluding the initial length field. */ unsigned int get_length_without_initial () const { return m_length; } - /* Return TRUE if OFF is within this CU. */ - bool offset_in_cu_p (sect_offset off) const + /* Return TRUE if OFF is within this unit. */ + bool offset_in_unit_p (sect_offset off) const { sect_offset bottom = sect_off; sect_offset top = sect_off + get_length_with_initial (); @@ -98,7 +99,7 @@ public: } /* Read an offset from the data stream. The size of the offset is - given by cu_header->offset_size. */ + given by unit_head::offset_size. */ LONGEST read_offset (bfd *abfd, const gdb_byte *buf, unsigned int *bytes_read) const { @@ -112,25 +113,24 @@ public: unsigned int *bytes_read) const; }; -/* Expected enum dwarf_unit_type for read_comp_unit_head. */ -enum class rcuh_kind { COMPILE, TYPE }; +/* Expected enum dwarf_unit_type for read_unit_head. */ +enum class ruh_kind { COMPILE, TYPE }; -/* Read in the comp unit header information from the debug_info at info_ptr. - Use rcuh_kind::COMPILE as the default type if not known by the caller. - NOTE: This leaves members offset, first_die_offset to be filled in +/* Read in the unit header information from the debug_info at info_ptr. + Use ruh_kind::COMPILE as the default type if not known by the caller. + NOTE: This leaves members sect_off, first_die_unit_offset to be filled in by the caller. */ -extern const gdb_byte *read_comp_unit_head - (struct comp_unit_head *cu_header, - const gdb_byte *info_ptr, - struct dwarf2_section_info *section, - rcuh_kind section_kind); +extern const gdb_byte *read_unit_head (unit_head *header, + const gdb_byte *info_ptr, + dwarf2_section_info *section, + ruh_kind section_kind); -/* Read in a CU/TU header and perform some basic error checking. +/* Read in a unit header and perform some basic error checking. The contents of the header are stored in HEADER. The result is a pointer to the start of the first DIE. */ -extern const gdb_byte *read_and_check_comp_unit_head - (comp_unit_head *header, dwarf2_section_info *section, +extern const gdb_byte *read_and_check_unit_head + (unit_head *header, dwarf2_section_info *section, dwarf2_section_info *abbrev_section, const gdb_byte *info_ptr, - rcuh_kind section_kind); + ruh_kind section_kind); -#endif /* GDB_DWARF2_COMP_UNIT_HEAD_H */ +#endif /* GDB_DWARF2_UNIT_HEAD_H */ diff --git a/gdb/event-top.c b/gdb/event-top.c index c533e74..968117c 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -980,11 +980,6 @@ handle_fatal_signal (int sig) #endif #ifdef GDB_PRINT_INTERNAL_BACKTRACE - const auto sig_write = [] (const char *msg) -> void - { - gdb_stderr->write_async_safe (msg, strlen (msg)); - }; - if (bt_on_fatal_signal) { sig_write ("\n\n"); @@ -1027,7 +1022,13 @@ handle_fatal_signal (int sig) } sig_write ("\n\n"); - gdb_stderr->flush (); + if (gdb_stderr == nullptr || gdb_stderr->fd () == -1) + { + /* Writing to file descriptor instead of stream, no flush + required. */ + } + else + gdb_stderr->flush (); } #endif diff --git a/gdb/exceptions.c b/gdb/exceptions.c index 6af3a7e..35400f3 100644 --- a/gdb/exceptions.c +++ b/gdb/exceptions.c @@ -25,6 +25,7 @@ #include "serial.h" #include "ui.h" #include <optional> +#include "cli/cli-style.h" static void print_flush (void) @@ -105,6 +106,7 @@ exception_print (struct ui_file *file, const struct gdb_exception &e) if (e.reason < 0 && e.message != NULL) { print_flush (); + print_error_prefix (file); print_exception (file, e); } } @@ -118,6 +120,7 @@ exception_fprintf (struct ui_file *file, const struct gdb_exception &e, va_list args; print_flush (); + print_error_prefix (file); /* Print the prefix. */ va_start (args, prefix); diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index 5713eac..a241223 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -902,7 +902,7 @@ operator== (const dynamic_prop &l, const dynamic_prop &r) return true; case PROP_CONST: return l.const_val () == r.const_val (); - case PROP_ADDR_OFFSET: + case PROP_FIELD: case PROP_LOCEXPR: case PROP_LOCLIST: return l.baton () == r.baton (); @@ -2146,7 +2146,7 @@ is_dynamic_type (struct type *type) } static struct type *resolve_dynamic_type_internal - (struct type *type, struct property_addr_info *addr_stack, + (struct type *type, const property_addr_info *addr_stack, const frame_info_ptr &frame, bool top_level); /* Given a dynamic range type (dyn_range_type) and a stack of @@ -2167,7 +2167,7 @@ static struct type *resolve_dynamic_type_internal static struct type * resolve_dynamic_range (struct type *dyn_range_type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const frame_info_ptr &frame, int rank, bool resolve_p = true) { @@ -2269,7 +2269,7 @@ resolve_dynamic_range (struct type *dyn_range_type, static struct type * resolve_dynamic_array_or_string_1 (struct type *type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const frame_info_ptr &frame, int rank, bool resolve_p) { @@ -2397,7 +2397,7 @@ resolve_dynamic_array_or_string_1 (struct type *type, static struct type * resolve_dynamic_array_or_string (struct type *type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const frame_info_ptr &frame) { CORE_ADDR value; @@ -2490,7 +2490,7 @@ resolve_dynamic_array_or_string (struct type *type, static struct type * resolve_dynamic_union (struct type *type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const frame_info_ptr &frame) { struct type *resolved_type; @@ -2534,7 +2534,7 @@ variant::matches (ULONGEST value, bool is_unsigned) const static void compute_variant_fields_inner (struct type *type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const variant_part &part, std::vector<bool> &flags); @@ -2549,7 +2549,7 @@ compute_variant_fields_inner (struct type *type, static void compute_variant_fields_recurse (struct type *type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const variant &variant, std::vector<bool> &flags, bool enabled) @@ -2581,7 +2581,7 @@ compute_variant_fields_recurse (struct type *type, static void compute_variant_fields_inner (struct type *type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const variant_part &part, std::vector<bool> &flags) { @@ -2650,7 +2650,7 @@ compute_variant_fields_inner (struct type *type, static void compute_variant_fields (struct type *type, struct type *resolved_type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const gdb::array_view<variant_part> &parts) { /* Assume all fields are included by default. */ @@ -2676,13 +2676,113 @@ compute_variant_fields (struct type *type, } } +/* See gdbtypes.h. */ + +void +apply_bit_offset_to_field (struct field &field, LONGEST bit_offset, + LONGEST explicit_byte_size) +{ + struct type *field_type = field.type (); + struct gdbarch *gdbarch = field_type->arch (); + LONGEST current_bitpos = field.loc_bitpos (); + + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + { + /* For big endian bits, the DW_AT_bit_offset gives the + additional bit offset from the MSB of the containing + anonymous object to the MSB of the field. We don't + have to do anything special since we don't need to + know the size of the anonymous object. */ + field.set_loc_bitpos (current_bitpos + bit_offset); + } + else + { + /* For little endian bits, compute the bit offset to the + MSB of the anonymous object, subtract off the number of + bits from the MSB of the field to the MSB of the + object, and then subtract off the number of bits of + the field itself. The result is the bit offset of + the LSB of the field. */ + LONGEST object_size = explicit_byte_size; + if (object_size == 0) + object_size = field_type->length (); + + field.set_loc_bitpos (current_bitpos + + 8 * object_size + - bit_offset + - field.bitsize ()); + } +} + +/* See gdbtypes.h. */ + +void +resolve_dynamic_field (struct field &field, + const property_addr_info *addr_stack, + const frame_info_ptr &frame) +{ + gdb_assert (!field.is_static ()); + + if (field.loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK) + { + dwarf2_locexpr_baton *field_loc + = field.loc_dwarf_block (); + + struct dwarf2_property_baton baton; + baton.property_type = lookup_pointer_type (field.type ()); + baton.locexpr = *field_loc; + + struct dynamic_prop prop; + prop.set_locexpr (&baton); + + CORE_ADDR vals[1] = {addr_stack->addr}; + CORE_ADDR addr; + if (dwarf2_evaluate_property (&prop, frame, addr_stack, &addr, vals)) + { + field.set_loc_bitpos (TARGET_CHAR_BIT * (addr - addr_stack->addr)); + + if (field_loc->is_field_location) + { + dwarf2_field_location_baton *fl_baton + = static_cast<dwarf2_field_location_baton *> (field_loc); + apply_bit_offset_to_field (field, fl_baton->bit_offset, + fl_baton->explicit_byte_size); + } + } + } + + /* As we know this field is not a static field, the field's + field_loc_kind should be FIELD_LOC_KIND_BITPOS. Verify + this is the case, but only trigger a simple error rather + than an internal error if that fails. While failing + that verification indicates a bug in our code, the error + is not severe enough to suggest to the user he stops + his debugging session because of it. */ + if (field.loc_kind () != FIELD_LOC_KIND_BITPOS) + error (_("Cannot determine struct field location" + " (invalid location kind)")); + + struct property_addr_info pinfo; + pinfo.type = check_typedef (field.type ()); + size_t offset = field.loc_bitpos () / TARGET_CHAR_BIT; + pinfo.valaddr = addr_stack->valaddr; + if (!pinfo.valaddr.empty ()) + pinfo.valaddr = pinfo.valaddr.slice (offset); + pinfo.addr = addr_stack->addr + offset; + pinfo.next = addr_stack; + + field.set_type (resolve_dynamic_type_internal (field.type (), + &pinfo, frame, false)); + gdb_assert (field.loc_kind () == FIELD_LOC_KIND_BITPOS); +} + /* Resolve dynamic bounds of members of the struct TYPE to static bounds. ADDR_STACK is a stack of struct property_addr_info to be used if needed during the dynamic resolution. */ static struct type * resolve_dynamic_struct (struct type *type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const frame_info_ptr &frame) { struct type *resolved_type; @@ -2710,52 +2810,11 @@ resolve_dynamic_struct (struct type *type, for (i = 0; i < resolved_type->num_fields (); ++i) { unsigned new_bit_length; - struct property_addr_info pinfo; if (resolved_type->field (i).is_static ()) continue; - if (resolved_type->field (i).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK) - { - struct dwarf2_property_baton baton; - baton.property_type - = lookup_pointer_type (resolved_type->field (i).type ()); - baton.locexpr = *resolved_type->field (i).loc_dwarf_block (); - - struct dynamic_prop prop; - prop.set_locexpr (&baton); - - CORE_ADDR addr; - if (dwarf2_evaluate_property (&prop, frame, addr_stack, &addr, - {addr_stack->addr})) - resolved_type->field (i).set_loc_bitpos - (TARGET_CHAR_BIT * (addr - addr_stack->addr)); - } - - /* As we know this field is not a static field, the field's - field_loc_kind should be FIELD_LOC_KIND_BITPOS. Verify - this is the case, but only trigger a simple error rather - than an internal error if that fails. While failing - that verification indicates a bug in our code, the error - is not severe enough to suggest to the user he stops - his debugging session because of it. */ - if (resolved_type->field (i).loc_kind () != FIELD_LOC_KIND_BITPOS) - error (_("Cannot determine struct field location" - " (invalid location kind)")); - - pinfo.type = check_typedef (resolved_type->field (i).type ()); - size_t offset = resolved_type->field (i).loc_bitpos () / TARGET_CHAR_BIT; - pinfo.valaddr = addr_stack->valaddr; - if (!pinfo.valaddr.empty ()) - pinfo.valaddr = pinfo.valaddr.slice (offset); - pinfo.addr = addr_stack->addr + offset; - pinfo.next = addr_stack; - - resolved_type->field (i).set_type - (resolve_dynamic_type_internal (resolved_type->field (i).type (), - &pinfo, frame, false)); - gdb_assert (resolved_type->field (i).loc_kind () - == FIELD_LOC_KIND_BITPOS); + resolve_dynamic_field (resolved_type->field (i), addr_stack, frame); new_bit_length = resolved_type->field (i).loc_bitpos (); if (resolved_type->field (i).bitsize () != 0) @@ -2797,7 +2856,7 @@ resolve_dynamic_struct (struct type *type, static struct type * resolve_dynamic_type_internal (struct type *type, - struct property_addr_info *addr_stack, + const property_addr_info *addr_stack, const frame_info_ptr &frame, bool top_level) { diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index 5ee9deb..67b4bf0 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -259,7 +259,7 @@ enum dynamic_prop_kind { PROP_UNDEFINED, /* Not defined. */ PROP_CONST, /* Constant. */ - PROP_ADDR_OFFSET, /* Address offset. */ + PROP_FIELD, /* Field of a type. */ PROP_LOCEXPR, /* Location expression. */ PROP_LOCLIST, /* Location list. */ PROP_VARIANT_PARTS, /* Variant parts. */ @@ -347,7 +347,7 @@ struct dynamic_prop { gdb_assert (m_kind == PROP_LOCEXPR || m_kind == PROP_LOCLIST - || m_kind == PROP_ADDR_OFFSET); + || m_kind == PROP_FIELD); return m_data.baton; } @@ -364,9 +364,9 @@ struct dynamic_prop m_data.baton = baton; } - void set_addr_offset (const dwarf2_property_baton *baton) + void set_field (const dwarf2_property_baton *baton) { - m_kind = PROP_ADDR_OFFSET; + m_kind = PROP_FIELD; m_data.baton = baton; } @@ -2629,6 +2629,41 @@ extern struct type *resolve_dynamic_type "dynamic". */ extern bool is_dynamic_type (struct type *type); +/* Resolve any dynamic components of FIELD. FIELD is updated. + ADDR_STACK and FRAME are used where necessary to supply information + for the resolution process; see resolve_dynamic_type. + Specifically, after calling this, the field's bit position will be + a constant, and the field's type will not have dynamic properties. + + This function assumes that FIELD is not a static field. */ + +extern void resolve_dynamic_field (struct field &field, + const struct property_addr_info *addr_stack, + const frame_info_ptr &frame); + +/* A helper function that handles the DWARF semantics for + DW_AT_bit_offset. + + DWARF 3 specified DW_AT_bit_offset in a funny way, making it simple + to use on big-endian targets but somewhat difficult for + little-endian. This function handles the logic here. + + While DW_AT_bit_offset was deprecated in DWARF 4 (and removed + entirely from DWARF 5), it is still useful because it is the only + way to describe a field that appears at a non-constant bit + offset. + + FIELD is updated in-place. It is assumed that FIELD already has a + constant bit position. BIT_OFFSET is the value of the + DW_AT_bit_offset attribute, and EXPLICIT_BYTE_SIZE is either the + value of a DW_AT_byte_size from the field's DIE -- indicating an + explicit size of the enclosing anonymous object -- or it may be 0, + indicating that the field's type size should be used. */ + +extern void apply_bit_offset_to_field (struct field &field, + LONGEST bit_offset, + LONGEST explicit_byte_size); + extern struct type *check_typedef (struct type *); extern void check_stub_method_group (struct type *, int); diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c index 4850575..cde22e5 100644 --- a/gdb/guile/scm-color.c +++ b/gdb/guile/scm-color.c @@ -24,6 +24,7 @@ #include "language.h" #include "arch-utils.h" #include "guile-internal.h" +#include "cli/cli-style.h" /* A GDB color. */ @@ -354,8 +355,14 @@ gdbscm_color_escape_sequence (SCM self, SCM is_fg_scm) const ui_file_style::color &color = coscm_get_color (self); SCM_ASSERT_TYPE (gdbscm_is_bool (is_fg_scm), is_fg_scm, SCM_ARG2, FUNC_NAME, _("boolean")); - bool is_fg = gdbscm_is_true (is_fg_scm); - std::string s = color.to_ansi (is_fg); + + std::string s; + if (term_cli_styling ()) + { + bool is_fg = gdbscm_is_true (is_fg_scm); + s = color.to_ansi (is_fg); + } + return gdbscm_scm_from_host_string (s.c_str (), s.size ()); } diff --git a/gdb/guile/scm-ports.c b/gdb/guile/scm-ports.c index ed43d64..f3e3ec8 100644 --- a/gdb/guile/scm-ports.c +++ b/gdb/guile/scm-ports.c @@ -336,9 +336,15 @@ ioscm_flush (SCM port) return; if (scm_is_eq (port, error_port_scm)) - gdb_flush (gdb_stderr); + { + if (gdb_stderr != nullptr) + gdb_flush (gdb_stderr); + } else - gdb_flush (gdb_stdout); + { + if (gdb_stdout != nullptr) + gdb_flush (gdb_stdout); + } } #else /* !USING_GUILE_BEFORE_2_2 */ diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index bbffb3d..0b08e12 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -630,9 +630,9 @@ mapping_is_anonymous_p (const char *filename) return 0; } -/* Return 0 if the memory mapping (which is related to FILTERFLAGS, V, - MAYBE_PRIVATE_P, MAPPING_ANONYMOUS_P, ADDR and OFFSET) should not - be dumped, or greater than 0 if it should. +/* Return false if the memory mapping represented by MAP should not be + dumped, or true if it should. FILTERFLAGS guides which mappings + should be dumped. In a nutshell, this is the logic that we follow in order to decide if a mapping should be dumped or not. @@ -677,11 +677,14 @@ mapping_is_anonymous_p (const char *filename) header (of a DSO or an executable, for example). If it is, and if the user is interested in dump it, then we should dump it. */ -static int -dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v, - int maybe_private_p, int mapping_anon_p, int mapping_file_p, - const char *filename, ULONGEST addr, ULONGEST offset) +static bool +dump_mapping_p (filter_flags filterflags, const smaps_data &map) { + /* Older Linux kernels did not support the "Anonymous:" counter. + If it is missing, we can't be sure what to dump, so dump everything. */ + if (!map.has_anonymous) + return true; + /* Initially, we trust in what we received from our caller. This value may not be very precise (i.e., it was probably gathered from the permission line in the /proc/PID/smaps list, which @@ -689,41 +692,42 @@ dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v, what we have until we take a look at the "VmFlags:" field (assuming that the version of the Linux kernel being used supports it, of course). */ - int private_p = maybe_private_p; - int dump_p; + int private_p = map.priv; /* We always dump vDSO and vsyscall mappings, because it's likely that there'll be no file to read the contents from at core load time. The kernel does the same. */ - if (strcmp ("[vdso]", filename) == 0 - || strcmp ("[vsyscall]", filename) == 0) - return 1; + if (map.filename == "[vdso]" || map.filename == "[vsyscall]") + return true; - if (v->initialized_p) + if (map.vmflags.initialized_p) { /* We never dump I/O mappings. */ - if (v->io_page) - return 0; + if (map.vmflags.io_page) + return false; /* Check if we should exclude this mapping. */ - if (!dump_excluded_mappings && v->exclude_coredump) - return 0; + if (!dump_excluded_mappings && map.vmflags.exclude_coredump) + return false; /* Update our notion of whether this mapping is shared or private based on a trustworthy value. */ - private_p = !v->shared_mapping; + private_p = !map.vmflags.shared_mapping; /* HugeTLB checking. */ - if (v->uses_huge_tlb) + if (map.vmflags.uses_huge_tlb) { if ((private_p && (filterflags & COREFILTER_HUGETLB_PRIVATE)) || (!private_p && (filterflags & COREFILTER_HUGETLB_SHARED))) - return 1; + return true; - return 0; + return false; } } + int mapping_anon_p = map.mapping_anon_p; + int mapping_file_p = map.mapping_file_p; + bool dump_p; if (private_p) { if (mapping_anon_p && mapping_file_p) @@ -763,7 +767,7 @@ dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v, A mapping contains an ELF header if it is a private mapping, its offset is zero, and its first word is ELFMAG. */ - if (!dump_p && private_p && offset == 0 + if (!dump_p && private_p && map.offset == 0 && (filterflags & COREFILTER_ELF_HEADERS) != 0) { /* Useful define specifying the size of the ELF magical @@ -774,7 +778,7 @@ dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v, /* Let's check if we have an ELF header. */ gdb_byte h[SELFMAG]; - if (target_read_memory (addr, h, SELFMAG) == 0) + if (target_read_memory (map.start_address, h, SELFMAG) == 0) { /* The EI_MAG* and ELFMAG* constants come from <elf/common.h>. */ @@ -783,7 +787,7 @@ dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v, { /* This mapping contains an ELF header, so we should dump it. */ - dump_p = 1; + dump_p = true; } } } @@ -794,20 +798,25 @@ dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v, /* As above, but return true only when we should dump the NT_FILE entry. */ -static int -dump_note_entry_p (filter_flags filterflags, const struct smaps_vmflags *v, - int maybe_private_p, int mapping_anon_p, int mapping_file_p, - const char *filename, ULONGEST addr, ULONGEST offset) +static bool +dump_note_entry_p (filter_flags filterflags, const smaps_data &map) { + /* No NT_FILE entry for mappings with no filename. */ + if (map.filename.length () == 0) + return false; + + /* Don't add NT_FILE entries for mappings with a zero inode. */ + if (map.inode == 0) + return false; + /* vDSO and vsyscall mappings will end up in the core file. Don't put them in the NT_FILE note. */ - if (strcmp ("[vdso]", filename) == 0 - || strcmp ("[vsyscall]", filename) == 0) - return 0; + if (map.filename == "[vdso]" || map.filename == "[vsyscall]") + return false; /* Otherwise, any other file-based mapping should be placed in the note. */ - return 1; + return true; } /* Implement the "info proc" command. */ @@ -1314,21 +1323,15 @@ linux_core_xfer_siginfo (struct gdbarch *gdbarch, gdb_byte *readbuf, } typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size, - ULONGEST offset, ULONGEST inode, + ULONGEST offset, int read, int write, int exec, int modified, bool memory_tagged, - const char *filename, + const std::string &filename, void *data); -typedef int linux_dump_mapping_p_ftype (filter_flags filterflags, - const struct smaps_vmflags *v, - int maybe_private_p, - int mapping_anon_p, - int mapping_file_p, - const char *filename, - ULONGEST addr, - ULONGEST offset); +typedef bool linux_dump_mapping_p_ftype (filter_flags filterflags, + const smaps_data &map); /* Helper function to parse the contents of /proc/<pid>/smaps into a data structure, for easy access. @@ -1590,35 +1593,15 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, for (const struct smaps_data &map : smaps) { - int should_dump_p = 0; - - if (map.has_anonymous) - { - should_dump_p - = should_dump_mapping_p (filterflags, &map.vmflags, - map.priv, - map.mapping_anon_p, - map.mapping_file_p, - map.filename.c_str (), - map.start_address, - map.offset); - } - else - { - /* Older Linux kernels did not support the "Anonymous:" counter. - If it is missing, we can't be sure - dump all the pages. */ - should_dump_p = 1; - } - /* Invoke the callback function to create the corefile segment. */ - if (should_dump_p) + if (should_dump_mapping_p (filterflags, map)) { func (map.start_address, map.end_address - map.start_address, - map.offset, map.inode, map.read, map.write, map.exec, + map.offset, map.read, map.write, map.exec, 1, /* MODIFIED is true because we want to dump the mapping. */ map.vmflags.memory_tagging != 0, - map.filename.c_str (), obfd); + map.filename, obfd); } } @@ -1644,10 +1627,10 @@ struct linux_find_memory_regions_data static int linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST size, - ULONGEST offset, ULONGEST inode, + ULONGEST offset, int read, int write, int exec, int modified, bool memory_tagged, - const char *filename, void *arg) + const std::string &filename, void *arg) { struct linux_find_memory_regions_data *data = (struct linux_find_memory_regions_data *) arg; @@ -1693,8 +1676,6 @@ struct linux_make_mappings_data struct type *long_type; }; -static linux_find_memory_region_ftype linux_make_mappings_callback; - /* A callback for linux_find_memory_regions_full that updates the mappings data for linux_make_mappings_corefile_notes. @@ -1703,17 +1684,16 @@ static linux_find_memory_region_ftype linux_make_mappings_callback; static int linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size, - ULONGEST offset, ULONGEST inode, + ULONGEST offset, int read, int write, int exec, int modified, bool memory_tagged, - const char *filename, void *data) + const std::string &filename, void *data) { struct linux_make_mappings_data *map_data = (struct linux_make_mappings_data *) data; gdb_byte buf[sizeof (ULONGEST)]; - if (*filename == '\0' || inode == 0) - return 0; + gdb_assert (filename.length () > 0); ++map_data->file_count; @@ -1724,7 +1704,7 @@ linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size, pack_long (buf, map_data->long_type, offset); obstack_grow (map_data->data_obstack, buf, map_data->long_type->length ()); - obstack_grow_str0 (map_data->filename_obstack, filename); + obstack_grow_str0 (map_data->filename_obstack, filename.c_str ()); return 0; } diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c index 092127dd..d79ec68 100644 --- a/gdb/loongarch-tdep.c +++ b/gdb/loongarch-tdep.c @@ -74,7 +74,9 @@ loongarch_insn_is_cond_branch (insn_t insn) || (insn & 0xfc000000) == 0x68000000 /* bltu */ || (insn & 0xfc000000) == 0x6c000000 /* bgeu */ || (insn & 0xfc000000) == 0x40000000 /* beqz */ - || (insn & 0xfc000000) == 0x44000000) /* bnez */ + || (insn & 0xfc000000) == 0x44000000 /* bnez */ + || (insn & 0xfc000300) == 0x48000000 /* bceqz */ + || (insn & 0xfc000300) == 0x48000100) /* bcnez */ return true; return false; } @@ -314,6 +316,20 @@ loongarch_next_pc (struct regcache *regcache, CORE_ADDR cur_pc) if (rj != 0) next_pc = cur_pc + loongarch_decode_imm ("0:5|10:16<<2", insn, 1); } + else if ((insn & 0xfc000300) == 0x48000000) /* bceqz cj, offs21 */ + { + LONGEST cj = regcache_raw_get_signed (regcache, + loongarch_decode_imm ("5:3", insn, 0) + LOONGARCH_FIRST_FCC_REGNUM); + if (cj == 0) + next_pc = cur_pc + loongarch_decode_imm ("0:5|10:16<<2", insn, 1); + } + else if ((insn & 0xfc000300) == 0x48000100) /* bcnez cj, offs21 */ + { + LONGEST cj = regcache_raw_get_signed (regcache, + loongarch_decode_imm ("5:3", insn, 0) + LOONGARCH_FIRST_FCC_REGNUM); + if (cj != 0) + next_pc = cur_pc + loongarch_decode_imm ("0:5|10:16<<2", insn, 1); + } else if ((insn & 0xffff8000) == 0x002b0000) /* syscall */ { if (tdep->syscall_next_pc != nullptr) @@ -672,6 +672,8 @@ captured_main_1 (struct captured_main_args *context) /* Ensure stderr is unbuffered. A Cygwin pty or pipe is implemented as a Windows pipe, and Windows buffers on pipes. */ setvbuf (stderr, NULL, _IONBF, BUFSIZ); + + windows_initialize_console (); #endif /* Note: `error' cannot be called before this point, because the @@ -44,6 +44,9 @@ extern std::string interpreter_p; return value is in malloc'ed storage. */ extern char *windows_get_absolute_argv0 (const char *argv0); +/* Initialize Windows console settings. */ +extern void windows_initialize_console (); + extern void set_gdb_data_directory (const char *new_data_dir); #endif /* GDB_MAIN_H */ diff --git a/gdb/maint.c b/gdb/maint.c index f5977ec..c6f9b32 100644 --- a/gdb/maint.c +++ b/gdb/maint.c @@ -39,6 +39,7 @@ #include "inferior.h" #include "gdbsupport/thread-pool.h" #include "event-top.h" +#include "cp-support.h" #include "cli/cli-decode.h" #include "cli/cli-utils.h" @@ -108,6 +109,18 @@ maintenance_demangle (const char *args, int from_tty) styled_string (command_style.style (), "demangle")); } +/* Print the canonical form of a name. */ + +static void +maintenance_canonicalize (const char *args, int from_tty) +{ + gdb::unique_xmalloc_ptr<char> canon = cp_canonicalize_string (args); + if (canon == nullptr) + gdb_printf ("No change.\n"); + else + gdb_printf ("canonical = %s\n", canon.get ()); +} + static void maintenance_time_display (const char *args, int from_tty) { @@ -1138,6 +1151,50 @@ set_per_command_cmd (const char *args, int from_tty) } } +/* See maint.h. */ + +scoped_time_it::scoped_time_it (const char *what) + : m_enabled (per_command_time), + m_what (what), + m_start_wall (m_enabled + ? std::chrono::steady_clock::now () + : std::chrono::steady_clock::time_point ()) +{ + if (m_enabled) + get_run_time (m_start_user, m_start_sys, run_time_scope::thread); +} + +/* See maint.h. */ + +scoped_time_it::~scoped_time_it () +{ + if (!m_enabled) + return; + + namespace chr = std::chrono; + auto end_wall = chr::steady_clock::now (); + + user_cpu_time_clock::time_point end_user; + system_cpu_time_clock::time_point end_sys; + get_run_time (end_user, end_sys, run_time_scope::thread); + + auto user = end_user - m_start_user; + auto sys = end_sys - m_start_sys; + auto wall = end_wall - m_start_wall; + auto user_ms = chr::duration_cast<chr::milliseconds> (user).count (); + auto sys_ms = chr::duration_cast<chr::milliseconds> (sys).count (); + auto wall_ms = chr::duration_cast<chr::milliseconds> (wall).count (); + auto user_plus_sys_ms = user_ms + sys_ms; + + auto str + = string_printf ("Time for \"%s\": wall %.03f, user %.03f, sys %.03f, " + "user+sys %.03f, %.01f %% CPU\n", + m_what, wall_ms / 1000.0, user_ms / 1000.0, + sys_ms / 1000.0, user_plus_sys_ms / 1000.0, + user_plus_sys_ms * 100.0 / wall_ms); + gdb_stdlog->write_async_safe (str.data (), str.size ()); +} + /* Options affecting the "maintenance selftest" command. */ struct maintenance_selftest_options @@ -1343,6 +1400,12 @@ This command has been moved to \"demangle\"."), &maintenancelist); deprecate_cmd (cmd, "demangle"); + cmd = add_cmd ("canonicalize", class_maintenance, maintenance_canonicalize, + _("\ +Show the canonical form of a C++ name.\n\ +Usage: maintenance canonicalize NAME"), + &maintenancelist); + add_prefix_cmd ("per-command", class_maintenance, set_per_command_cmd, _("\ Per-command statistics settings."), &per_command_setlist, diff --git a/gdb/maint.h b/gdb/maint.h index 0ddc62b..28e6280 100644 --- a/gdb/maint.h +++ b/gdb/maint.h @@ -70,6 +70,33 @@ class scoped_command_stats int m_start_nr_blocks; }; +/* RAII structure used to measure the time spent by the current thread in a + given scope. */ + +struct scoped_time_it +{ + /* WHAT is the prefix to show when the summary line is printed. */ + scoped_time_it (const char *what); + + DISABLE_COPY_AND_ASSIGN (scoped_time_it); + ~scoped_time_it (); + +private: + bool m_enabled; + + /* Summary line prefix. */ + const char *m_what; + + /* User time at the start of execution. */ + user_cpu_time_clock::time_point m_start_user; + + /* System time at the start of execution. */ + system_cpu_time_clock::time_point m_start_sys; + + /* Wall-clock time at the start of execution. */ + std::chrono::steady_clock::time_point m_start_wall; +}; + extern obj_section *maint_obj_section_from_bfd_section (bfd *abfd, asection *asection, objfile *ofile); diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index 850a9ab..cda72ca 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -2218,7 +2218,7 @@ timestamp (struct mi_timestamp *tv) using namespace std::chrono; tv->wallclock = steady_clock::now (); - run_time_clock::now (tv->utime, tv->stime); + get_run_time (tv->utime, tv->stime, run_time_scope::process); } static void diff --git a/gdb/mingw-hdep.c b/gdb/mingw-hdep.c index dc7ca42..84a7b9f 100644 --- a/gdb/mingw-hdep.c +++ b/gdb/mingw-hdep.c @@ -22,6 +22,7 @@ #include "gdbsupport/event-loop.h" #include "gdbsupport/gdb_select.h" #include "inferior.h" +#include "cli/cli-style.h" #include <windows.h> #include <signal.h> @@ -212,7 +213,30 @@ static int mingw_console_initialized; static HANDLE hstdout = INVALID_HANDLE_VALUE; /* Text attribute to use for normal text (the "none" pseudo-color). */ -static SHORT norm_attr; +static SHORT norm_attr; + +/* Initialize settings related to the console. */ + +void +windows_initialize_console () +{ + hstdout = (HANDLE)_get_osfhandle (fileno (stdout)); + DWORD cmode; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (hstdout != INVALID_HANDLE_VALUE + && GetConsoleMode (hstdout, &cmode) != 0 + && GetConsoleScreenBufferInfo (hstdout, &csbi)) + { + norm_attr = csbi.wAttributes; + mingw_console_initialized = 1; + } + else if (hstdout != INVALID_HANDLE_VALUE) + mingw_console_initialized = -1; /* valid, but not a console device */ + + if (mingw_console_initialized > 0) + no_emojis (); +} /* The most recently applied style. */ static ui_file_style last_style; @@ -223,22 +247,6 @@ static ui_file_style last_style; int gdb_console_fputs (const char *linebuf, FILE *fstream) { - if (!mingw_console_initialized) - { - hstdout = (HANDLE)_get_osfhandle (fileno (fstream)); - DWORD cmode; - CONSOLE_SCREEN_BUFFER_INFO csbi; - - if (hstdout != INVALID_HANDLE_VALUE - && GetConsoleMode (hstdout, &cmode) != 0 - && GetConsoleScreenBufferInfo (hstdout, &csbi)) - { - norm_attr = csbi.wAttributes; - mingw_console_initialized = 1; - } - else if (hstdout != INVALID_HANDLE_VALUE) - mingw_console_initialized = -1; /* valid, but not a console device */ - } /* If our stdout is not a console device, let the default 'fputs' handle the task. */ if (mingw_console_initialized <= 0) diff --git a/gdb/minsyms.c b/gdb/minsyms.c index 9ac3145..124d96d 100644 --- a/gdb/minsyms.c +++ b/gdb/minsyms.c @@ -37,6 +37,7 @@ #include <ctype.h> +#include "maint.h" #include "symtab.h" #include "bfd.h" #include "filenames.h" @@ -1481,6 +1482,8 @@ minimal_symbol_reader::install () gdb::parallel_for_each (10, &msymbols[0], &msymbols[mcount], [&] (minimal_symbol *start, minimal_symbol *end) { + scoped_time_it time_it ("minsyms install worker"); + for (minimal_symbol *msym = start; msym < end; ++msym) { size_t idx = msym - msymbols; diff --git a/gdb/parse.c b/gdb/parse.c index 3108017..64653c8 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -60,8 +60,8 @@ show_expressiondebug (struct ui_file *file, int from_tty, } -/* True if an expression parser should set yydebug. */ -static bool parser_debug; +/* See parser-defs.h. */ +bool parser_debug; static void show_parserdebug (struct ui_file *file, int from_tty, diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h index c13a56e..f5618f3 100644 --- a/gdb/parser-defs.h +++ b/gdb/parser-defs.h @@ -389,4 +389,7 @@ extern bool fits_in_type (int n_sign, const gdb_mpz &n, int type_bits, extern void parser_fprintf (FILE *, const char *, ...) ATTRIBUTE_PRINTF (2, 3); +/* True if an expression parser should set yydebug. */ +extern bool parser_debug; + #endif /* GDB_PARSER_DEFS_H */ diff --git a/gdb/printcmd.c b/gdb/printcmd.c index 2be5eaa..6659c5a 100644 --- a/gdb/printcmd.c +++ b/gdb/printcmd.c @@ -1320,7 +1320,9 @@ process_print_command_args (const char *args, value_print_options *print_opts, value, so invert it for parse_expression. */ parser_flags flags = 0; if (!voidprint) - flags = PARSER_VOID_CONTEXT; + flags |= PARSER_VOID_CONTEXT; + if (parser_debug) + flags |= PARSER_DEBUG; expression_up expr = parse_expression (exp, nullptr, flags); return expr->evaluate (); } diff --git a/gdb/progspace.c b/gdb/progspace.c index 569dfc8..eda6379 100644 --- a/gdb/progspace.c +++ b/gdb/progspace.c @@ -202,12 +202,14 @@ program_space::exec_close () if (ebfd != nullptr) { /* Removing target sections may close the exec_ops target. - Clear ebfd before doing so to prevent recursion. */ - bfd *saved_ebfd = ebfd.get (); + Clear ebfd before doing so to prevent recursion. We + move it to another ref_ptr instead of saving it to a raw + pointer to avoid it looking like possible use-after-free. */ + gdb_bfd_ref_ptr saved_ebfd = std::move (ebfd); ebfd.reset (nullptr); ebfd_mtime = 0; - remove_target_sections (saved_ebfd); + remove_target_sections (saved_ebfd.get ()); m_exec_filename.reset (); } diff --git a/gdb/python/lib/gdb/dap/sources.py b/gdb/python/lib/gdb/dap/sources.py index 625c01f..efcd799 100644 --- a/gdb/python/lib/gdb/dap/sources.py +++ b/gdb/python/lib/gdb/dap/sources.py @@ -64,9 +64,9 @@ def decode_source(source): """Decode a Source object. Finds and returns the filename of a given Source object.""" - if "path" in source: - return source["path"] - if "sourceReference" not in source: + if "sourceReference" not in source or source["sourceReference"] <= 0: + if "path" in source: + return source["path"] raise DAPException("either 'path' or 'sourceReference' must appear in Source") ref = source["sourceReference"] if ref not in _id_map: diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c index e208506..3bbd22d 100644 --- a/gdb/python/py-color.c +++ b/gdb/python/py-color.c @@ -21,6 +21,7 @@ #include "python-internal.h" #include "py-color.h" #include "cli/cli-decode.h" +#include "cli/cli-style.h" /* Colorspace constants and their values. */ static struct { @@ -152,8 +153,12 @@ colorpy_escape_sequence (PyObject *self, PyObject *args, PyObject *kwargs) /* The argument parsing ensures we have a bool. */ gdb_assert (PyBool_Check (is_fg_obj)); - bool is_fg = is_fg_obj == Py_True; - std::string s = gdbpy_get_color (self).to_ansi (is_fg); + std::string s; + if (term_cli_styling ()) + { + bool is_fg = is_fg_obj == Py_True; + s = gdbpy_get_color (self).to_ansi (is_fg); + } return host_string_to_python_string (s.c_str ()).release (); } diff --git a/gdb/python/python.c b/gdb/python/python.c index 7e28eb6..24cb511 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1612,16 +1612,19 @@ gdbpy_flush (PyObject *self, PyObject *args, PyObject *kw) { case 1: { - gdb_flush (gdb_stderr); + if (gdb_stderr != nullptr) + gdb_flush (gdb_stderr); break; } case 2: { - gdb_flush (gdb_stdlog); + if (gdb_stdlog != nullptr) + gdb_flush (gdb_stdlog); break; } default: - gdb_flush (gdb_stdout); + if (gdb_stdout != nullptr) + gdb_flush (gdb_stdout); } Py_RETURN_NONE; diff --git a/gdb/ravenscar-thread.c b/gdb/ravenscar-thread.c index 13258c4..45205a0 100644 --- a/gdb/ravenscar-thread.c +++ b/gdb/ravenscar-thread.c @@ -503,7 +503,7 @@ ravenscar_thread_target::pid_to_str (ptid_t ptid) return beneath ()->pid_to_str (ptid); return string_printf ("Ravenscar Thread 0x%s", - phex_nz (ptid.tid (), sizeof (ULONGEST))); + phex_nz (ptid.tid ())); } CORE_ADDR diff --git a/gdb/remote.c b/gdb/remote.c index 73dc426..1c49cdf 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -11650,7 +11650,7 @@ remote_target::remote_write_qxfer (const char *object_name, i = snprintf (rs->buf.data (), max_size, "qXfer:%s:write:%s:%s:", object_name, annex ? annex : "", - phex_nz (offset, sizeof offset)); + phex_nz (offset)); max_size -= (i + 1); /* Escape as much data as fits into rs->buf. */ @@ -11715,8 +11715,8 @@ remote_target::remote_read_qxfer (const char *object_name, snprintf (rs->buf.data (), get_remote_packet_size () - 4, "qXfer:%s:read:%s:%s,%s", object_name, annex ? annex : "", - phex_nz (offset, sizeof offset), - phex_nz (n, sizeof n)); + phex_nz (offset), + phex_nz (n)); i = putpkt (rs->buf); if (i < 0) return TARGET_XFER_E_IO; @@ -12014,7 +12014,7 @@ remote_target::search_memory (CORE_ADDR start_addr, ULONGEST search_space_len, i = snprintf (rs->buf.data (), max_size, "qSearch:memory:%s;%s;", phex_nz (start_addr, addr_size), - phex_nz (search_space_len, sizeof (search_space_len))); + phex_nz (search_space_len)); max_size -= (i + 1); /* Escape as much data as fits into rs->buf. */ @@ -13763,7 +13763,7 @@ remote_target::download_tracepoint (struct bp_location *loc) encode_actions_rsp (loc, &tdp_actions, &stepping_actions); tpaddr = loc->address; - strcpy (addrbuf, phex (tpaddr, sizeof (CORE_ADDR))); + strcpy (addrbuf, phex (tpaddr)); ret = snprintf (buf.data (), buf.size (), "QTDP:%x:%s:%c:%lx:%x", b->number, addrbuf, /* address */ (b->enable_state == bp_enabled ? 'E' : 'D'), @@ -14027,7 +14027,7 @@ remote_target::enable_tracepoint (struct bp_location *location) xsnprintf (rs->buf.data (), get_remote_packet_size (), "QTEnable:%x:%s", location->owner->number, - phex (location->address, sizeof (CORE_ADDR))); + phex (location->address)); putpkt (rs->buf); remote_get_noisy_reply (); if (rs->buf[0] == '\0') @@ -14043,7 +14043,7 @@ remote_target::disable_tracepoint (struct bp_location *location) xsnprintf (rs->buf.data (), get_remote_packet_size (), "QTDisable:%x:%s", location->owner->number, - phex (location->address, sizeof (CORE_ADDR))); + phex (location->address)); putpkt (rs->buf); remote_get_noisy_reply (); if (rs->buf[0] == '\0') @@ -15569,7 +15569,7 @@ remote_target::commit_requested_thread_options () *obuf_p++ = ';'; obuf_p += xsnprintf (obuf_p, obuf_endp - obuf_p, "%s", - phex_nz (options, sizeof (options))); + phex_nz (options)); if (tp->ptid != magic_null_ptid) { *obuf_p++ = ':'; @@ -15808,8 +15808,8 @@ create_fetch_memtags_request (gdb::char_vector &packet, CORE_ADDR address, std::string request = string_printf ("qMemTags:%s,%s:%s", phex_nz (address, addr_size), - phex_nz (len, sizeof (len)), - phex_nz (type, sizeof (type))); + phex_nz (len), + phex_nz (type)); strcpy (packet.data (), request.c_str ()); } @@ -15843,8 +15843,8 @@ create_store_memtags_request (gdb::char_vector &packet, CORE_ADDR address, /* Put together the main packet, address and length. */ std::string request = string_printf ("QMemTags:%s,%s:%s:", phex_nz (address, addr_size), - phex_nz (len, sizeof (len)), - phex_nz (type, sizeof (type))); + phex_nz (len), + phex_nz (type)); request += bin2hex (tags.data (), tags.size ()); /* Check if we have exceeded the maximum packet size. */ diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c index a735c09..8998a29 100644 --- a/gdb/riscv-tdep.c +++ b/gdb/riscv-tdep.c @@ -4884,7 +4884,7 @@ try_read (struct regcache *regcache, int regnum, ULONGEST &addr) if (regcache->raw_read (regnum, &addr) != register_status::REG_VALID) { - warning (_("Can not read at address %lx"), addr); + warning (_("Can not read at address %s"), hex_string (addr)); return false; } return true; @@ -5270,8 +5270,8 @@ private: || try_save_pc_rd_mem (ival, regcache)) return !has_error (); - warning (_("Currently this instruction with len 4(%lx) is unsupported"), - ival); + warning (_("Currently this instruction with len 4(%s) is unsupported"), + hex_string (ival)); return false; } @@ -5380,8 +5380,8 @@ private: || !save_mem (addr + offset, 4) || set_ordinary_record_type ()); } - warning (_("Currently this instruction with len 2(%lx) is unsupported"), - ival); + warning (_("Currently this instruction with len 2(%s) is unsupported"), + hex_string (ival)); return false; } @@ -5415,7 +5415,8 @@ public: are not defined yet, so just ignore it. */ gdb_assert (m_length > 0 && m_length % 2 == 0); - warning (_("Can not record unknown instruction (opcode = %lx)"), ival); + warning (_("Can not record unknown instruction (opcode = %s)"), + hex_string (ival)); return false; } diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 458c4ba..9f56d86 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -2058,9 +2058,9 @@ solist_update_incremental (svr4_info *info, CORE_ADDR debug_base, /* Unknown key=value pairs are ignored by the gdbstub. */ xsnprintf (annex, sizeof (annex), "lmid=%s;start=%s;prev=%s", - phex_nz (debug_base, sizeof (debug_base)), - phex_nz (lm, sizeof (lm)), - phex_nz (prev_lm, sizeof (prev_lm))); + phex_nz (debug_base), + phex_nz (lm), + phex_nz (prev_lm)); if (!svr4_current_sos_via_xfer_libraries (&library_list, annex)) return 0; diff --git a/gdb/solib.c b/gdb/solib.c index 5c5cfbd..85ec6bb 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -1230,7 +1230,7 @@ info_linker_namespace_command (const char *pattern, int from_tty) break; } uiout->message - (_ ("There are %ld libraries loaded in linker namespace [[%d]]\n"), + (_ ("There are %zu libraries loaded in linker namespace [[%d]]\n"), solibs_to_print.size (), ns); uiout->message (_ ("Displaying libraries for linker namespace [[%d]]:\n"), ns); diff --git a/gdb/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py index 40039bb..c7dda93 100755 --- a/gdb/syscalls/riscv-canonicalize-syscall-gen.py +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py @@ -111,7 +111,7 @@ class Generator: canon_syscalls[syscall_num] = value # this is a place for corner cases elif syscall_name == "mmap": - gdb_old_syscall_name = "gdb_sys_old_mmap" + gdb_old_syscall_name = "gdb_old_mmap" value = ( f" case {syscall_num}: return {gdb_old_syscall_name};\n" ) diff --git a/gdb/testsuite/Makefile.in b/gdb/testsuite/Makefile.in index 0d5ad90..4a6665d 100644 --- a/gdb/testsuite/Makefile.in +++ b/gdb/testsuite/Makefile.in @@ -440,14 +440,15 @@ expect-read1 expect-readmore: # function, making it read one byte at a time. Running the testsuite # with this catches racy tests. read1.so: lib/read1.c - $(ECHO_CC) $(CC) -o $@ ${srcdir}/lib/read1.c -Wall -g -shared -fPIC $(CFLAGS) + $(ECHO_CC) $(CC) -o $@ ${srcdir}/lib/read1.c -Wall -g -shared -fPIC \ + $(filter-out -fsanitize=%,$(CFLAGS)) # Build the readmore.so preload library. This overrides the `read' # function, making it try harder to read more at a time. Running the # testsuite with this catches racy tests. readmore.so: lib/read1.c $(ECHO_CC) $(CC) -o $@ ${srcdir}/lib/read1.c -Wall -g -shared -fPIC \ - $(CFLAGS) -DREADMORE + $(filter-out -fsanitize=%,$(CFLAGS)) -DREADMORE # Build the read1 machinery. .PHONY: read1 readmore diff --git a/gdb/testsuite/gdb.ada/array_subscript_addr/p.adb b/gdb/testsuite/gdb.ada/array_subscript_addr/p.adb index eaa35c5..057d7a0 100644 --- a/gdb/testsuite/gdb.ada/array_subscript_addr/p.adb +++ b/gdb/testsuite/gdb.ada/array_subscript_addr/p.adb @@ -14,11 +14,12 @@ -- along with this program. If not, see <http://www.gnu.org/licenses/>. procedure P is - type Table is array (1 .. 3) of Integer; + -- Make this large enough to force it into memory with gnat-llvm. + type Table is array (1 .. 15) of Integer; function Create (I : Integer) return Table is begin - return (4 + I, 8 * I, 7 * I + 4); + return (4 + I, 8 * I, 7 * I + 4, others => 72); end Create; A : Table := Create (7); diff --git a/gdb/testsuite/gdb.ada/bp_inlined_func.exp b/gdb/testsuite/gdb.ada/bp_inlined_func.exp index 6593d1e..04cf755 100644 --- a/gdb/testsuite/gdb.ada/bp_inlined_func.exp +++ b/gdb/testsuite/gdb.ada/bp_inlined_func.exp @@ -41,8 +41,10 @@ gdb_test "break read_small" \ for {set i 0} {$i < 4} {incr i} { with_test_prefix "iteration $i" { + # gnat-llvm may emit a call to an out-of-line copy, so allow + # for this here. gdb_test "continue" \ - "Breakpoint $bkptno_num_re, b\\.read_small \\(\\).*" \ + "Breakpoint $bkptno_num_re, ($hex in )?b\\.read_small \\(\\).*" \ "stopped in read_small" } } diff --git a/gdb/testsuite/gdb.ada/dyn-bit-offset.exp b/gdb/testsuite/gdb.ada/dyn-bit-offset.exp new file mode 100644 index 0000000..19d16b1 --- /dev/null +++ b/gdb/testsuite/gdb.ada/dyn-bit-offset.exp @@ -0,0 +1,46 @@ +# Copyright 2025 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +load_lib "ada.exp" + +require allow_ada_tests + +standard_ada_testfile exam + +set flags {debug} +if {[ada_minimal_encodings]} { + lappend flags additional_flags=-fgnat-encodings=minimal +} + +if {[gdb_compile_ada "${srcfile}" "${binfile}" executable $flags] != ""} { + return -1 +} + +clean_restart ${testfile} + +set bp_location [gdb_get_line_number "STOP" ${testdir}/exam.adb] +runto "exam.adb:$bp_location" + +gdb_test_multiple "print spr" "" { + -re -wrap " = \\(discr => 3, array_field => \\(-5, -6, -7\\), field => -4, another_field => -6\\)" { + pass $gdb_test_name + } + -re -wrap " = \\(discr => 3, array_field => \\(-5, -6, -7\\), field => -4, another_field => -4\\)" { + # A known GCC bug. + xfail $gdb_test_name + } +} + +gdb_test "print spr.field" " = -4" diff --git a/gdb/testsuite/gdb.ada/dyn-bit-offset/exam.adb b/gdb/testsuite/gdb.ada/dyn-bit-offset/exam.adb new file mode 100644 index 0000000..a882afd --- /dev/null +++ b/gdb/testsuite/gdb.ada/dyn-bit-offset/exam.adb @@ -0,0 +1,45 @@ +-- Copyright 2025 Free Software Foundation, Inc. +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. + +procedure Exam is + type Small is range -7 .. -4; + for Small'Size use 2; + + type Packed_Array is array (Integer range <>) of Small; + pragma pack (Packed_Array); + + subtype Range_Int is Natural range 0 .. 7; + + type Some_Packed_Record (Discr : Range_Int := 3) is record + Array_Field : Packed_Array (1 .. Discr); + Field: Small; + case Discr is + when 3 => + Another_Field : Small; + when others => + null; + end case; + end record; + pragma Pack (Some_Packed_Record); + pragma No_Component_Reordering (Some_Packed_Record); + + SPR : Some_Packed_Record := (Discr => 3, + Field => -4, + Another_Field => -6, + Array_Field => (-5, -6, -7)); + +begin + null; -- STOP +end Exam; diff --git a/gdb/testsuite/gdb.ada/fixed_points.exp b/gdb/testsuite/gdb.ada/fixed_points.exp index 8bb9e10..0e65004 100644 --- a/gdb/testsuite/gdb.ada/fixed_points.exp +++ b/gdb/testsuite/gdb.ada/fixed_points.exp @@ -90,6 +90,10 @@ foreach_gnat_encoding scenario flags {all minimal} { # This only started working in GCC 11. if {$scenario == "minimal" && [gnat_version_compare >= 11]} { gdb_test "print fp5_var" " = 3e-19" + + gdb_test "print Float(Object_Fixed) = Float(Semicircle_Delta * 5)" \ + " = true" \ + "examine object_fixed" } # This failed before GCC 10. diff --git a/gdb/testsuite/gdb.ada/fixed_points/fixed_points.adb b/gdb/testsuite/gdb.ada/fixed_points/fixed_points.adb index adab614..94a41b9 100644 --- a/gdb/testsuite/gdb.ada/fixed_points/fixed_points.adb +++ b/gdb/testsuite/gdb.ada/fixed_points/fixed_points.adb @@ -64,6 +64,12 @@ procedure Fixed_Points is for Another_Type'size use 64; Another_Fixed : Another_Type := Another_Delta * 5; + Semicircle_Delta : constant := 1.0/(2**31); + type Semicircle_Type is delta Semicircle_Delta range -1.0 .. (1.0 - Semicircle_Delta); + for Semicircle_Type'small use Semicircle_Delta; + for Semicircle_Type'size use 32; + Object_Fixed : Semicircle_Type := Semicircle_Delta * 5; + begin Base_Object := 1.0/16.0; -- Set breakpoint here Subtype_Object := 1.0/16.0; @@ -75,4 +81,5 @@ begin Do_Nothing (FP4_Var'Address); Do_Nothing (FP5_Var'Address); Do_Nothing (Another_Fixed'Address); + Do_Nothing (Object_Fixed'Address); end Fixed_Points; diff --git a/gdb/testsuite/gdb.ada/null_overload/foo.adb b/gdb/testsuite/gdb.ada/null_overload/foo.adb index 002238f..55d3fd6 100644 --- a/gdb/testsuite/gdb.ada/null_overload/foo.adb +++ b/gdb/testsuite/gdb.ada/null_overload/foo.adb @@ -13,6 +13,8 @@ -- You should have received a copy of the GNU General Public License -- along with this program. If not, see <http://www.gnu.org/licenses/>. +with pck; use pck; + procedure Foo is type R_Type is null record; @@ -38,5 +40,5 @@ procedure Foo is U_Ptr : U_P_T := null; begin - null; -- START + Do_Nothing (U_Ptr'Address); -- START end Foo; diff --git a/gdb/testsuite/gdb.ada/null_overload/pck.adb b/gdb/testsuite/gdb.ada/null_overload/pck.adb new file mode 100644 index 0000000..95bd90a --- /dev/null +++ b/gdb/testsuite/gdb.ada/null_overload/pck.adb @@ -0,0 +1,23 @@ +-- Copyright 2020-2025 Free Software Foundation, Inc. +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. + +package body Pck is + + procedure Do_Nothing (A : System.Address) is + begin + null; + end Do_Nothing; + +end Pck; diff --git a/gdb/testsuite/gdb.ada/null_overload/pck.ads b/gdb/testsuite/gdb.ada/null_overload/pck.ads new file mode 100644 index 0000000..114aee0 --- /dev/null +++ b/gdb/testsuite/gdb.ada/null_overload/pck.ads @@ -0,0 +1,22 @@ +-- Copyright 2020-2025 Free Software Foundation, Inc. +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. + +with System; + +package Pck is + + procedure Do_Nothing (A : System.Address); + +end Pck; diff --git a/gdb/testsuite/gdb.ada/packed_record_2.exp b/gdb/testsuite/gdb.ada/packed_record_2.exp new file mode 100644 index 0000000..d0bcdbd --- /dev/null +++ b/gdb/testsuite/gdb.ada/packed_record_2.exp @@ -0,0 +1,61 @@ +# Copyright 2025 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +load_lib "ada.exp" + +require allow_ada_tests + +standard_ada_testfile exam + +set flags {debug} +if {[ada_minimal_encodings]} { + lappend flags additional_flags=-fgnat-encodings=minimal +} + +if {[gdb_compile_ada "${srcfile}" "${binfile}" executable $flags] != ""} { + return -1 +} + +clean_restart ${testfile} + +set bp_location [gdb_get_line_number "STOP" ${testdir}/exam.adb] +runto "exam.adb:$bp_location" + +set spr_contents "discr => 3, field => -4, array_field => \\(-5, -6, -7\\)" + +gdb_test "print spr" " = \\($spr_contents\\)" + +gdb_test "print spr.discr" " = 3" + +# See PR ada/32880 -- gdb should probably print array (1 .. 3) here, +# but instead shows array (<>). However as this isn't totally +# relevant to this test, we just accept it. +gdb_test "ptype spr" \ + [multi_line \ + "type = tagged record" \ + " discr: range 1 .. 8;" \ + " field: range -7 .. -4;" \ + " array_field: array \\(<>\\) of exam.small <packed: 2-bit elements>;" \ + "end record"] + +gdb_test_multiple "print sc" "" { + -re " \\($spr_contents, outer => 2, another_array => \\(-7, -6\\)\\)" { + pass $gdb_test_name + } + -re " \\($spr_contents, outer => $decimal, another_array => \\(.*\\)\\)" { + # Other output is a known GCC bug. + xfail $gdb_test_name + } +} diff --git a/gdb/testsuite/gdb.ada/packed_record_2/exam.adb b/gdb/testsuite/gdb.ada/packed_record_2/exam.adb new file mode 100644 index 0000000..e528ecf --- /dev/null +++ b/gdb/testsuite/gdb.ada/packed_record_2/exam.adb @@ -0,0 +1,51 @@ +-- Copyright 2025 Free Software Foundation, Inc. +-- +-- This program is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. + +procedure Exam is + type Small is range -7 .. -4; + for Small'Size use 2; + + type Range_Int is range 1 .. 8; + for Range_Int'Size use 3; + + type Packed_Array is array (Range_Int range <>) of Small; + pragma pack (Packed_Array); + + type Some_Packed_Record (Discr : Range_Int) is tagged record + Field: Small; + Array_Field : Packed_Array (1 .. Discr); + end record; + pragma Pack (Some_Packed_Record); + + type Sub_Class (Inner, Outer : Range_Int) + is new Some_Packed_Record (Inner) with + record + Another_Array : Packed_Array (1 .. Outer); + end record; + pragma Pack (Sub_Class); + + SPR : Some_Packed_Record := (Discr => 3, + Field => -4, + Array_Field => (-5, -6, -7)); + + SC : Sub_Class := (Inner => 3, + Outer => 2, + Field => -4, + Array_Field => (-5, -6, -7), + Another_Array => (-7, -6)); + +begin + null; -- STOP +end Exam; diff --git a/gdb/testsuite/gdb.ada/task_switch_in_core.exp b/gdb/testsuite/gdb.ada/task_switch_in_core.exp index 3aafc2b..bded377 100644 --- a/gdb/testsuite/gdb.ada/task_switch_in_core.exp +++ b/gdb/testsuite/gdb.ada/task_switch_in_core.exp @@ -15,7 +15,7 @@ load_lib "ada.exp" -require allow_ada_tests +require allow_ada_tests gcore_cmd_available standard_ada_testfile crash diff --git a/gdb/testsuite/gdb.ada/type-tick-size/prog.adb b/gdb/testsuite/gdb.ada/type-tick-size/prog.adb index 34a9fca..a7457fd 100644 --- a/gdb/testsuite/gdb.ada/type-tick-size/prog.adb +++ b/gdb/testsuite/gdb.ada/type-tick-size/prog.adb @@ -50,6 +50,8 @@ procedure Prog is Rec_Type_Size : Integer := Rec'Object_Size; begin + Do_Nothing (Simple_Val'Address); + Do_Nothing (Rec_Val'Address); Do_Nothing (Static_Blob'Address); Do_Nothing (Dynamic_Blob'Address); null; -- STOP diff --git a/gdb/testsuite/gdb.base/coredump-filter-build-id.exp b/gdb/testsuite/gdb.base/coredump-filter-build-id.exp index 7594cc2..eb5b489 100644 --- a/gdb/testsuite/gdb.base/coredump-filter-build-id.exp +++ b/gdb/testsuite/gdb.base/coredump-filter-build-id.exp @@ -28,7 +28,7 @@ if { ![istarget *-*-linux*] } { untested "$testfile.exp" return -1 } -require is_x86_64_m64_target +require is_x86_64_m64_target gcore_cmd_available if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug build-id}] } { return -1 diff --git a/gdb/testsuite/gdb.base/exprs.exp b/gdb/testsuite/gdb.base/exprs.exp index eb2d0e4..f703c18 100644 --- a/gdb/testsuite/gdb.base/exprs.exp +++ b/gdb/testsuite/gdb.base/exprs.exp @@ -284,3 +284,21 @@ gdb_test "print v_short + " \ # Test for a syntax error in the middle of an expression. gdb_test "print v_short =}{= 3" \ "A syntax error in expression, near `\\}\\{= 3'\\." + +gdb_test_no_output "set debug parse 1" +set saw_start 0 +set saw_val 0 +gdb_test_multiple "print 23" "print with debugging" -lbl { + -re "\r\nStarting parse(?=\r\n)" { + set saw_start 1 + exp_continue + } + -re "\r\n.$decimal = 23(?=\r\n)" { + set saw_val 1 + exp_continue + } + + -re -wrap "" { + gdb_assert {$saw_start && $saw_val} $gdb_test_name + } +} diff --git a/gdb/testsuite/gdb.base/foll-fork-syscall.c b/gdb/testsuite/gdb.base/foll-fork-syscall.c new file mode 100644 index 0000000..ef695f5 --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-fork-syscall.c @@ -0,0 +1,35 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <unistd.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + int pid, x = 0; + + pid = fork (); + if (pid == 0) /* set breakpoint here */ + printf ("I am the child\n"); + else + printf ("I am the parent\n"); + + chdir ("."); + ++x; /* set exit breakpoint here */ + return 0; +} diff --git a/gdb/testsuite/gdb.base/foll-fork-syscall.exp b/gdb/testsuite/gdb.base/foll-fork-syscall.exp new file mode 100644 index 0000000..4aee683 --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-fork-syscall.exp @@ -0,0 +1,142 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Test catching syscalls with all permutations of follow-fork parent/child +# and detach-on-fork on/off. + +# Test relies on checking follow-fork output. Do not run if gdb debug is +# enabled because it will be redirected to the log. +require !gdb_debug_enabled +require {is_any_target "i?86-*-*" "x86_64-*-*"} + +standard_testfile + +if {[build_executable "failed to prepare" $testfile $srcfile debug]} { + return -1 +} + +proc setup_gdb {} { + global testfile + + clean_restart $testfile + + if {![runto_main]} { + return false + } + + # Set a breakpoint after the fork is "complete." + if {![gdb_breakpoint [gdb_get_line_number "set breakpoint here"]]} { + return false + } + + # Set exit breakpoint (to prevent inferior from exiting). + if {![gdb_breakpoint [gdb_get_line_number "set exit breakpoint here"]]} { + return false + } + return true +} + +# Check that fork catchpoints are supported, as an indicator for whether +# fork-following is supported. Return 1 if they are, else 0. + +proc_with_prefix check_fork_catchpoints {} { + global gdb_prompt + + if { ![setup_gdb] } { + return false + } + + # Verify that the system supports "catch fork". + gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "insert first fork catchpoint" + set has_fork_catchpoints false + gdb_test_multiple "continue" "continue to first fork catchpoint" { + -re ".*Your system does not support this type\r\nof catchpoint.*$gdb_prompt $" { + unsupported "continue to first fork catchpoint" + } + -re ".*Catchpoint.*$gdb_prompt $" { + set has_fork_catchpoints true + pass "continue to first fork catchpoint" + } + } + + return $has_fork_catchpoints +} + +proc_with_prefix test_catch_syscall {follow-fork-mode detach-on-fork} { + # Start with shiny new gdb instance. + if {![setup_gdb]} { + return + } + + # The "Detaching..." and "Attaching..." messages may be hidden by + # default. + gdb_test_no_output "set verbose" + + # Setup modes to test. + gdb_test_no_output "set follow-fork-mode ${follow-fork-mode}" + gdb_test_no_output "set detach-on-fork ${detach-on-fork}" + + gdb_test "catch fork" "Catchpoint . \\(fork\\)" + gdb_test "catch syscall chdir" "Catchpoint . \\(syscall 'chdir'.*\\)" + + # Which inferior we're expecting to follow. Assuming the parent + # will be inferior #1, and the child will be inferior #2. + if {${follow-fork-mode} == "parent"} { + set following_inf 1 + } else { + set followin_inf 2 + } + # Next stop should be the fork catchpoint. + set expected_re "" + append expected_re "Catchpoint . \\(forked process.*" + gdb_test "continue" $expected_re "continue to fork catchpoint" + + # Next stop should be the breakpoint after the fork. + set expected_re ".*" + if {${follow-fork-mode} == "child" || ${detach-on-fork} == "off"} { + append expected_re "\\\[New inferior.*" + } + if {${detach-on-fork} == "on"} { + append expected_re "\\\[Detaching after fork from " + if {${follow-fork-mode} == "parent"} { + append expected_re "child" + } else { + append expected_re "parent" + } + append expected_re " process.*" + } + append expected_re "Breakpoint .*set breakpoint here.*" + gdb_test "continue" $expected_re "continue to breakpoint after fork" + + # Next stop should be the syscall catchpoint. + set expected_re ".*Catchpoint . \\(call to syscall chdir\\).*" + gdb_test continue $expected_re "continue to chdir syscall" +} + +# Check for follow-fork support. +if {![check_fork_catchpoints]} { + untested "follow-fork not supported" + return +} + +# Test all permutations. +foreach_with_prefix follow-fork-mode {"parent" "child"} { + + # Do not run tests when not detaching from the parent. + # See breakpoints/13457 for discussion. + foreach_with_prefix detach-on-fork {"on"} { + test_catch_syscall ${follow-fork-mode} ${detach-on-fork} + } +} diff --git a/gdb/testsuite/gdb.base/gcore.exp b/gdb/testsuite/gdb.base/gcore.exp index 5251e3f..0a9f099 100644 --- a/gdb/testsuite/gdb.base/gcore.exp +++ b/gdb/testsuite/gdb.base/gcore.exp @@ -16,6 +16,7 @@ # This file was written by Michael Snyder (msnyder@redhat.com) # This is a test for the gdb command "generate-core-file". +require gcore_cmd_available standard_testfile diff --git a/gdb/testsuite/gdb.base/jit-bfd-name.exp b/gdb/testsuite/gdb.base/jit-bfd-name.exp index 9e4daa1..219929b 100644 --- a/gdb/testsuite/gdb.base/jit-bfd-name.exp +++ b/gdb/testsuite/gdb.base/jit-bfd-name.exp @@ -67,11 +67,13 @@ gdb_breakpoint [gdb_get_line_number "break here 1" $::main_srcfile] gdb_continue_to_breakpoint "break here 1" # Confirm that the two expected functions are available. +set re_f1 [string_to_regexp "int jit_function_0001(void)"] +set re_f2 [string_to_regexp "int jit_function_0002(void)"] gdb_test "info function ^jit_function" \ [multi_line \ "File \[^\r\n\]+jit-elf-solib.c:" \ - "${decimal}:\\s+int jit_function_0001\\(\\);" \ - "${decimal}:\\s+int jit_function_0002\\(\\);"] + "${decimal}:\\s+$re_f1;" \ + "${decimal}:\\s+$re_f2;"] # Capture the addresses of each JIT symfile. set symfile_addrs {} diff --git a/gdb/testsuite/gdb.base/jit-elf-solib.c b/gdb/testsuite/gdb.base/jit-elf-solib.c index 690d7a0..c6fcb89 100644 --- a/gdb/testsuite/gdb.base/jit-elf-solib.c +++ b/gdb/testsuite/gdb.base/jit-elf-solib.c @@ -22,4 +22,4 @@ #error "Must define the FUNCTION_NAME macro to set a jited function name" #endif -int FUNCTION_NAME() { return 42; } +int FUNCTION_NAME(void) { return 42; } diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp index e006d55..52282bc 100644 --- a/gdb/testsuite/gdb.base/maint.exp +++ b/gdb/testsuite/gdb.base/maint.exp @@ -513,4 +513,9 @@ gdb_test_no_output "maint print symbols" gdb_test_no_output "maint print msymbols" gdb_test_no_output "maint print psymbols" +gdb_test "maint canonicalize int short" "canonical = short" +gdb_test "maint canonicalize fn<ty<int>>" \ + "canonical = fn<ty<int> >" +gdb_test "maint canonical unsigned int" "No change\\." + gdb_exit diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp index f32d258..a0947e2 100644 --- a/gdb/testsuite/gdb.base/options.exp +++ b/gdb/testsuite/gdb.base/options.exp @@ -62,8 +62,7 @@ proc check_completion_result {expected test} { # just checking whether GDB recognizes the option and auto-appends a # space. proc test_completer_recognizes {res input_line} { - set expected_re [string_to_regexp $input_line] - test_gdb_complete_unique $input_line $expected_re + test_gdb_complete_unique $input_line $input_line check_completion_result $res $input_line } @@ -509,12 +508,26 @@ proc_with_prefix test-thread-apply {} { proc_with_prefix test-info-threads {} { test_gdb_complete_multiple "info threads " "" "" { "-gid" + "-running" + "-stopped" "ID" } + test_gdb_complete_multiple "info threads " "-" "" { + "-gid" + "-running" + "-stopped" + } + test_gdb_complete_unique \ - "info threads -" \ + "info threads -g" \ "info threads -gid" + test_gdb_complete_unique \ + "info threads -r" \ + "info threads -running" + test_gdb_complete_unique \ + "info threads -s" \ + "info threads -stopped" # "ID" isn't really something the user can type. test_gdb_complete_none "info threads I" diff --git a/gdb/testsuite/gdb.base/print-symbol-loading.exp b/gdb/testsuite/gdb.base/print-symbol-loading.exp index 15f2c19..c9e2480 100644 --- a/gdb/testsuite/gdb.base/print-symbol-loading.exp +++ b/gdb/testsuite/gdb.base/print-symbol-loading.exp @@ -15,7 +15,7 @@ # Test the "print symbol-loading" option. -require allow_shlib_tests +require allow_shlib_tests gcore_cmd_available standard_testfile print-symbol-loading-main.c set libfile print-symbol-loading-lib diff --git a/gdb/testsuite/gdb.base/ptype.exp b/gdb/testsuite/gdb.base/ptype.exp index 788cdfc..6971f4c 100644 --- a/gdb/testsuite/gdb.base/ptype.exp +++ b/gdb/testsuite/gdb.base/ptype.exp @@ -544,10 +544,10 @@ proc ptype_maybe_prototyped { id prototyped plain { overprototyped "NO-MATCH" } fail "ptype $id (compiler doesn't emit prototyped types)" } -re "type = $overprototyped\[\r\n\]+$gdb_prompt $" { - if { [test_compiler_info "armcc-*"] } { - setup_xfail "*-*-*" - } - fail "ptype $id (compiler doesn't emit unprototyped types)" + # C23 no longer supports non-prototype function declaration, in which + # case the overprototyped regexp is the expected one. Simply pass + # in all cases. + pass "ptype $id (overprototyped)" } } } diff --git a/gdb/testsuite/gdb.base/solib-search.exp b/gdb/testsuite/gdb.base/solib-search.exp index 2efad18..35b0314 100644 --- a/gdb/testsuite/gdb.base/solib-search.exp +++ b/gdb/testsuite/gdb.base/solib-search.exp @@ -16,7 +16,7 @@ # Test solib-search-path, and in the case of solib-svr4.c whether l_addr_p # is properly reset when the path is changed. -require allow_shlib_tests +require allow_shlib_tests gcore_cmd_available require {!is_remote target} # Build "wrong" and "right" versions of the libraries in separate directories. diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index 59c93ee..c10be3b 100644 --- a/gdb/testsuite/gdb.base/style.exp +++ b/gdb/testsuite/gdb.base/style.exp @@ -329,6 +329,18 @@ proc run_style_tests { } { "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \ "Version's TrueColor foreground style" } + + gdb_test_no_output "set host-charset UTF-8" + # Chosen since it will print an error. + gdb_test "maint translate-address" \ + "❌️ requires argument.*" \ + "emoji output" + + gdb_test_no_output "set style error-prefix abcd:" \ + "set the error prefix" + gdb_test "maint translate-address" \ + "abcd:requires argument.*" \ + "error prefix" } } diff --git a/gdb/testsuite/gdb.cp/templates.exp b/gdb/testsuite/gdb.cp/templates.exp index 74e4a92..52d0229 100644 --- a/gdb/testsuite/gdb.cp/templates.exp +++ b/gdb/testsuite/gdb.cp/templates.exp @@ -58,9 +58,9 @@ proc test_ptype_of_templates {} { xfail "ptype T5<int> (obsolescent gcc or gdb)" } -re "type = class T5<int> \{${ws}public:${ws}static int X;${ws}int x;${ws}int val;${ws}void T5\\(int\\);${ws}void T5\\((T5<int> const|const T5<int>) ?&\\);${ws}~T5\\(\\);${ws}static void \\* operator new\\((size_t|unsigned( int| long|))\\);${ws}static void operator delete\\(void ?\\*\\);${ws}int value\\((void|)\\);${ws}\}\r\n$gdb_prompt $" { - # This also triggers gdb/1113... - kfail "gdb/1111" "ptype T5<int>" - # Add here a PASS case when PR gdb/1111 gets fixed. + # This also triggers gdb/8218... + kfail "gdb/8216" "ptype T5<int>" + # Add here a PASS case when PR gdb/8216 gets fixed. # These are really: # http://sourceware.org/bugzilla/show_bug.cgi?id=8216 # http://sourceware.org/bugzilla/show_bug.cgi?id=8218 @@ -98,9 +98,9 @@ proc test_ptype_of_templates {} { xfail "ptype t5i (obsolescent gcc or gdb) -- without size_t" } -re "type = class T5<int> \{${ws}public:${ws}static int X;${ws}int x;${ws}int val;${ws}void T5\\(int\\);${ws}void T5\\((T5<int> const|const T5<int>) ?&\\);${ws}~T5\\(\\);${ws}static void \\* operator new\\((size_t|unsigned( int| long|))\\);${ws}static void operator delete\\(void ?\\*\\);${ws}int value\\((void|)\\);${ws}\}\r\n$gdb_prompt $" { - # This also triggers gdb/1113... - kfail "gdb/1111" "ptype t5i" - # Add here a PASS case when PR gdb/1111 gets fixed. + # This also triggers gdb/8218... + kfail "gdb/8216" "ptype t5i" + # Add here a PASS case when PR gdb/8216 gets fixed. # These are really: # http://sourceware.org/bugzilla/show_bug.cgi?id=8216 # http://sourceware.org/bugzilla/show_bug.cgi?id=8218 @@ -132,7 +132,7 @@ proc test_template_breakpoints {} { "constructor breakpoint" } -re "0. cancel.*\[\r\n\]*.1. all.*\[\r\n\]*.2. T5 at .*\[\r\n\]*.3. T5 at .*\[\r\n\]*> $" { - setup_kfail "gdb/1062" "*-*-*" + setup_kfail "gdb/8167" "*-*-*" gdb_test "0" \ "nonsense intended to insure that this test fails" \ "constructor breakpoint" @@ -151,7 +151,7 @@ proc test_template_breakpoints {} { } -re "the class `T5<int>' does not have destructor defined\r\nHint: try 'T5<int>::~T5<TAB> or 'T5<int>::~T5<ESC-\\?>\r\n\\(Note leading single quote.\\)\r\n$gdb_prompt $" { - kfail "gdb/1112" "destructor breakpoint" + kfail "gdb/8217" "destructor breakpoint" } } @@ -307,7 +307,7 @@ gdb_test_multiple "ptype/r Foo" "ptype Foo" { } -re "type = class Foo<int> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*int foo\\(int, int\\);\r\n\\}\r\n$gdb_prompt $" { # GCC 3.1, DWARF-2 output. - kfail "gdb/57" "ptype Foo" + kfail "gdb/7162" "ptype Foo" } -re "No symbol \"Foo\" in current context.\r\n$gdb_prompt $" { # GCC 2.95.3, stabs+ output. @@ -342,28 +342,25 @@ gdb_test_multiple "ptype/r fchar" "ptype fchar" { # ptype Foo<volatile char *> gdb_test_multiple "ptype/r fvpchar" "ptype fvpchar" { - -re "type = (class |)Foo<volatile char ?\\*> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*.*char.*\\*t;\r\n\r\n\[ \t\]*.*char \\* foo\\(int,.*char.*\\*\\);\r\n\\}\r\n$gdb_prompt $" { - pass "ptype fvpchar" - } - -re "type = (class |)Foo<volatile char ?\\*> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*.*char.*\\*t;\r\n\r\n\[ \t\]*.*char \\* foo\\(int,.*char.*\\*\\);.*\r\n\\}\r\n$gdb_prompt $" { + -re "type = class Foo<char volatile\\*> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*volatile char \\*t;\r\n\r\n\[ \t\]*volatile char \\* foo\\(int, volatile char \\*\\);.*\r\n\\}\r\n$gdb_prompt $" { pass "ptype fvpchar" } -re "type = (class |)Foo<char volatile ?\\*> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*.*char.*\\*t;\r\n\r\n\[ \t\]*.*char \\* foo\\(int,.*char.*\\*\\);\r\n\\}\r\n$gdb_prompt $" { - kfail "gdb/1512" "ptype fvpchar" + kfail "gdb/8617" "ptype fvpchar" } } # print a function from Foo<volatile char *> # This test is sensitive to whitespace matching, so we'll do it twice, -# varying the spacing, because of PR gdb/33. +# varying the spacing, because of PR gdb/7138. gdb_test_multiple "print Foo<volatile char *>::foo" "print Foo<volatile char *>::foo" { -re "\\$\[0-9\]* = \\{.*char \\*\\((class |)Foo<(volatile char|char volatile) ?\\*> \\*(| const), int, .*char \\*\\)\\} $hex <Foo<.*char.*\\*>::foo\\(int, .*char.*\\*\\)>\r\n$gdb_prompt $" { pass "print Foo<volatile char *>::foo" } -re "No symbol \"Foo<volatile char \\*>\" in current context.\r\n$gdb_prompt $" { - # This used to be a kfail gdb/33 and then kfail gdb/931. + # This used to be a kfail gdb/7138 and then kfail gdb/8036. fail "print Foo<volatile char *>::foo" } } @@ -373,7 +370,7 @@ gdb_test_multiple "print Foo<volatile char*>::foo" "print Foo<volatile char*>::f pass "print Foo<volatile char*>::foo" } -re "No symbol \"Foo<volatile char\\*>\" in current context.\r\n$gdb_prompt $" { - # This used to be a kfail gdb/33 and then kfail gdb/931. + # This used to be a kfail gdb/7138 and then kfail gdb/8036. fail "print Foo<volatile char*>::foo" } } @@ -390,7 +387,7 @@ gdb_test_multiple "ptype/r Bar" "ptype Bar" { } -re "ptype Bar\r\ntype = class Bar<int, ?33> {\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*int bar\\(int, int\\);\r\n}\r\n$gdb_prompt $" { # GCC 3.1, DWARF-2 output. - kfail "gdb/57" "ptype Bar" + kfail "gdb/7162" "ptype Bar" } -re "No symbol \"Bar\" in current context.\r\n$gdb_prompt $" { # GCC 2.95.3, stabs+ output. @@ -433,11 +430,11 @@ gdb_test_multiple "ptype/r Baz" "ptype Baz" { } -re "type = class Baz<int, ?'s'> {\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*int baz\\(int, int\\);\r\n}\r\n$gdb_prompt $" { # GCC 3.1, DWARF-2 output. - kfail "gdb/57" "ptype Baz" + kfail "gdb/7162" "ptype Baz" } -re "type = class Baz<int, ?(\\(char\\))?115> {\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*int baz\\(int, int\\);\r\n}\r\n$gdb_prompt $" { - # GCC 3.x, DWARF-2 output, running into gdb/57 and gdb/1512. - kfail "gdb/57" "ptype Baz" + # GCC 3.x, DWARF-2 output, running into gdb/7162 and gdb/8617. + kfail "gdb/7162" "ptype Baz" } -re "No symbol \"Baz\" in current context.\r\n$gdb_prompt $" { # GCC 2.95.3, stabs+ output. @@ -479,11 +476,11 @@ gdb_test_multiple "ptype/r Qux" "ptype Qux" { } -re "type = class Qux<char, ?&string> {\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*char t;\r\n\r\n\[ \t\]*char qux\\(int, char\\);\r\n}\r\n$gdb_prompt $" { # GCC 3.1, DWARF-2 output. - kfail "gdb/57" "ptype Qux" + kfail "gdb/7162" "ptype Qux" } -re "type = class Qux<char, ?&\\(string\\)> {\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*char t;\r\n\r\n\[ \t\]*char qux\\(int, char\\);\r\n}\r\n$gdb_prompt $" { - # GCC 3.x, DWARF-2 output; gdb/57 + gdb/1512. - kfail "gdb/57" "ptype Qux" + # GCC 3.x, DWARF-2 output; gdb/7162 + gdb/8617. + kfail "gdb/7162" "ptype Qux" } -re "No symbol \"Qux\" in current context.\r\n$gdb_prompt $" { # GCC 2.95.3, stabs+ output. @@ -507,7 +504,7 @@ gdb_test_multiple "ptype/r quxint" "ptype quxint" { pass "ptype quxint" } -re "type = class Qux<int, ?& ?\\(string\\)> \\{\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\[ \t\]*int t;\r\n\r\n\[ \t\]*int qux\\(int, int\\);.*\r\n\\}\r\n$gdb_prompt $" { - kfail "gdb/1512" "ptype quxint" + kfail "gdb/8617" "ptype quxint" } } @@ -524,7 +521,7 @@ gdb_test_multiple "ptype/r Spec" "ptype Spec" { } -re "type = class Spec<int, ?char> {\r\n\[ \t\]*public:\r\n\[ \t\]*int x;\r\n\r\n\[ \t\]*int spec\\(char\\);\r\n}\r\n$gdb_prompt $" { # GCC 3.1, DWARF-2 output. - kfail "gdb/57" "ptype Spec" + kfail "gdb/7162" "ptype Spec" } -re "No symbol \"Spec\" in current context.\r\n$gdb_prompt $" { # GCC 2.95.3, stabs+ output. diff --git a/gdb/testsuite/gdb.dwarf2/dw-form-strx-out-of-bounds.exp b/gdb/testsuite/gdb.dwarf2/dw-form-strx-out-of-bounds.exp new file mode 100644 index 0000000..f2123fa --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx-out-of-bounds.exp @@ -0,0 +1,35 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Check that an out-of-bounds DW_FORM_strx attribute triggers a DWARF error. + +# Out of bounds index. +set int_str_idx 1 + +source $srcdir/$subdir/dw-form-strx.exp.tcl + +set re_dwarf_error \ + [string_list_to_regexp \ + "DWARF Error: Offset from DW_FORM_GNU_str_index or DW_FORM_strx" \ + " pointing outside of .debug_str_offsets section in CU at offset"\ + " "]$hex +set re_in_module \ + {in module [^\r\n]+} +set re_in_module [string_to_regexp {[}]$re_in_module[string_to_regexp {]}] +set re_no_symbol [string_to_regexp {No symbol "global_var" in current context.}] +gdb_test "ptype global_var" \ + [multi_line \ + "$re_dwarf_error $re_in_module"\ + $re_no_symbol] diff --git a/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp new file mode 100644 index 0000000..9b62edf --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp @@ -0,0 +1,23 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Check that DW_FORM_strx works. + +# Correct index. +set int_str_idx 0 + +source $srcdir/$subdir/dw-form-strx.exp.tcl + +gdb_test "ptype global_var" "type = int" diff --git a/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp.tcl b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp.tcl new file mode 100644 index 0000000..15cfe27 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp.tcl @@ -0,0 +1,60 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile main.c -dw.S + +set asm_file [standard_output_file $srcfile2] + +# Debug info in the main file. +Dwarf::assemble $asm_file { + declare_labels base_offset + + debug_str_offsets base_offset int + + cu { + version 5 + } { + DW_TAG_compile_unit { + {DW_AT_str_offsets_base $base_offset sec_offset} + } { + declare_labels int4_type + + int4_type: DW_TAG_base_type { + {DW_AT_byte_size 4 DW_FORM_sdata} + {DW_AT_encoding @DW_ATE_signed} + {DW_AT_name $::int_str_idx DW_FORM_strx_id} + } + + DW_TAG_variable { + {DW_AT_name global_var} + {DW_AT_type :$int4_type} + {DW_AT_location { + DW_OP_const1u 12 + DW_OP_stack_value + } SPECIAL_expr} + } + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-error.exp b/gdb/testsuite/gdb.dwarf2/dw2-error.exp index f1c8617..0701ec6 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-error.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-error.exp @@ -37,7 +37,7 @@ set host_binfile [gdb_remote_download host $binfile] # First test that reading symbols fails. gdb_test "file $host_binfile" \ - {Reading symbols.*DWARF Error: wrong version in compilation unit header \(is 153, should be 2, 3, 4 or 5\).*} \ + {Reading symbols.*DWARF Error: wrong version in unit header \(is 153, should be 2, 3, 4 or 5\).*} \ "file $testfile" # We can't use proc readnow, because the PR makes it return 0. diff --git a/gdb/testsuite/gdb.dwarf2/intbits.c b/gdb/testsuite/gdb.dwarf2/intbits.c index 82e6ae8..909d283 100644 --- a/gdb/testsuite/gdb.dwarf2/intbits.c +++ b/gdb/testsuite/gdb.dwarf2/intbits.c @@ -41,6 +41,9 @@ unsigned char be30_1_off[4] = { 0x80, 0, 0, 2 }; here, to catch any situation where gdb tries to use the memory. */ unsigned char u32_0[4] = { 0xff, 0xff, 0xff, 0xff }; +/* An 8 bit slot holding a 3 bit value. */ +unsigned char just_bit_0 = 5; + int main (void) { diff --git a/gdb/testsuite/gdb.dwarf2/intbits.exp b/gdb/testsuite/gdb.dwarf2/intbits.exp index 7b50e15..ff1d69a 100644 --- a/gdb/testsuite/gdb.dwarf2/intbits.exp +++ b/gdb/testsuite/gdb.dwarf2/intbits.exp @@ -36,7 +36,7 @@ Dwarf::assemble ${asm_file} { {DW_AT_language @DW_LANG_C_plus_plus} } { declare_labels i7_type u1_type u17_type u31_type \ - u31_1_type u32_0_type u0_0_type be30_1_type + u31_1_type u32_0_type u0_0_type be30_1_type just_bit_type i7_type: DW_TAG_base_type { {DW_AT_encoding @DW_ATE_signed} @@ -167,6 +167,20 @@ Dwarf::assemble ${asm_file} { {DW_AT_location {DW_OP_addr [gdb_target_symbol "u32_0"]} SPECIAL_expr} } + + just_bit_type: DW_TAG_base_type { + {DW_AT_encoding @DW_ATE_unsigned} + {DW_AT_name "just_bit_type"} + {DW_AT_bit_size 3 DW_FORM_udata} + } + + DW_TAG_variable { + {DW_AT_name "v_just_bit"} + {DW_AT_type :${just_bit_type}} + {DW_AT_external 1 DW_FORM_flag} + {DW_AT_location {DW_OP_addr [gdb_target_symbol "just_bit_0"]} + SPECIAL_expr} + } } } } @@ -197,3 +211,6 @@ gdb_test "x/4xb &v_u32_1_off" ":\t0x0e\t0x00\t0x00\t0x00" gdb_test "print v_be30_1_off" "= 1" gdb_test "print v_be30_1_off = 7" " = 7" gdb_test "x/4xb &v_be30_1_off" ":\t0x00\t0x00\t0x00\t0x0e" + +gdb_test "print/x v_just_bit" " = 0x5" +gdb_test "print/x (just_bit_type) 5" " = 0x5" diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp index bbc9e71..578f712 100644 --- a/gdb/testsuite/gdb.guile/scm-color.exp +++ b/gdb/testsuite/gdb.guile/scm-color.exp @@ -20,7 +20,10 @@ load_lib gdb-guile.exp require allow_guile_tests -clean_restart +# Start GDB with styling support. +with_ansi_styling_terminal { + clean_restart +} gdb_install_guile_utils gdb_install_guile_module @@ -108,3 +111,8 @@ gdb_test [concat "guile " \ "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \ "escape sequences" +# Ensure that turning styling off means no escape sequences. +gdb_test_no_output "set style enabled off" +gdb_test_no_output "guile (display (color-escape-sequence c_red #t))" +gdb_test_no_output "guile (display (color-escape-sequence c_red #f))" +gdb_test_no_output "set style enabled on" diff --git a/gdb/testsuite/gdb.multi/tids.exp b/gdb/testsuite/gdb.multi/tids.exp index b84f908..dab6275 100644 --- a/gdb/testsuite/gdb.multi/tids.exp +++ b/gdb/testsuite/gdb.multi/tids.exp @@ -290,7 +290,7 @@ with_test_prefix "two inferiors" { # Try both the convenience variable and the literal number. foreach thr {"\$thr" "20" "1.20" "\$inf.1" "30.1" } { set expected [string_to_regexp $thr] - gdb_test "info threads $thr" "No threads match '${expected}'." + gdb_test "info threads $thr" "No threads matched\\." # "info threads" works like a filter. If there's any other # valid thread in the list, there's no error. info_threads "$thr 1.1" "1.1" @@ -412,7 +412,7 @@ with_test_prefix "two inferiors" { # Check that we do parse the inferior number and don't confuse it. gdb_test "info threads 3.1" \ - "No threads match '3.1'\." + "No threads matched\\." } if { [allow_python_tests] } { diff --git a/gdb/testsuite/gdb.python/py-color-leak.py b/gdb/testsuite/gdb.python/py-color-leak.py index 50dc315..28afd59 100644 --- a/gdb/testsuite/gdb.python/py-color-leak.py +++ b/gdb/testsuite/gdb.python/py-color-leak.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import sys + +# Avoid generating +# src/gdb/testsuite/gdb.python/__pycache__/gdb_leak_detector.cpython-<n>.pyc. +sys.dont_write_bytecode = True + import gdb_leak_detector diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp index 3563d22..2601cf3 100644 --- a/gdb/testsuite/gdb.python/py-color.exp +++ b/gdb/testsuite/gdb.python/py-color.exp @@ -19,8 +19,10 @@ load_lib gdb-python.exp require allow_python_tests -# Start with a fresh gdb. -clean_restart +# Start with a fresh GDB, but enable color support. +with_ansi_styling_terminal { + clean_restart +} gdb_test_no_output "python get_color_attrs = lambda c: \"%s %s %s %s %s\" % (str(c), c.colorspace, c.is_none, c.is_indexed, c.is_direct)" \ "get_color_attrs helper" @@ -115,6 +117,12 @@ gdb_test [concat "python print (c_red.escape_sequence (is_foreground = True) + " "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \ "escape sequences using keyword arguments" +# Ensure that turning styling off means no escape sequences. +gdb_test_no_output "set style enabled off" +gdb_test_no_output "python print (c_red.escape_sequence (True), end='')" +gdb_test_no_output "python print (c_red.escape_sequence (False), end='')" +gdb_test_no_output "set style enabled on" + gdb_test_multiline "Try to sub-class gdb.Color" \ "python" "" \ "class my_color(gdb.Color):" "" \ diff --git a/gdb/testsuite/gdb.python/py-inferior-leak.py b/gdb/testsuite/gdb.python/py-inferior-leak.py index f764688..bf61668 100644 --- a/gdb/testsuite/gdb.python/py-inferior-leak.py +++ b/gdb/testsuite/gdb.python/py-inferior-leak.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import sys + +# Avoid generating +# src/gdb/testsuite/gdb.python/__pycache__/gdb_leak_detector.cpython-<n>.pyc. +sys.dont_write_bytecode = True + import re import gdb_leak_detector diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.exp b/gdb/testsuite/gdb.python/py-missing-objfile.exp index e4a1b3f..29bc555 100644 --- a/gdb/testsuite/gdb.python/py-missing-objfile.exp +++ b/gdb/testsuite/gdb.python/py-missing-objfile.exp @@ -79,6 +79,16 @@ proc setup_debugdir { dirname files } { # executable (when EXEC_LOADED is true) and/or the library (when LIB_LOADED # is true). proc check_loaded_debug { exec_loaded lib_loaded } { + set re_warn \ + [string_to_regexp \ + "Warning: the current language does not match this frame."] + set cmd "set lang c" + gdb_test_multiple $cmd "" { + -re -wrap "${cmd}(\r\n$re_warn)?" { + pass $gdb_test_name + } + } + if { $exec_loaded } { gdb_test "whatis global_exec_var" "^type = volatile struct exec_type" diff --git a/gdb/testsuite/gdb.python/py-objfile.c b/gdb/testsuite/gdb.python/py-objfile.c index fe68671..d721e0c 100644 --- a/gdb/testsuite/gdb.python/py-objfile.c +++ b/gdb/testsuite/gdb.python/py-objfile.c @@ -19,7 +19,7 @@ int global_var = 42; static int __attribute__ ((used)) static_var = 50; int -main () +main (void) { int some_var = 0; return 0; diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp index d14eec6..8d11028 100644 --- a/gdb/testsuite/gdb.python/py-objfile.exp +++ b/gdb/testsuite/gdb.python/py-objfile.exp @@ -144,7 +144,8 @@ gdb_test "python print (sep_objfile.owner.filename)" "${testfile}2" \ gdb_test "python print (sep_objfile.owner.username)" "${testfile}2" \ "Test user-name of owner of separate debug file" -gdb_test "p main" "= {int \\(\\)} $hex <main>" \ +set re_prototype [string_to_regexp "int (void)"] +gdb_test "p main" "= {$re_prototype} $hex <main>" \ "print main with debug info" # Separate debug files are not findable. diff --git a/gdb/testsuite/gdb.python/py-read-memory-leak.py b/gdb/testsuite/gdb.python/py-read-memory-leak.py index 71edf47..89647cf 100644 --- a/gdb/testsuite/gdb.python/py-read-memory-leak.py +++ b/gdb/testsuite/gdb.python/py-read-memory-leak.py @@ -13,6 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import sys + +# Avoid generating +# src/gdb/testsuite/gdb.python/__pycache__/gdb_leak_detector.cpython-<n>.pyc. +sys.dont_write_bytecode = True + import gdb_leak_detector diff --git a/gdb/testsuite/gdb.reverse/time-reverse.exp b/gdb/testsuite/gdb.reverse/time-reverse.exp index fe191a0..58dcdde 100644 --- a/gdb/testsuite/gdb.reverse/time-reverse.exp +++ b/gdb/testsuite/gdb.reverse/time-reverse.exp @@ -20,6 +20,7 @@ # require supports_reverse +require supports_process_record standard_testfile @@ -38,23 +39,49 @@ proc test {mode} { return } - runto marker1 - - if [supports_process_record] { - # Activate process record/replay - gdb_test_no_output "record" "turn on process record" + if { ![runto marker1] } { + return } + # Activate process record/replay + gdb_test_no_output "record" "turn on process record" + gdb_test_no_output "set record full stop-at-limit on" + gdb_test_no_output "set record full insn-number-max 2000" + + set re_srcfile [string_to_regexp $::srcfile] + gdb_test "break marker2" \ - "Breakpoint $::decimal at $::hex: file .*$::srcfile, line $::decimal.*" \ + "Breakpoint $::decimal at $::hex: file .*$re_srcfile, line $::decimal.*" \ "set breakpoint at marker2" - gdb_continue_to_breakpoint "marker2" ".*$::srcfile:.*" + set re_question \ + [string_list_to_regexp \ + "Do you want to auto delete previous execution log entries when" \ + " record/replay buffer becomes full" \ + { (record full stop-at-limit)?([y] or n)}] + set re_program_stopped \ + [multi_line \ + [string_to_regexp "Process record: stopped by user."] \ + "" \ + [string_to_regexp "Program stopped."]] + set re_marker2 [string_to_regexp "marker2 ()"] + gdb_test_multiple "continue" "continue to breakpoint: marker2" { + -re "$re_question " { + send_gdb "n\n" + exp_continue + } + -re -wrap "Breakpoint $::decimal, $re_marker2 .*" { + pass $gdb_test_name + } + -re -wrap "\r\n$re_program_stopped\r\n.*" { + unsupported $gdb_test_name + } + } # Show how many instructions we've recorded. gdb_test "info record" "Active record target: .*" - gdb_test "reverse-continue" ".*$::srcfile:$::decimal.*" "reverse to marker1" + gdb_test "reverse-continue" ".*$re_srcfile:$::decimal.*" "reverse to marker1" # If the variable was recorded properly, the old contents (-1) # will be remembered. If not, new contents (current time) will be diff --git a/gdb/testsuite/gdb.rocm/mi-attach.exp b/gdb/testsuite/gdb.rocm/mi-attach.exp index 2ca610c..37ce92a 100644 --- a/gdb/testsuite/gdb.rocm/mi-attach.exp +++ b/gdb/testsuite/gdb.rocm/mi-attach.exp @@ -13,10 +13,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +load_lib rocm.exp load_lib mi-support.exp set MIFLAGS "-i=mi" -require can_spawn_for_attach +require can_spawn_for_attach allow_hipcc_tests standard_testfile .cpp diff --git a/gdb/testsuite/gdb.threads/current-lwp-dead.exp b/gdb/testsuite/gdb.threads/current-lwp-dead.exp index 7aa7ab9..c8364df 100644 --- a/gdb/testsuite/gdb.threads/current-lwp-dead.exp +++ b/gdb/testsuite/gdb.threads/current-lwp-dead.exp @@ -47,6 +47,6 @@ gdb_breakpoint $line gdb_continue_to_breakpoint "fn_return" ".*at-fn_return.*" # Confirm thread 2 is really gone. -gdb_test "info threads 2" "No threads match '2'\\." +gdb_test "info threads 2" "No threads matched\\." gdb_continue_to_end "" continue 1 diff --git a/gdb/testsuite/gdb.threads/inf-thr-count.exp b/gdb/testsuite/gdb.threads/inf-thr-count.exp index d7a7687..61533ab 100644 --- a/gdb/testsuite/gdb.threads/inf-thr-count.exp +++ b/gdb/testsuite/gdb.threads/inf-thr-count.exp @@ -43,7 +43,7 @@ if {[build_executable "failed to prepare" $testfile $srcfile \ # Start GDB. Ensure we are in non-stop mode as we need to read from # the inferior while it is running. save_vars {GDBFLAGS} { - append GDBFLAGS " -ex \"set non-stop on\"" + append GDBFLAGS { -ex "set non-stop on"} clean_restart $binfile } @@ -54,22 +54,20 @@ if ![runto_main] { gdb_breakpoint breakpt gdb_continue_to_breakpoint "first breakpt call" +set re_var [string_to_regexp "$"]$decimal + # Check we can see a single thread to begin with. -gdb_test "p \$_inferior_thread_count" \ - "^\\\$$::decimal = 1" \ - "only one thread in \$_inferior_thread_count" +gdb_test {p $_inferior_thread_count} \ + "^$re_var = 1" \ + {only one thread in $_inferior_thread_count} # We don't want thread events, it makes it harder to match GDB's # output. gdb_test_no_output "set print thread-events off" # Continue the program in the background. -set test "continue&" -gdb_test_multiple "continue&" $test { - -re "Continuing\\.\r\n$gdb_prompt " { - pass $test - } -} +gdb_test -no-prompt-anchor "continue&" \ + [string_to_regexp "Continuing."] # Read the 'stage' flag from the inferior. This is initially 0, but # will be set to 1 once the extra thread has been created, and then 2 @@ -88,8 +86,17 @@ proc wait_for_stage { num } { set failure_count 0 set cmd "print /d stage" set stage_flag 0 + + set re_int -?$::decimal + + set re_msg \ + [multi_line \ + "Cannot execute this command while the target is running" \ + {Use the "interrupt" command to stop the target} \ + [string_to_regexp "and then try again."]] + gdb_test_multiple "$cmd" "wait for 'stage' flag to be $num" { - -re -wrap "^Cannot execute this command while the target is running\\.\r\nUse the \"interrupt\" command to stop the target\r\nand then try again\\." { + -re -wrap ^$re_msg { fail "$gdb_test_name (can't read asynchronously)" gdb_test_no_output "interrupt" @@ -101,7 +108,7 @@ proc wait_for_stage { num } { } } - -re -wrap "^\\$\[0-9\]* = (\[-\]*\[0-9\]*).*" { + -re -wrap "^$::re_var = ($re_int).*" { set stage_flag $expect_out(1,string) if {$stage_flag != $num} { set stage_flag 0 @@ -131,8 +138,8 @@ if {![wait_for_stage 1]} { if {[target_info exists gdb_protocol] && ([target_info gdb_protocol] == "remote" || [target_info gdb_protocol] == "extended-remote")} { - set new_thread_re "\\\[New Thread \[^\r\n\]+\\\]\r\n" - set exit_thread_re "\\\[Thread \[^\r\n\]+ exited\\\]\r\n" + set new_thread_re {\[New Thread [^\r\n]+\]\r\n} + set exit_thread_re {\[Thread [^\r\n]+ exited\]\r\n} } else { set new_thread_re "" set exit_thread_re "" @@ -141,9 +148,9 @@ if {[target_info exists gdb_protocol] # This is the test we actually care about. Check that the # $_inferior_thread_count convenience variable shows the correct # thread count; the new thread should be visible. -gdb_test "with print thread-events on -- p \$_inferior_thread_count" \ - "^${new_thread_re}\\\$$::decimal = 2" \ - "second thread visible in \$_inferior_thread_count" +gdb_test {with print thread-events on -- p $_inferior_thread_count} \ + "^${new_thread_re}$re_var = 2" \ + {second thread visible in $_inferior_thread_count} # Set a variable in the inferior, this will cause the second thread to # exit. @@ -157,19 +164,25 @@ if {![wait_for_stage 2]} { } # Check that the second thread has gone away. -gdb_test "with print thread-events on -- p \$_inferior_thread_count" \ - "^${exit_thread_re}\\\$$::decimal = 1" \ - "back to one thread visible in \$_inferior_thread_count" +gdb_test {with print thread-events on -- p $_inferior_thread_count} \ + "^${exit_thread_re}$re_var = 1" \ + {back to one thread visible in $_inferior_thread_count} # Set a variable in the inferior, this will cause the second thread to # exit. -gdb_test_no_output "set variable spin = 0" \ +gdb_test_no_output -no-prompt-anchor "set variable spin = 0" \ "set 'spin' flag to allow main thread to exit" # When the second thread exits, the main thread joins with it, and # then proceeds to hit the breakpt function again. +set re_breakpt [string_to_regexp "breakpt ()"] +set re \ + [multi_line \ + "Thread 1 \[^\r\n\]+ hit Breakpoint $decimal, $re_breakpt\[^\r\n\]+" \ + "\[^\r\n\]+" \ + ""] gdb_test_multiple "" "wait for main thread to stop" { - -re "Thread 1 \[^\r\n\]+ hit Breakpoint $decimal, breakpt \\(\\)\[^\r\n\]+\r\n\[^\r\n\]+\r\n" { + -re $re { pass $gdb_test_name } } diff --git a/gdb/testsuite/gdb.threads/info-threads-options.c b/gdb/testsuite/gdb.threads/info-threads-options.c new file mode 100644 index 0000000..2c4cd85 --- /dev/null +++ b/gdb/testsuite/gdb.threads/info-threads-options.c @@ -0,0 +1,77 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022-2025 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <pthread.h> + +#define NUM 4 + +static pthread_barrier_t threads_started_barrier; + +static void +stop_here () +{ +} + +static void +spin () +{ + while (1) + usleep (1); +} + +static void * +work (void *arg) +{ + int id = *(int *) arg; + + pthread_barrier_wait (&threads_started_barrier); + + if (id % 2 == 0) + stop_here (); + else + spin (); + + pthread_exit (NULL); +} + +int +main () +{ + /* Ensure we stop if GDB crashes and DejaGNU fails to kill us. */ + alarm (10); + + pthread_t threads[NUM]; + int ids[NUM]; + + pthread_barrier_init (&threads_started_barrier, NULL, NUM + 1); + + for (int i = 0; i < NUM; i++) + { + ids[i] = i; + pthread_create (&threads[i], NULL, work, &ids[i]); + } + + /* Wait until all threads are seen running. */ + pthread_barrier_wait (&threads_started_barrier); + + stop_here (); + + return 0; +} diff --git a/gdb/testsuite/gdb.threads/info-threads-options.exp b/gdb/testsuite/gdb.threads/info-threads-options.exp new file mode 100644 index 0000000..38e4e67 --- /dev/null +++ b/gdb/testsuite/gdb.threads/info-threads-options.exp @@ -0,0 +1,131 @@ +# Copyright (C) 2022-2025 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Test the filter flags of the "info threads" command. + +standard_testfile + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable debug] != "" } { + return -1 +} + +save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set non-stop on\"" + clean_restart $binfile +} + +if ![runto_main] { + return -1 +} + +gdb_breakpoint "stop_here" +gdb_test_multiple "continue -a&" "" { + -re "Continuing.\r\n$gdb_prompt " { + pass $gdb_test_name + } +} + +set expected_hits 3 +set fill "\[^\r\n\]+" +set num_hits 0 +gdb_test_multiple "" "hit the breakpoint" -lbl { + -re "\r\nThread ${fill} hit Breakpoint ${decimal}," { + incr num_hits + if {$num_hits < $expected_hits} { + exp_continue + } + } +} +gdb_assert {$num_hits == $expected_hits} "expected threads hit the bp" + +# Count the number of running/stopped threads reported +# by the "info threads" command. We also capture thread ids +# for additional tests. +set running_tid "invalid" +set stopped_tid "invalid" + +set eol "(?=\r\n)" + +foreach_with_prefix flag {"" "-running" "-stopped" "-running -stopped"} { + set num_running 0 + set num_stopped 0 + gdb_test_multiple "info threads $flag" "info threads $flag" -lbl { + -re "Id${fill}Target Id${fill}Frame${fill}${eol}" { + exp_continue + } + -re "^\r\n. (${decimal})${fill}Thread ${fill}.running.${eol}" { + incr num_running + set running_tid $expect_out(1,string) + exp_continue + } + -re "^\r\n. (${decimal})${fill}Thread ${fill}stop_here ${fill}${eol}" { + incr num_stopped + set stopped_tid $expect_out(1,string) + exp_continue + } + -re "^\r\n$gdb_prompt $" { + pass $gdb_test_name + } + } + + if {$flag eq "-running"} { + gdb_assert {$num_running == 2} "num running" + gdb_assert {$num_stopped == 0} "num stopped" + } elseif {$flag eq "-stopped"} { + gdb_assert {$num_running == 0} "num running" + gdb_assert {$num_stopped == 3} "num stopped" + } else { + gdb_assert {$num_running == 2} "num running" + gdb_assert {$num_stopped == 3} "num stopped" + } +} + +verbose -log "running_tid=$running_tid, stopped_tid=$stopped_tid" + +# Test specifying thread ids. +gdb_test "info threads -running $stopped_tid" \ + "No threads matched\\." \ + "info thread -running for a stopped thread" +gdb_test "info threads -stopped $running_tid" \ + "No threads matched\\." \ + "info thread -stopped for a running thread" + +set ws "\[ \t\]+" +foreach tid "\"$running_tid\" \"$running_tid $stopped_tid\"" { + gdb_test "info threads -running $tid" \ + [multi_line \ + "${ws}Id${ws}Target Id${ws}Frame${ws}" \ + "${ws}${running_tid}${ws}Thread ${fill}.running."] \ + "info thread -running with [llength $tid] thread ids" +} + +foreach tid "\"$stopped_tid\" \"$stopped_tid $running_tid\"" { + gdb_test "info threads -stopped $tid" \ + [multi_line \ + "${ws}Id${ws}Target Id${ws}Frame${ws}" \ + "${ws}${stopped_tid}${ws}Thread ${fill} stop_here ${fill}"] \ + "info thread -stopped with [llength $tid] thread ids" +} + +gdb_test_multiple "info threads -stopped -running $stopped_tid $running_tid" \ + "filter flags and tids combined" { + -re -wrap ".*stop_here.*running.*" { + pass $gdb_test_name + } + -re -wrap ".*running.*stop_here.*" { + pass $gdb_test_name + } +} diff --git a/gdb/testsuite/gdb.threads/thread-bp-deleted.exp b/gdb/testsuite/gdb.threads/thread-bp-deleted.exp index 2eadd38..8cabb70 100644 --- a/gdb/testsuite/gdb.threads/thread-bp-deleted.exp +++ b/gdb/testsuite/gdb.threads/thread-bp-deleted.exp @@ -147,7 +147,7 @@ if {$is_remote} { exp_continue } - -re "No threads match '99'\\.\r\n$gdb_prompt $" { + -re "No threads matched\\.\r\n$gdb_prompt $" { if {!$saw_thread_exited && !$saw_bp_deleted && $attempt_count > 0} { sleep 1 incr attempt_count -1 diff --git a/gdb/testsuite/gdb.tui/corefile-run.exp b/gdb/testsuite/gdb.tui/corefile-run.exp index 89a48b5..657fc83 100644 --- a/gdb/testsuite/gdb.tui/corefile-run.exp +++ b/gdb/testsuite/gdb.tui/corefile-run.exp @@ -18,6 +18,8 @@ # # Ref.: https://bugzilla.redhat.com/show_bug.cgi?id=1765117 +require gcore_cmd_available + tuiterm_env standard_testfile tui-layout.c diff --git a/gdb/testsuite/gdb.tui/tui-layout-asm.exp b/gdb/testsuite/gdb.tui/tui-layout-asm.exp index ec78a0c..333276e 100644 --- a/gdb/testsuite/gdb.tui/tui-layout-asm.exp +++ b/gdb/testsuite/gdb.tui/tui-layout-asm.exp @@ -24,7 +24,9 @@ if {[build_executable "failed to prepare" ${testfile} ${srcfile}] == -1} { return -1 } -# PPC currently needs a minimum window width of 90 to work correctly. +# The wider the window is, the less line truncation happens, so matching +# pre-scroll to post-scroll lines is more accurate. But 100% accurate line +# matching isn't a goal of the test-case. set tui_asm_window_width 90 Term::clean_restart 24 ${tui_asm_window_width} $testfile @@ -33,15 +35,41 @@ if {![Term::prepare_for_tui]} { return } -# Helper proc, returns a count of the ' ' characters in STRING. -proc count_whitespace { string } { - return [expr {[llength [split $string { }]] - 1}] -} - # This puts us into TUI mode, and should display the ASM window. Term::command_no_prompt_prefix "layout asm" Term::check_box_contents "check asm box contents" 0 0 ${tui_asm_window_width} 15 "<main>" +set re_border [string_to_regexp "|"] + +proc drop_borders { line } { + # Drop left border. + set line [regsub -- ^$::re_border $line {}] + # Drop right border. + set line [regsub -- $::re_border$ $line {}] + + return $line +} + +proc lines_match { line1 line2 } { + set line1 [drop_borders $line1] + set line2 [drop_borders $line2] + + foreach line [list $line1 $line2] re [list $line2 $line1] { + # Convert to regexp. + set re [string_to_regexp $re] + + # Ignore whitespace mismatches. + regsub -all {\s+} $re {\s+} re + + # Allow a substring match. + if { [regexp -- $re $line] } { + return 1 + } + } + + return 0 +} + # Scroll the ASM window down using the down arrow key. In an ideal # world we'd like to use PageDown here, but currently our terminal # library doesn't support such advanced things. @@ -58,51 +86,55 @@ while (1) { # below will just timeout. So for now we avoid testing the edge # case. if {[regexp -- "^\\| +\\|$" $line]} { - # Second line is blank, we're at the end of the assembler. - pass $testname + # Second line is blank, we're at the end of the assembly. + pass "$testname (end of assembly reached)" break } # Send the down key to GDB. send_gdb "\033\[B" incr down_count - set re_line [string_to_regexp $line] - # Ignore whitespace mismatches. - regsub -all {\s+} $re_line {\s+} re_line + + # Get address from the line. + regexp \ + [join \ + [list \ + ^ \ + $re_border \ + {\s+} \ + ($hex) \ + {\s+}] \ + ""] \ + $line \ + match \ + address + + # Regexp to match line containing address. + set re_line \ + [join \ + [list \ + ^ \ + $re_border \ + {\s+} \ + $address \ + {\s+} \ + {[^\r\n]+} \ + $re_border \ + $] \ + ""] + if {[Term::wait_for $re_line] \ - && [regexp $re_line [Term::get_line 1]]} { + && [lines_match $line [Term::get_line 1]]} { # We scrolled successfully. } else { - if {[count_whitespace ${line}] != \ - [count_whitespace [Term::get_line 1]]} { - # GDB's TUI assembler display will widen columns based on - # the longest item that appears in a column on any line. - # As we have just scrolled, and so revealed a new line, it - # is possible that the width of some columns has changed. - # - # As a result it is possible that part of the line we were - # expected to see in the output is now off the screen. And - # this test will fail. - # - # This is unfortunate, but, right now, there's no easy way - # to "lock" the format of the TUI assembler window. The - # only option appears to be making the window width wider, - # this can be done by adjusting TUI_ASM_WINDOW_WIDTH. - verbose -log "WARNING: The following failure is probably due to the TUI window" - verbose -log " width. See the comments in the test script for more" - verbose -log " details." - } - fail "$testname (scroll failed)" Term::dump_screen break } - if { $down_count > 250 } { - # Maybe we should accept this as a pass in case a target - # really does have loads of assembler to scroll through. - fail "$testname (too much assembler)" - Term::dump_screen + if { $down_count > 25 } { + # We've scrolled enough, we're done. + pass "$testname (scroll limit reached)" break } } diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index 7e8778a..b347437 100644 --- a/gdb/testsuite/lib/dwarf.exp +++ b/gdb/testsuite/lib/dwarf.exp @@ -678,6 +678,11 @@ namespace eval Dwarf { } } close $fd + + variable _constants + + # Add DW_FORM_strx_id as alias of DW_FORM_strx. + _process_one_constant DW_FORM_strx_id $_constants(DW_FORM_strx) } proc _quote {string} { @@ -823,6 +828,12 @@ namespace eval Dwarf { DW_FORM_indirect - DW_FORM_exprloc - + # Generate a DW_FORM_str index, but assume generation of .debug_str and + # .debug_str_offsets is taken care of elsewhere. + DW_FORM_strx_id { + _op .uleb128 $value + } + DW_FORM_strx - DW_FORM_strx1 - DW_FORM_strx2 - @@ -3385,6 +3396,37 @@ namespace eval Dwarf { debug_names_end: } + # Add the strings in ARGS to the .debug_str section, and create a + # .debug_str_offsets section pointing to those strings. + # BASE_OFFSET is the label for DW_AT_str_offsets_base. + proc debug_str_offsets { base_offset args } { + _section .debug_str + set num 0 + foreach arg $args { + set str_label [_compute_label "str_${num}"] + define_label $str_label + _op .asciz \"$arg\" ".debug_str_offsets string $num" + incr num + } + + declare_labels debug_str_offsets_start debug_str_offsets_end + set initial_length "$debug_str_offsets_end - $debug_str_offsets_start" + + _section .debug_str_offsets + _op .4byte $initial_length "Initial_length" + debug_str_offsets_start: + _op .2byte 0x5 "version" + _op .2byte 0x0 "padding" + $base_offset: + set num 0 + foreach arg $args { + set str_label [_compute_label "str_${num}"] + _op .4byte $str_label "string $num" + incr num + } + debug_str_offsets_end: + } + # The top-level interface to the DWARF assembler. # OPTIONS is a list with an even number of elements containing # option-name and option-value pairs. diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index ead14bd..2a5d37c 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -7007,6 +7007,24 @@ proc gdb_load_cmd { args } { return -1 } +# Return non-zero if 'gcore' command is available. +gdb_caching_proc gcore_cmd_available { } { + gdb_exit + gdb_start + + # Does this gdb support gcore? + gdb_test_multiple "help gcore" "" { + -re -wrap "Undefined command: .*" { + return 0 + } + -re -wrap "Save a core file .*" { + return 1 + } + } + + return 0 +} + # Invoke "gcore". CORE is the name of the core file to write. TEST # is the name of the test case. This will return 1 if the core file # was created, 0 otherwise. If this fails to make a core file because diff --git a/gdb/thread.c b/gdb/thread.c index b659463..0228027 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -1038,6 +1038,37 @@ pc_in_thread_step_range (CORE_ADDR pc, struct thread_info *thread) && pc < thread->control.step_range_end); } +/* The options for the "info threads" command. */ + +struct info_threads_opts +{ + /* For "-gid". */ + bool show_global_ids = false; + /* For "-running". */ + bool show_running_threads = false; + /* For "-stopped". */ + bool show_stopped_threads = false; +}; + +static const gdb::option::option_def info_threads_option_defs[] = { + + gdb::option::flag_option_def<info_threads_opts> { + "gid", + [] (info_threads_opts *opts) { return &opts->show_global_ids; }, + N_("Show global thread IDs."), + }, + gdb::option::flag_option_def<info_threads_opts> { + "running", + [] (info_threads_opts *opts) { return &opts->show_running_threads; }, + N_("Show running threads only."), + }, + gdb::option::flag_option_def<info_threads_opts> { + "stopped", + [] (info_threads_opts *opts) { return &opts->show_stopped_threads; }, + N_("Show stopped threads only."), + }, +}; + /* Helper for print_thread_info. Returns true if THR should be printed. If REQUESTED_THREADS, a list of GDB ids/ranges, is not NULL, only print THR if its ID is included in the list. GLOBAL_IDS @@ -1046,11 +1077,13 @@ pc_in_thread_step_range (CORE_ADDR pc, struct thread_info *thread) is a thread from the process PID. Otherwise, threads from all attached PIDs are printed. If both REQUESTED_THREADS is not NULL and PID is not -1, then the thread is printed if it belongs to the - specified process. Otherwise, an error is raised. */ + specified process. Otherwise, an error is raised. OPTS is the + options of the "info threads" command. */ static bool -should_print_thread (const char *requested_threads, int default_inf_num, - int global_ids, int pid, struct thread_info *thr) +should_print_thread (const char *requested_threads, + const info_threads_opts &opts, int default_inf_num, + int global_ids, int pid, thread_info *thr) { if (requested_threads != NULL && *requested_threads != '\0') { @@ -1075,7 +1108,17 @@ should_print_thread (const char *requested_threads, int default_inf_num, if (thr->state == THREAD_EXITED) return false; - return true; + bool is_stopped = (thr->state == THREAD_STOPPED); + if (opts.show_stopped_threads && is_stopped) + return true; + + bool is_running = (thr->state == THREAD_RUNNING); + if (opts.show_running_threads && is_running) + return true; + + /* If the user did not pass a filter flag, show the thread. */ + return (!opts.show_stopped_threads + && !opts.show_running_threads); } /* Return the string to display in "info threads"'s "Target Id" @@ -1104,8 +1147,8 @@ thread_target_id_str (thread_info *tp) static void do_print_thread (ui_out *uiout, const char *requested_threads, - int global_ids, int pid, int show_global_ids, - int default_inf_num, thread_info *tp, + const info_threads_opts &opts, int global_ids, + int pid, int default_inf_num, thread_info *tp, thread_info *current_thread) { int core; @@ -1114,7 +1157,7 @@ do_print_thread (ui_out *uiout, const char *requested_threads, if (current_thread != nullptr) switch_to_thread (current_thread); - if (!should_print_thread (requested_threads, default_inf_num, + if (!should_print_thread (requested_threads, opts, default_inf_num, global_ids, pid, tp)) return; @@ -1130,7 +1173,7 @@ do_print_thread (ui_out *uiout, const char *requested_threads, uiout->field_string ("id-in-tg", print_thread_id (tp)); } - if (show_global_ids || uiout->is_mi_like_p ()) + if (opts.show_global_ids || uiout->is_mi_like_p ()) uiout->field_signed ("id", tp->global_num); /* Switch to the thread (and inferior / target). */ @@ -1191,23 +1234,22 @@ do_print_thread (ui_out *uiout, const char *requested_threads, static void print_thread (ui_out *uiout, const char *requested_threads, - int global_ids, int pid, int show_global_ids, + const info_threads_opts &opts, int global_ids, int pid, int default_inf_num, thread_info *tp, thread_info *current_thread) { do_with_buffered_output (do_print_thread, uiout, requested_threads, - global_ids, pid, show_global_ids, - default_inf_num, tp, current_thread); + opts, global_ids, pid, default_inf_num, tp, + current_thread); } /* Like print_thread_info, but in addition, GLOBAL_IDS indicates whether REQUESTED_THREADS is a list of global or per-inferior - thread ids. */ + thread ids. OPTS is the options of the "info threads" command. */ static void print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, - int global_ids, int pid, - int show_global_ids) + const info_threads_opts &opts, int global_ids, int pid) { int default_inf_num = current_inferior ()->num; @@ -1235,19 +1277,21 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, list_emitter.emplace (uiout, "threads"); else { - int n_threads = 0; + int n_matching_threads = 0; /* The width of the "Target Id" column. Grown below to accommodate the largest entry. */ size_t target_id_col_width = 17; for (thread_info *tp : all_threads ()) { + any_thread = true; + /* In case REQUESTED_THREADS contains $_thread. */ if (current_thread != nullptr) switch_to_thread (current_thread); - if (!should_print_thread (requested_threads, default_inf_num, - global_ids, pid, tp)) + if (!should_print_thread (requested_threads, opts, + default_inf_num, global_ids, pid, tp)) continue; /* Switch inferiors so we're looking at the right @@ -1258,25 +1302,24 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, = std::max (target_id_col_width, thread_target_id_str (tp).size ()); - ++n_threads; + ++n_matching_threads; } - if (n_threads == 0) + if (n_matching_threads == 0) { - if (requested_threads == NULL || *requested_threads == '\0') + if (!any_thread) uiout->message (_("No threads.\n")); else - uiout->message (_("No threads match '%s'.\n"), - requested_threads); + uiout->message (_("No threads matched.\n")); return; } - table_emitter.emplace (uiout, show_global_ids ? 5 : 4, - n_threads, "threads"); + table_emitter.emplace (uiout, opts.show_global_ids ? 5 : 4, + n_matching_threads, "threads"); uiout->table_header (1, ui_left, "current", ""); uiout->table_header (4, ui_left, "id-in-tg", "Id"); - if (show_global_ids) + if (opts.show_global_ids) uiout->table_header (4, ui_left, "id", "GId"); uiout->table_header (target_id_col_width, ui_left, "target-id", "Target Id"); @@ -1287,13 +1330,11 @@ print_thread_info_1 (struct ui_out *uiout, const char *requested_threads, for (inferior *inf : all_inferiors ()) for (thread_info *tp : inf->threads ()) { - any_thread = true; - if (tp == current_thread && tp->state == THREAD_EXITED) current_exited = true; - print_thread (uiout, requested_threads, global_ids, pid, - show_global_ids, default_inf_num, tp, current_thread); + print_thread (uiout, requested_threads, opts, global_ids, pid, + default_inf_num, tp, current_thread); } /* This end scope restores the current thread and the frame @@ -1322,27 +1363,10 @@ void print_thread_info (struct ui_out *uiout, const char *requested_threads, int pid) { - print_thread_info_1 (uiout, requested_threads, 1, pid, 0); + info_threads_opts opts; + print_thread_info_1 (uiout, requested_threads, opts, 1, pid); } -/* The options for the "info threads" command. */ - -struct info_threads_opts -{ - /* For "-gid". */ - bool show_global_ids = false; -}; - -static const gdb::option::option_def info_threads_option_defs[] = { - - gdb::option::flag_option_def<info_threads_opts> { - "gid", - [] (info_threads_opts *opts) { return &opts->show_global_ids; }, - N_("Show global thread IDs."), - }, - -}; - /* Create an option_def_group for the "info threads" options, with IT_OPTS as context. */ @@ -1367,7 +1391,7 @@ info_threads_command (const char *arg, int from_tty) gdb::option::process_options (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp); - print_thread_info_1 (current_uiout, arg, 0, -1, it_opts.show_global_ids); + print_thread_info_1 (current_uiout, arg, it_opts, 0, -1); } /* Completer for the "info threads" command. */ diff --git a/gdb/tracefile-tfile.c b/gdb/tracefile-tfile.c index c2b5e3b..5f8b338 100644 --- a/gdb/tracefile-tfile.c +++ b/gdb/tracefile-tfile.c @@ -194,12 +194,12 @@ tfile_write_status (struct trace_file_writer *self, if (ts->start_time) { fprintf (writer->fp, ";starttime:%s", - phex_nz (ts->start_time, sizeof (ts->start_time))); + phex_nz (ts->start_time)); } if (ts->stop_time) { fprintf (writer->fp, ";stoptime:%s", - phex_nz (ts->stop_time, sizeof (ts->stop_time))); + phex_nz (ts->stop_time)); } if (ts->notes != NULL) { @@ -254,7 +254,7 @@ tfile_write_uploaded_tp (struct trace_file_writer *self, char buf[MAX_TRACE_UPLOAD]; fprintf (writer->fp, "tp T%x:%s:%c:%x:%x", - utp->number, phex_nz (utp->addr, sizeof (utp->addr)), + utp->number, phex_nz (utp->addr), (utp->enabled ? 'E' : 'D'), utp->step, utp->pass); if (utp->type == bp_fast_tracepoint) fprintf (writer->fp, ":F%x", utp->orig_size); @@ -265,10 +265,10 @@ tfile_write_uploaded_tp (struct trace_file_writer *self, fprintf (writer->fp, "\n"); for (const auto &act : utp->actions) fprintf (writer->fp, "tp A%x:%s:%s\n", - utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act.get ()); + utp->number, phex_nz (utp->addr), act.get ()); for (const auto &act : utp->step_actions) fprintf (writer->fp, "tp S%x:%s:%s\n", - utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act.get ()); + utp->number, phex_nz (utp->addr), act.get ()); if (utp->at_string) { encode_source_string (utp->number, utp->addr, @@ -290,7 +290,7 @@ tfile_write_uploaded_tp (struct trace_file_writer *self, fprintf (writer->fp, "tp Z%s\n", buf); } fprintf (writer->fp, "tp V%x:%s:%x:%s\n", - utp->number, phex_nz (utp->addr, sizeof (utp->addr)), + utp->number, phex_nz (utp->addr), utp->hit_count, phex_nz (utp->traceframe_usage, sizeof (utp->traceframe_usage))); diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index a2f1a75..b431468 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -2818,7 +2818,7 @@ encode_source_string (int tpnum, ULONGEST addr, if (80 + strlen (srctype) > buf_size) error (_("Buffer too small for source encoding")); sprintf (buf, "%x:%s:%s:%x:%x:", - tpnum, phex_nz (addr, sizeof (addr)), + tpnum, phex_nz (addr), srctype, 0, (int) strlen (src)); if (strlen (buf) + strlen (src) * 2 >= buf_size) error (_("Source string too long for buffer")); diff --git a/gdb/unittests/utils-selftests.c b/gdb/unittests/utils-selftests.c deleted file mode 100644 index b1c457c..0000000 --- a/gdb/unittests/utils-selftests.c +++ /dev/null @@ -1,59 +0,0 @@ -/* Unit tests for the utils.c file. - - Copyright (C) 2018-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 "utils.h" -#include "gdbsupport/selftest.h" - -namespace selftests { -namespace utils { - -static void -test_substitute_path_component () -{ - auto test = [] (std::string s, const char *from, const char *to, - const char *expected) - { - char *temp = xstrdup (s.c_str ()); - substitute_path_component (&temp, from, to); - SELF_CHECK (strcmp (temp, expected) == 0); - xfree (temp); - }; - - test ("/abc/$def/g", "abc", "xyz", "/xyz/$def/g"); - test ("abc/$def/g", "abc", "xyz", "xyz/$def/g"); - test ("/abc/$def/g", "$def", "xyz", "/abc/xyz/g"); - test ("/abc/$def/g", "g", "xyz", "/abc/$def/xyz"); - test ("/abc/$def/g", "ab", "xyz", "/abc/$def/g"); - test ("/abc/$def/g", "def", "xyz", "/abc/$def/g"); - test ("/abc/$def/g", "abc", "abc", "/abc/$def/g"); - test ("/abc/$def/g", "abc", "", "//$def/g"); - test ("/abc/$def/g", "abc/$def", "xyz", "/xyz/g"); - test ("/abc/$def/abc", "abc", "xyz", "/xyz/$def/xyz"); -} - -} -} - -void _initialize_utils_selftests (); -void -_initialize_utils_selftests () -{ - selftests::register_test ("substitute_path_component", - selftests::utils::test_substitute_path_component); -} diff --git a/gdb/utils.c b/gdb/utils.c index ce3c26e..7b0c812 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -177,6 +177,7 @@ vwarning (const char *string, va_list args) target_terminal::ours_for_output (); } gdb_puts (warning_pre_print, gdb_stderr); + print_warning_prefix (gdb_stderr); gdb_puts (_("warning: "), gdb_stderr); gdb_vprintf (gdb_stderr, string, args); gdb_printf (gdb_stderr, "\n"); @@ -3374,51 +3375,6 @@ parse_pid_to_attach (const char *args) return pid; } -/* Substitute all occurrences of string FROM by string TO in *STRINGP. *STRINGP - must come from xrealloc-compatible allocator and it may be updated. FROM - needs to be delimited by IS_DIR_SEPARATOR or DIRNAME_SEPARATOR (or be - located at the start or end of *STRINGP. */ - -void -substitute_path_component (char **stringp, const char *from, const char *to) -{ - char *string = *stringp, *s; - const size_t from_len = strlen (from); - const size_t to_len = strlen (to); - - for (s = string;;) - { - s = strstr (s, from); - if (s == NULL) - break; - - if ((s == string || IS_DIR_SEPARATOR (s[-1]) - || s[-1] == DIRNAME_SEPARATOR) - && (s[from_len] == '\0' || IS_DIR_SEPARATOR (s[from_len]) - || s[from_len] == DIRNAME_SEPARATOR)) - { - char *string_new; - - string_new - = (char *) xrealloc (string, (strlen (string) + to_len + 1)); - - /* Relocate the current S pointer. */ - s = s - string + string_new; - string = string_new; - - /* Replace from by to. */ - memmove (&s[to_len], &s[from_len], strlen (&s[from_len]) + 1); - memcpy (s, to, to_len); - - s += to_len; - } - else - s++; - } - - *stringp = string; -} - #ifdef HAVE_WAITPID #ifdef SIGALRM diff --git a/gdb/utils.h b/gdb/utils.h index bc8c2ef..a8834cf 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -133,9 +133,6 @@ private: extern int gdb_filename_fnmatch (const char *pattern, const char *string, int flags); -extern void substitute_path_component (char **stringp, const char *from, - const char *to); - std::string ldirname (const char *filename); extern int count_path_elements (const char *path); diff --git a/gdb/value.c b/gdb/value.c index 0f1be2e..41dce77 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -3266,6 +3266,9 @@ unpack_bits_as_long (struct type *field_type, const gdb_byte *valaddr, } } + if (field_type->code () == TYPE_CODE_RANGE) + val += field_type->bounds ()->bias; + return val; } @@ -3296,21 +3299,28 @@ unpack_value_field_as_long (struct type *type, const gdb_byte *valaddr, return 1; } -/* Unpack a field FIELDNO of the specified TYPE, from the anonymous - object at VALADDR. See unpack_bits_as_long for more details. */ +/* See value.h. */ LONGEST -unpack_field_as_long (struct type *type, const gdb_byte *valaddr, int fieldno) +unpack_field_as_long (const gdb_byte *valaddr, struct field *field) { - int bitpos = type->field (fieldno).loc_bitpos (); - int bitsize = type->field (fieldno).bitsize (); - struct type *field_type = type->field (fieldno).type (); + int bitpos = field->loc_bitpos (); + int bitsize = field->bitsize (); + struct type *field_type = field->type (); return unpack_bits_as_long (field_type, valaddr, bitpos, bitsize); } /* See value.h. */ +LONGEST +unpack_field_as_long (struct type *type, const gdb_byte *valaddr, int fieldno) +{ + return unpack_field_as_long (valaddr, &type->field (fieldno)); +} + +/* See value.h. */ + void value::unpack_bitfield (struct value *dest_val, LONGEST bitpos, LONGEST bitsize, diff --git a/gdb/value.h b/gdb/value.h index 71d0ba1..0c7c785 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -1058,10 +1058,19 @@ extern gdb_mpz value_as_mpz (struct value *val); extern LONGEST unpack_long (struct type *type, const gdb_byte *valaddr); extern CORE_ADDR unpack_pointer (struct type *type, const gdb_byte *valaddr); +/* Unpack a field FIELDNO of the specified TYPE, from the anonymous + object at VALADDR. See unpack_bits_as_long for more details. */ + extern LONGEST unpack_field_as_long (struct type *type, const gdb_byte *valaddr, int fieldno); +/* Unpack a field, FIELD, from the anonymous object at VALADDR. See + unpack_bits_as_long for more details. */ + +extern LONGEST unpack_field_as_long (const gdb_byte *valaddr, + struct field *field); + /* Unpack a bitfield of the specified FIELD_TYPE, from the object at VALADDR, and store the result in *RESULT. The bitfield starts at BITPOS bits and contains BITSIZE bits; if |