diff options
Diffstat (limited to 'gdb')
782 files changed, 14858 insertions, 5750 deletions
diff --git a/gdb/MAINTAINERS b/gdb/MAINTAINERS index 5e1aada..5284110 100644 --- a/gdb/MAINTAINERS +++ b/gdb/MAINTAINERS @@ -492,7 +492,7 @@ sim/ See sim/MAINTAINERS readline/ Master version: ftp://ftp.cwru.edu/pub/bash/ ALL - Host maintainers (host dependant parts) + Host maintainers (host dependent parts) (but get your changes into the master version) tcl/ tk/ itcl/ ALL @@ -602,6 +602,7 @@ Klaus Gerlicher klaus.gerlicher@intel.com Mircea Gherzan mircea.gherzan@intel.com Paul Gilliam pgilliam@us.ibm.com Tristan Gingold tgingold@free.fr +Timur Golubovich timurgol007@gmail.com Anton Gorenkov xgsa@yandex.ru Raoul Gough RaoulGough@yahoo.co.uk Anthony Green green@redhat.com diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 68b5e56..fc0c565 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -265,7 +265,7 @@ INCSUPPORT = \ -I.. # -# CLI sub directory definitons +# CLI sub directory definitions # SUBDIR_CLI_SRCS = \ cli/cli-cmds.c \ @@ -282,7 +282,7 @@ SUBDIR_CLI_SRCS = \ SUBDIR_CLI_OBS = $(patsubst %.c,%.o,$(SUBDIR_CLI_SRCS)) # -# MI sub directory definitons +# MI sub directory definitions # SUBDIR_MI_SRCS = \ mi/mi-cmd-break.c \ @@ -350,7 +350,7 @@ SUBDIR_GCC_COMPILE_SRCS = \ SUBDIR_GCC_COMPILE_OBS = $(patsubst %.c,%.o,$(filter %.c,$(SUBDIR_GCC_COMPILE_SRCS))) # -# Guile sub directory definitons for guile support. +# Guile sub directory definitions for guile support. # SUBDIR_GUILE_SRCS = \ guile/guile.c \ @@ -387,7 +387,7 @@ SUBDIR_GUILE_LDFLAGS = SUBDIR_GUILE_CFLAGS = # -# python sub directory definitons +# python sub directory definitions # SUBDIR_PYTHON_SRCS = \ python/py-arch.c \ @@ -479,6 +479,7 @@ SELFTESTS_SRCS = \ unittests/ptid-selftests.c \ unittests/main-thread-selftests.c \ unittests/mkdir-recursive-selftests.c \ + unittests/remote-arg-selftests.c \ unittests/rsp-low-selftests.c \ unittests/scoped_fd-selftests.c \ unittests/scoped_ignore_signal-selftests.c \ @@ -491,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 @@ -891,12 +891,14 @@ ALL_TARGET_OBS = \ solib-dsbt.o \ solib-frv.o \ solib-svr4.o \ + solib-svr4-linux.o \ sparc-linux-tdep.o \ sparc-netbsd-tdep.o \ sparc-obsd-tdep.o \ sparc-ravenscar-thread.o \ sparc-sol2-tdep.o \ sparc-tdep.o \ + svr4-tls-tdep.o \ symfile-mem.o \ tic6x-linux-tdep.o \ tic6x-tdep.o \ @@ -1091,36 +1093,6 @@ COMMON_SFILES = \ disasm.c \ displaced-stepping.c \ dummy-frame.c \ - dwarf2/abbrev.c \ - dwarf2/abbrev-table-cache.c \ - 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 \ - dwarf2/cooked-index-worker.c \ - dwarf2/cooked-indexer.c \ - dwarf2/cu.c \ - dwarf2/die.c \ - dwarf2/dwz.c \ - dwarf2/expr.c \ - dwarf2/frame-tailcall.c \ - dwarf2/frame.c \ - dwarf2/index-cache.c \ - dwarf2/index-common.c \ - dwarf2/index-write.c \ - dwarf2/leb.c \ - dwarf2/line-header.c \ - dwarf2/loc.c \ - dwarf2/macro.c \ - dwarf2/parent-map.c \ - dwarf2/read.c \ - dwarf2/read-debug-names.c \ - dwarf2/read-gdb-index.c \ - dwarf2/section.c \ - dwarf2/stringify.c \ extract-store-integer.c \ eval.c \ event-top.c \ @@ -1171,7 +1143,6 @@ COMMON_SFILES = \ maint.c \ maint-test-options.c \ maint-test-settings.c \ - mdebugread.c \ mem-break.c \ memattr.c \ memory-map.c \ @@ -1508,9 +1479,10 @@ HFILES_NO_SRCDIR = \ solib.h \ solib-aix.h \ solib-darwin.h \ + solib-frv.h \ solib-svr4.h \ + solib-svr4-linux.h \ solib-target.h \ - solist.h \ source.h \ source-cache.h \ sparc-nat.h \ @@ -1521,6 +1493,7 @@ HFILES_NO_SRCDIR = \ stabsread.h \ stack.h \ stap-probe.h \ + svr4-tls-tdep.h \ symfile.h \ symtab.h \ target.h \ @@ -1884,6 +1857,7 @@ ALLDEPFILES = \ sparc64-obsd-tdep.c \ sparc64-sol2-tdep.c \ sparc64-tdep.c \ + svr4-tls-tdep.c \ tilegx-linux-nat.c \ tilegx-linux-tdep.c \ tilegx-tdep.c \ @@ -1910,6 +1884,40 @@ TAGFILES_NO_SRCDIR = $(SFILES) $(HFILES_NO_SRCDIR) $(ALLDEPFILES) \ $(CONFIG_SRCS) TAGFILES_WITH_SRCDIR = $(HFILES_WITH_SRCDIR) +# Files that are used to support certain debuginfo formats +DWARF2_SRCS = \ + dwarf2/abbrev.c \ + dwarf2/abbrev-table-cache.c \ + dwarf2/ada-imported.c \ + dwarf2/aranges.c \ + dwarf2/attribute.c \ + dwarf2/cooked-index.c \ + dwarf2/cooked-index-entry.c \ + dwarf2/cooked-index-shard.c \ + dwarf2/cooked-index-worker.c \ + dwarf2/cooked-indexer.c \ + dwarf2/cu.c \ + dwarf2/die.c \ + dwarf2/dwz.c \ + dwarf2/expr.c \ + dwarf2/frame-tailcall.c \ + dwarf2/frame.c \ + dwarf2/index-cache.c \ + dwarf2/index-common.c \ + dwarf2/index-write.c \ + dwarf2/leb.c \ + dwarf2/line-header.c \ + dwarf2/loc.c \ + dwarf2/macro.c \ + dwarf2/parent-map.c \ + dwarf2/read.c \ + dwarf2/read-debug-names.c \ + dwarf2/read-gdb-index.c \ + dwarf2/section.c \ + dwarf2/stringify.c \ + dwarf2/unit-head.c +DWARF2_OBS = $(patsubst %.c,%.o, $(DWARF2_SRCS)) + COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ mi/mi-common.o \ version.o \ @@ -35,18 +35,18 @@ a -h or --help option, which prints each options and a brief description. -* On systems that support linkage namespaces, the output of the command +* On systems that support linker namespaces, the output of the command "info sharedlibraries" may add one more column, NS, which identifies the namespace into which the library was loaded, if more than one namespace is active. * New built-in convenience variables $_active_linker_namespaces and - $_current_linker_namespace. These show the number of active linkage + $_linker_namespace. These show the number of active linker namespaces, and the namespace to which the current location belongs to. - In systems that don't support linkage namespaces, these always return 1 - and [[0]] respectively. + In systems that don't support linker namespaces, these always return + the integers 1 and 0 respectively. - * Add record full support for rv64gc architectures +* Add record full support for rv64gc architectures * New commands @@ -56,12 +56,34 @@ maintenance check psymtabs maintenance check symtabs Renamed from maintenance check-symtabs +maintenance canonicalize + Show the canonical form of a C++ name. + +maintenance set console-translation-mode <binary|text> +maintenance show console-translation-mode + Controls the translation mode of GDB stdout/stderr. MS-Windows only. In + binary mode, no translation is done. In text mode, a Line Feed is + translated into a Carriage Return-Line Feed combination. + 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,8 +96,42 @@ 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 + architectures now have GDB-internal support for TLS address + lookup in addition to that traditionally provided by the + libthread_db library. This internal support works for programs + linked against either the GLIBC or MUSL C libraries. For + programs linked against MUSL, this new internal support provides + new debug functionality, allowing access to TLS variables, due to + the fact that MUSL does not implement the libthread_db library. + Internal TLS support is also useful in cross-debugging + situations, debugging statically linked binaries, and debugging + programs linked against GLIBC 2.33 and earlier, but which are not + linked against libpthread. + + ** The command 'maint set force-internal-tls-address-lookup on' may + be used to force the internal TLS lookup mechanisms to be used. + Otherwise, TLS lookup via libthread_db will still be preferred, + when available. + * Python API + ** GDB no longer supports Python versions less than 3.4. + ** New class gdb.Color for dealing with colors. ** New constant gdb.PARAM_COLOR represents color type of a @@ -92,6 +148,23 @@ info sharedlibrary when output is going to standard output, and False when output is going to a string. + ** Setting the documentation string (__doc__) of a gdb.Parameter + sub-class to the empty string, means GDB will only display the + set_doc or show_doc strings in the set/show help output. + + ** New gdb.ParameterPrefix class. This can be used to create 'set' + and 'show' gdb.Command prefixes, suitable for use with new + gdb.Parameters. + + ** Prefix commands (gdb.Command sub-classes) that don't have an + invoke method will now behave like builtin prefix commands when + invoked without a sub-command name. This means printing the help + text for all sub-commands, unless the prefix command is a 'show' + command, in which case the value of all sub-commands is printed. + + ** New gdb.warning() function that takes a string and prints it as a + warning, with GDB's standard 'warning' prefix. + * Guile API ** New type <gdb:color> for dealing with colors. @@ -99,6 +172,17 @@ info sharedlibrary ** New constant PARAM_COLOR represents color type of a value of a <gdb:parameter> object. Parameter's value is <gdb::color> instance. + ** Eliding the #:doc string from make-parameter now means that GDB + will use a default documentation string. Setting #:doc to the + empty string for make-parameter means GDB will only display the + #:set_doc or #:show_doc strings in the set/show help output. + + ** Prefix commands (using make-command) that don't have a #:invoke + property will now behave like builtin prefix commands when + invoked without a sub-command name. This means printing the help + text for all sub-commands, unless the prefix command is a 'show' + command, in which case the value of all sub-commands is printed. + * New remote packets binary-upload in qSupported reply @@ -107,6 +191,11 @@ binary-upload in qSupported reply stub doesn't report this feature supported, then GDB will not use the 'x' packet. +vFile:lstat + Return information about files on the remote system. Like + vFile:stat but if the filename is a symbolic link, return + information about the link itself, the file the link refers to. + * Changed remote packets qXfer:threads:read @@ -115,6 +204,11 @@ qXfer:threads:read should print as the target ID of the thread, for example in the "info threads" command or when switching to the thread. +vFile:stat + Previously, gdbserver incorrectly implemented this packet using + lstat rather than stat. This has now been corrected. The + documentation has also been clarified. + * MI changes ** The =library-unloaded event now includes the 'ranges' field, which @@ -134,6 +228,14 @@ qXfer:threads:read subsystem to be disabled at configure time, in the form of --disable-gdb-compile. +* A new configure option was added, allowing support for DWARF debug + information to be disabled at configure time. The flag is + --disable-gdb-dwarf-support. + +* A new configure option was added, allowing support for mdebug/ecoff + debug information to be disabled at configure time. The flag to do + that is --disable-gdb-mdebug-support. + *** Changes in GDB 16 * Support for Nios II targets has been removed as this architecture @@ -7509,7 +7611,7 @@ for DW_OP_piece is still missing). A number of long standing bugs that caused GDB to die while starting a Java application have been fixed. GDB's Java support is now -considered "useable". +considered "usable". * GNU/Linux support for fork, vfork, and exec. @@ -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 @@ -443,7 +443,14 @@ more obscure GDB `configure' options are not listed here. supported). `--disable-gdb-compile' - Build GDB without support for the 'compile' command. + Build GDB without support for the 'compile' command. DWARF support + is required for this feature. + +`--disable-gdb-dwarf-support' + Build GDB without support for reading DWARF debug information. + +`--disable-gdb-mdebug-support' + Build GDB without support for reading mdebug debug information. `--with-curses' Use the curses library instead of the termcap library, for @@ -533,7 +540,7 @@ more obscure GDB `configure' options are not listed here. GDB scripting much more powerful than the restricted CLI scripting language. If your host does not have Python installed, you can find it on `http://www.python.org/download/'. The oldest - version of Python supported by GDB is 3.2. The optional argument + version of Python supported by GDB is 3.4. The optional argument PYTHON is used to find the Python headers and libraries. It can be either the name of a Python executable, or the name of the directory in which Python is installed. diff --git a/gdb/aarch64-fbsd-nat.c b/gdb/aarch64-fbsd-nat.c index 1746ad1..ecf0bb2 100644 --- a/gdb/aarch64-fbsd-nat.c +++ b/gdb/aarch64-fbsd-nat.c @@ -348,9 +348,7 @@ aarch64_notify_debug_reg_change (ptid_t ptid, } #endif -void _initialize_aarch64_fbsd_nat (); -void -_initialize_aarch64_fbsd_nat () +INIT_GDB_FILE (aarch64_fbsd_nat) { #ifdef HAVE_DBREG aarch64_initialize_hw_point (); diff --git a/gdb/aarch64-fbsd-tdep.c b/gdb/aarch64-fbsd-tdep.c index 07fa38a..db9b82f 100644 --- a/gdb/aarch64-fbsd-tdep.c +++ b/gdb/aarch64-fbsd-tdep.c @@ -239,8 +239,7 @@ aarch64_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Generic FreeBSD support. */ fbsd_init_abi (info, gdbarch); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); tramp_frame_prepend_unwinder (gdbarch, &aarch64_fbsd_sigframe); @@ -261,9 +260,7 @@ aarch64_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) } } -void _initialize_aarch64_fbsd_tdep (); -void -_initialize_aarch64_fbsd_tdep () +INIT_GDB_FILE (aarch64_fbsd_tdep) { gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_FREEBSD, aarch64_fbsd_init_abi); diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index d7869f4..725c632 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -1068,9 +1068,7 @@ aarch64_linux_nat_target::is_address_tagged (gdbarch *gdbarch, CORE_ADDR address return gdbarch_tagged_address_p (gdbarch, address); } -void _initialize_aarch64_linux_nat (); -void -_initialize_aarch64_linux_nat () +INIT_GDB_FILE (aarch64_linux_nat) { aarch64_initialize_hw_point (); diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 9cee355..dd35eaf 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -23,7 +23,9 @@ #include "extract-store-integer.h" #include "gdbarch.h" #include "glibc-tdep.h" +#include "solib-svr4-linux.h" #include "linux-tdep.h" +#include "svr4-tls-tdep.h" #include "aarch64-tdep.h" #include "aarch64-linux-tdep.h" #include "osabi.h" @@ -35,6 +37,7 @@ #include "target/target.h" #include "expop.h" #include "auxv.h" +#include "inferior.h" #include "regcache.h" #include "regset.h" @@ -2597,8 +2600,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; } @@ -2701,6 +2704,57 @@ aarch64_use_target_description_from_corefile_notes (gdbarch *gdbarch, return true; } +/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID. + Throw a suitable TLS error if something goes wrong. */ + +static CORE_ADDR +aarch64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid, + svr4_tls_libc libc) +{ + /* On aarch64, the thread pointer is found in the TPIDR register. + Note that this is the first register in the TLS feature - see + features/aarch64-tls.c - and it will always be present. */ + regcache *regcache + = get_thread_arch_regcache (current_inferior (), ptid, gdbarch); + aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch); + target_fetch_registers (regcache, tdep->tls_regnum_base); + ULONGEST thr_ptr; + if (regcache->cooked_read (tdep->tls_regnum_base, &thr_ptr) != REG_VALID) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer")); + + CORE_ADDR dtv_ptr_addr; + switch (libc) + { + case svr4_tls_libc_musl: + /* MUSL: The DTV pointer is found at the very end of the pthread + struct which is located *before* the thread pointer. I.e. + the thread pointer will be just beyond the end of the struct, + so the address of the DTV pointer is found one pointer-size + before the thread pointer. */ + dtv_ptr_addr = thr_ptr - (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + break; + case svr4_tls_libc_glibc: + /* GLIBC: The thread pointer (tpidr) points at the TCB (thread control + block). On aarch64, this struct (tcbhead_t) is defined to + contain two pointers. The first is a pointer to the DTV and + the second is a pointer to private data. So the DTV pointer + address is the same as the thread pointer. */ + dtv_ptr_addr = thr_ptr; + break; + default: + throw_error (TLS_GENERIC_ERROR, _("Unknown aarch64 C library")); + break; + } + gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address")); + + const struct builtin_type *builtin = builtin_type (gdbarch); + CORE_ADDR dtv_addr = gdbarch_pointer_to_address + (gdbarch, builtin->builtin_data_ptr, buf.data ()); + return dtv_addr; +} + static void aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -2715,13 +2769,14 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->lowest_pc = 0x8000; linux_init_abi (info, gdbarch, 1); - - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops); /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); + set_gdbarch_get_thread_local_address (gdbarch, + svr4_tls_get_thread_local_address); + svr4_tls_register_tls_methods (info, gdbarch, aarch64_linux_get_tls_dtv_addr); /* Shared library handling. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); @@ -2985,9 +3040,7 @@ aarch64_linux_ltag_tests (void) } /* namespace selftests */ #endif /* GDB_SELF_TEST */ -void _initialize_aarch64_linux_tdep (); -void -_initialize_aarch64_linux_tdep () +INIT_GDB_FILE (aarch64_linux_tdep) { gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX, aarch64_linux_init_abi); diff --git a/gdb/aarch64-newlib-tdep.c b/gdb/aarch64-newlib-tdep.c index c0ecd3f..5045d5f9 100644 --- a/gdb/aarch64-newlib-tdep.c +++ b/gdb/aarch64-newlib-tdep.c @@ -35,9 +35,7 @@ aarch64_newlib_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->jb_pc = 11; } -void _initialize_aarch64_newlib_tdep (); -void -_initialize_aarch64_newlib_tdep () +INIT_GDB_FILE (aarch64_newlib_tdep) { gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_NEWLIB, aarch64_newlib_init_abi); diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index bf5b5d3..f2e3ce2 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. */ @@ -4914,9 +4914,7 @@ static void aarch64_process_record_test (void); } #endif -void _initialize_aarch64_tdep (); -void -_initialize_aarch64_tdep () +INIT_GDB_FILE (aarch64_tdep) { gdbarch_register (bfd_arch_aarch64, aarch64_gdbarch_init, aarch64_dump_tdep); diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index 3f5e707..329d114 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -13928,9 +13928,7 @@ static const char * const gnat_source_charsets[] = nullptr }; -void _initialize_ada_language (); -void -_initialize_ada_language () +INIT_GDB_FILE (ada_language) { add_setshow_prefix_cmd ("ada", no_class, diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c index 2595123..f9551d4 100644 --- a/gdb/ada-tasks.c +++ b/gdb/ada-tasks.c @@ -1650,9 +1650,7 @@ task_apply_command (const char *tidlist, int from_tty) from_tty, flags); } -void _initialize_tasks (); -void -_initialize_tasks () +INIT_GDB_FILE (tasks) { /* Attach various observers. */ gdb::observers::normal_stop.attach (ada_tasks_normal_stop_observer, diff --git a/gdb/ada-typeprint.c b/gdb/ada-typeprint.c index 404d8fe..defd828 100644 --- a/gdb/ada-typeprint.c +++ b/gdb/ada-typeprint.c @@ -702,7 +702,7 @@ print_variant_part (const variant_part &part, name = "?"; else { - name = type->field (part.discriminant_index).name ();; + name = type->field (part.discriminant_index).name (); discr_type = type->field (part.discriminant_index).type (); } diff --git a/gdb/addrmap.c b/gdb/addrmap.c index c625973..4549350 100644 --- a/gdb/addrmap.c +++ b/gdb/addrmap.c @@ -398,20 +398,20 @@ namespace selftests { /* Convert P to CORE_ADDR. */ static CORE_ADDR -core_addr (void *p) +core_addr (const void *p) { - return (CORE_ADDR)(uintptr_t)p; + return (CORE_ADDR) (uintptr_t) p; } /* Check that &ARRAY[LOW]..&ARRAY[HIGH] has VAL in MAP. */ -#define CHECK_ADDRMAP_FIND(MAP, ARRAY, LOW, HIGH, VAL) \ - do \ - { \ - for (unsigned i = LOW; i <= HIGH; ++i) \ - SELF_CHECK (MAP->find (core_addr (&ARRAY[i])) == VAL); \ - } \ - while (0) +static void +check_addrmap_find (const addrmap &map, const char *array, unsigned int low, + unsigned int high, const void *val) +{ + for (unsigned int i = low; i <= high; ++i) + SELF_CHECK (map.find (core_addr (&array[i])) == val); +} /* Entry point for addrmap unit tests. */ @@ -427,25 +427,24 @@ test_addrmap () /* Create mutable addrmap. */ auto_obstack temp_obstack; - auto map = std::make_unique<struct addrmap_mutable> (); - SELF_CHECK (map != nullptr); + addrmap_mutable map; /* Check initial state. */ - CHECK_ADDRMAP_FIND (map, array, 0, 19, nullptr); + check_addrmap_find (map, array, 0, 19, nullptr); /* Insert address range into mutable addrmap. */ - map->set_empty (core_addr (&array[10]), core_addr (&array[12]), val1); - CHECK_ADDRMAP_FIND (map, array, 0, 9, nullptr); - CHECK_ADDRMAP_FIND (map, array, 10, 12, val1); - CHECK_ADDRMAP_FIND (map, array, 13, 19, nullptr); + map.set_empty (core_addr (&array[10]), core_addr (&array[12]), val1); + check_addrmap_find (map, array, 0, 9, nullptr); + check_addrmap_find (map, array, 10, 12, val1); + check_addrmap_find (map, array, 13, 19, nullptr); /* Create corresponding fixed addrmap. */ addrmap_fixed *map2 - = new (&temp_obstack) addrmap_fixed (&temp_obstack, map.get ()); + = new (&temp_obstack) addrmap_fixed (&temp_obstack, &map); SELF_CHECK (map2 != nullptr); - CHECK_ADDRMAP_FIND (map2, array, 0, 9, nullptr); - CHECK_ADDRMAP_FIND (map2, array, 10, 12, val1); - CHECK_ADDRMAP_FIND (map2, array, 13, 19, nullptr); + check_addrmap_find (*map2, array, 0, 9, nullptr); + check_addrmap_find (*map2, array, 10, 12, val1); + check_addrmap_find (*map2, array, 13, 19, nullptr); /* Iterate over both addrmaps. */ auto callback = [&] (CORE_ADDR start_addr, void *obj) @@ -460,29 +459,27 @@ test_addrmap () SELF_CHECK (false); return 0; }; - SELF_CHECK (map->foreach (callback) == 0); + SELF_CHECK (map.foreach (callback) == 0); SELF_CHECK (map2->foreach (callback) == 0); /* Relocate fixed addrmap. */ map2->relocate (1); - CHECK_ADDRMAP_FIND (map2, array, 0, 10, nullptr); - CHECK_ADDRMAP_FIND (map2, array, 11, 13, val1); - CHECK_ADDRMAP_FIND (map2, array, 14, 19, nullptr); + check_addrmap_find (*map2, array, 0, 10, nullptr); + check_addrmap_find (*map2, array, 11, 13, val1); + check_addrmap_find (*map2, array, 14, 19, nullptr); /* Insert partially overlapping address range into mutable addrmap. */ - map->set_empty (core_addr (&array[11]), core_addr (&array[13]), val2); - CHECK_ADDRMAP_FIND (map, array, 0, 9, nullptr); - CHECK_ADDRMAP_FIND (map, array, 10, 12, val1); - CHECK_ADDRMAP_FIND (map, array, 13, 13, val2); - CHECK_ADDRMAP_FIND (map, array, 14, 19, nullptr); + map.set_empty (core_addr (&array[11]), core_addr (&array[13]), val2); + check_addrmap_find (map, array, 0, 9, nullptr); + check_addrmap_find (map, array, 10, 12, val1); + check_addrmap_find (map, array, 13, 13, val2); + check_addrmap_find (map, array, 14, 19, nullptr); } } /* namespace selftests */ #endif /* GDB_SELF_TEST */ -void _initialize_addrmap (); -void -_initialize_addrmap () +INIT_GDB_FILE (addrmap) { #if GDB_SELF_TEST selftests::register_test ("addrmap", selftests::test_addrmap); diff --git a/gdb/agent.c b/gdb/agent.c index 2009980..dd3a557 100644 --- a/gdb/agent.c +++ b/gdb/agent.c @@ -73,9 +73,7 @@ agent_new_objfile (struct objfile *objfile) agent_look_up_symbols (objfile); } -void _initialize_agent (); -void -_initialize_agent () +INIT_GDB_FILE (agent) { gdb::observers::new_objfile.attach (agent_new_objfile, "agent"); diff --git a/gdb/aix-thread.c b/gdb/aix-thread.c index 2fd6121..1e3015d 100644 --- a/gdb/aix-thread.c +++ b/gdb/aix-thread.c @@ -2062,9 +2062,7 @@ aix_thread_target::get_ada_task_ptid (long lwp, ULONGEST thread) /* Module startup initialization function, automagically called by init.c. */ -void _initialize_aix_thread (); -void -_initialize_aix_thread () +INIT_GDB_FILE (aix_thread) { /* Notice when object files get loaded and unloaded. */ gdb::observers::new_objfile.attach (new_objfile, "aix-thread"); diff --git a/gdb/alpha-bsd-nat.c b/gdb/alpha-bsd-nat.c index 85fb525..5f7d3ef 100644 --- a/gdb/alpha-bsd-nat.c +++ b/gdb/alpha-bsd-nat.c @@ -156,9 +156,7 @@ alphabsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) } -void _initialize_alphabsd_nat (); -void -_initialize_alphabsd_nat () +INIT_GDB_FILE (alphabsd_nat) { add_inf_child_target (&the_alpha_bsd_nat_target); diff --git a/gdb/alpha-linux-nat.c b/gdb/alpha-linux-nat.c index 6d9de81..1297a45 100644 --- a/gdb/alpha-linux-nat.c +++ b/gdb/alpha-linux-nat.c @@ -100,9 +100,7 @@ alpha_linux_nat_target::register_u_offset (struct gdbarch *gdbarch, return FPR_BASE + regno - gdbarch_fp0_regnum (gdbarch); } -void _initialize_alpha_linux_nat (); -void -_initialize_alpha_linux_nat () +INIT_GDB_FILE (alpha_linux_nat) { linux_target = &the_alpha_linux_nat_target; add_inf_child_target (&the_alpha_linux_nat_target); diff --git a/gdb/alpha-linux-tdep.c b/gdb/alpha-linux-tdep.c index 2f6affa..8b4b4f1 100644 --- a/gdb/alpha-linux-tdep.c +++ b/gdb/alpha-linux-tdep.c @@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "frame.h" +#include "solib-svr4-linux.h" #include "osabi.h" #include "solib-svr4.h" #include "symtab.h" @@ -369,9 +370,7 @@ alpha_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->jb_elt_size = 8; set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops); /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, @@ -386,9 +385,7 @@ alpha_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) alpha_linux_gdb_signal_to_target); } -void _initialize_alpha_linux_tdep (); -void -_initialize_alpha_linux_tdep () +INIT_GDB_FILE (alpha_linux_tdep) { gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_LINUX, alpha_linux_init_abi); diff --git a/gdb/alpha-netbsd-tdep.c b/gdb/alpha-netbsd-tdep.c index a240039..0f5e305 100644 --- a/gdb/alpha-netbsd-tdep.c +++ b/gdb/alpha-netbsd-tdep.c @@ -264,8 +264,7 @@ alphanbsd_init_abi (struct gdbarch_info info, set_gdbarch_software_single_step (gdbarch, alpha_software_single_step); /* NetBSD/alpha has SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset; tdep->pc_in_sigtramp = alphanbsd_pc_in_sigtramp; @@ -279,9 +278,7 @@ alphanbsd_init_abi (struct gdbarch_info info, } -void _initialize_alphanbsd_tdep (); -void -_initialize_alphanbsd_tdep () +INIT_GDB_FILE (alphanbsd_tdep) { /* Even though NetBSD/alpha used ELF since day one, it used the traditional a.out-style core dump format before NetBSD 1.6, but diff --git a/gdb/alpha-obsd-tdep.c b/gdb/alpha-obsd-tdep.c index b5ddbbc..63f9050 100644 --- a/gdb/alpha-obsd-tdep.c +++ b/gdb/alpha-obsd-tdep.c @@ -109,8 +109,7 @@ alphaobsd_init_abi(struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_software_single_step (gdbarch, alpha_software_single_step); /* OpenBSD/alpha has SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver); tdep->dynamic_sigtramp_offset = alphaobsd_sigtramp_offset; @@ -125,9 +124,7 @@ alphaobsd_init_abi(struct gdbarch_info info, struct gdbarch *gdbarch) } -void _initialize_alphaobsd_tdep (); -void -_initialize_alphaobsd_tdep () +INIT_GDB_FILE (alphaobsd_tdep) { gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_OPENBSD, alphaobsd_init_abi); diff --git a/gdb/alpha-tdep.c b/gdb/alpha-tdep.c index f0272b0..92a6411 100644 --- a/gdb/alpha-tdep.c +++ b/gdb/alpha-tdep.c @@ -1815,9 +1815,7 @@ alpha_dwarf2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer); } -void _initialize_alpha_tdep (); -void -_initialize_alpha_tdep () +INIT_GDB_FILE (alpha_tdep) { gdbarch_register (bfd_arch_alpha, alpha_gdbarch_init, NULL); diff --git a/gdb/amd-dbgapi-target.c b/gdb/amd-dbgapi-target.c index 8e067b6..a0210f4 100644 --- a/gdb/amd-dbgapi-target.c +++ b/gdb/amd-dbgapi-target.c @@ -104,12 +104,26 @@ amd_dbgapi_lib_debug_module () static gdb::observers::token amd_dbgapi_target_inferior_created_observer_token; +/* See amd-dbgapi-target.h. */ + const gdb::observers::token & get_amd_dbgapi_target_inferior_created_observer_token () { return amd_dbgapi_target_inferior_created_observer_token; } +/* inferior_execd observer token. */ + +static gdb::observers::token amd_dbgapi_target_inferior_execd_observer_token; + +/* See amd-dbgapi-target.h. */ + +const gdb::observers::token & +get_amd_dbgapi_target_inferior_execd_observer_token () +{ + return amd_dbgapi_target_inferior_execd_observer_token; +} + /* A type holding coordinates, etc. info for a given wave. */ struct wave_coordinates @@ -234,7 +248,7 @@ struct amd_dbgapi_inferior_info }; static amd_dbgapi_event_id_t process_event_queue - (amd_dbgapi_process_id_t process_id, + (amd_dbgapi_inferior_info &info, amd_dbgapi_event_kind_t until_event_kind = AMD_DBGAPI_EVENT_KIND_NONE); static const target_info amd_dbgapi_target_info = { @@ -443,6 +457,32 @@ async_event_handler_mark () mark_async_event_handler (amd_dbgapi_async_event_handler); } +/* Set forward progress requirement to REQUIRE for inferior INFO. */ + +static void +require_forward_progress (amd_dbgapi_inferior_info &info, bool require) +{ + /* If we try to disable forward progress requirement but the target expects + resumed threads to be committed to the target, we could wait for events + that will never arrive. */ + if (!require) + gdb_assert (!info.inf->process_target ()->commit_resumed_state); + + gdb_assert (info.process_id != AMD_DBGAPI_PROCESS_NONE); + + /* Don't do unnecessary calls to amd-dbgapi to avoid polluting the logs. */ + if (info.forward_progress_required == require) + return; + + const auto progress + = require ? AMD_DBGAPI_PROGRESS_NORMAL : AMD_DBGAPI_PROGRESS_NO_FORWARD; + const auto status + = amd_dbgapi_process_set_progress (info.process_id, progress); + gdb_assert (status == AMD_DBGAPI_STATUS_SUCCESS); + + info.forward_progress_required = require; +} + /* Set forward progress requirement to REQUIRE for all processes of PROC_TARGET matching PTID. */ @@ -457,21 +497,8 @@ require_forward_progress (ptid_t ptid, process_stratum_target *proc_target, amd_dbgapi_inferior_info *info = get_amd_dbgapi_inferior_info (inf); - if (info->process_id == AMD_DBGAPI_PROCESS_NONE) - continue; - - /* Don't do unnecessary calls to amd-dbgapi to avoid polluting the logs. */ - if (info->forward_progress_required == require) - continue; - - amd_dbgapi_status_t status - = amd_dbgapi_process_set_progress - (info->process_id, (require - ? AMD_DBGAPI_PROGRESS_NORMAL - : AMD_DBGAPI_PROGRESS_NO_FORWARD)); - gdb_assert (status == AMD_DBGAPI_STATUS_SUCCESS); - - info->forward_progress_required = require; + if (info->process_id != AMD_DBGAPI_PROCESS_NONE) + require_forward_progress (*info, require); /* If ptid targets a single inferior and we have found it, no need to continue. */ @@ -555,11 +582,12 @@ amd_dbgapi_target_breakpoint::check_status (struct bpstat *bs) if (action == AMD_DBGAPI_BREAKPOINT_ACTION_RESUME) return; + require_forward_progress (*info, false); + /* If the action is AMD_DBGAPI_BREAKPOINT_ACTION_HALT, we need to wait until a breakpoint resume event for this breakpoint_id is seen. */ amd_dbgapi_event_id_t resume_event_id - = process_event_queue (info->process_id, - AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME); + = process_event_queue (*info, AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME); /* We should always get a breakpoint_resume event after processing all events generated by reporting the breakpoint hit. */ @@ -1138,32 +1166,14 @@ add_gpu_thread (inferior *inf, ptid_t wave_ptid) /* Process an event that was just pulled out of the amd-dbgapi library. */ static void -process_one_event (amd_dbgapi_event_id_t event_id, +process_one_event (amd_dbgapi_inferior_info &info, + amd_dbgapi_event_id_t event_id, amd_dbgapi_event_kind_t event_kind) { /* Automatically mark this event processed when going out of scope. */ scoped_amd_dbgapi_event_processed mark_event_processed (event_id); - amd_dbgapi_process_id_t process_id; - amd_dbgapi_status_t status - = amd_dbgapi_event_get_info (event_id, AMD_DBGAPI_EVENT_INFO_PROCESS, - sizeof (process_id), &process_id); - if (status != AMD_DBGAPI_STATUS_SUCCESS) - error (_("event_get_info for event_%ld failed (%s)"), event_id.handle, - get_status_string (status)); - - amd_dbgapi_os_process_id_t pid; - status = amd_dbgapi_process_get_info (process_id, - AMD_DBGAPI_PROCESS_INFO_OS_ID, - sizeof (pid), &pid); - if (status != AMD_DBGAPI_STATUS_SUCCESS) - error (_("process_get_info for process_%ld failed (%s)"), - process_id.handle, get_status_string (status)); - - auto *proc_target = current_inferior ()->process_target (); - inferior *inf = find_inferior_pid (proc_target, pid); - gdb_assert (inf != nullptr); - amd_dbgapi_inferior_info *info = get_amd_dbgapi_inferior_info (inf); + gdb_assert (info.inf != nullptr); switch (event_kind) { @@ -1171,14 +1181,14 @@ process_one_event (amd_dbgapi_event_id_t event_id, case AMD_DBGAPI_EVENT_KIND_WAVE_STOP: { amd_dbgapi_wave_id_t wave_id; - status + amd_dbgapi_status_t status = amd_dbgapi_event_get_info (event_id, AMD_DBGAPI_EVENT_INFO_WAVE, sizeof (wave_id), &wave_id); if (status != AMD_DBGAPI_STATUS_SUCCESS) error (_("event_get_info for event_%ld failed (%s)"), event_id.handle, get_status_string (status)); - ptid_t event_ptid = make_gpu_ptid (pid, wave_id); + ptid_t event_ptid = make_gpu_ptid (info.inf->pid, wave_id); target_waitstatus ws; amd_dbgapi_wave_stop_reasons_t stop_reason; @@ -1219,9 +1229,10 @@ process_one_event (amd_dbgapi_event_id_t event_id, else ws.set_stopped (GDB_SIGNAL_0); - thread_info *thread = proc_target->find_thread (event_ptid); + thread_info *thread + = info.inf->process_target ()->find_thread (event_ptid); if (thread == nullptr) - thread = add_gpu_thread (inf, event_ptid); + thread = add_gpu_thread (info.inf, event_ptid); /* If the wave is stopped because of a software breakpoint, the program counter needs to be adjusted so that it points to the @@ -1243,7 +1254,7 @@ process_one_event (amd_dbgapi_event_id_t event_id, error (_("wave_get_info for wave_%ld failed (%s)"), wave_id.handle, get_status_string (status)); - info->wave_events.emplace_back (event_ptid, ws); + info.wave_events.emplace_back (event_ptid, ws); break; } @@ -1261,7 +1272,7 @@ process_one_event (amd_dbgapi_event_id_t event_id, When amd_dbgapi_target_breakpoint::check_status is called, the current inferior is the inferior that hit the breakpoint, which should still be the case now. */ - gdb_assert (inf == current_inferior ()); + gdb_assert (info.inf == current_inferior ()); handle_solib_event (); break; @@ -1275,22 +1286,22 @@ process_one_event (amd_dbgapi_event_id_t event_id, { amd_dbgapi_runtime_state_t runtime_state; - status = amd_dbgapi_event_get_info (event_id, - AMD_DBGAPI_EVENT_INFO_RUNTIME_STATE, - sizeof (runtime_state), - &runtime_state); + amd_dbgapi_status_t status + = amd_dbgapi_event_get_info (event_id, + AMD_DBGAPI_EVENT_INFO_RUNTIME_STATE, + sizeof (runtime_state), &runtime_state); if (status != AMD_DBGAPI_STATUS_SUCCESS) error (_("event_get_info for event_%ld failed (%s)"), event_id.handle, get_status_string (status)); gdb_assert (runtime_state == AMD_DBGAPI_RUNTIME_STATE_UNLOADED); gdb_assert - (info->runtime_state == AMD_DBGAPI_RUNTIME_STATE_LOADED_SUCCESS); + (info.runtime_state == AMD_DBGAPI_RUNTIME_STATE_LOADED_SUCCESS); - info->runtime_state = runtime_state; + info.runtime_state = runtime_state; - gdb_assert (inf->target_is_pushed (&the_amd_dbgapi_target)); - inf->unpush_target (&the_amd_dbgapi_target); + gdb_assert (info.inf->target_is_pushed (&the_amd_dbgapi_target)); + info.inf->unpush_target (&the_amd_dbgapi_target); } break; @@ -1331,20 +1342,18 @@ event_kind_str (amd_dbgapi_event_kind_t kind) gdb_assert_not_reached ("unhandled amd_dbgapi_event_kind_t value"); } -/* Drain the dbgapi event queue of a given process_id, or of all processes if - process_id is AMD_DBGAPI_PROCESS_NONE. Stop processing the events if an - event of a given kind is requested and `process_id` is not - AMD_DBGAPI_PROCESS_NONE. Wave stop events that are not returned are queued - into their inferior's amd_dbgapi_inferior_info pending wave events. */ +/* Drain the dbgapi event queue of a given inferior. Stop processing the + events if an event of a given kind is requested (not AMD_DBGAPI_EVENT_NONE). + Wave stop events that are not returned are queued into their inferior's + amd_dbgapi_inferior_info pending wave events. */ static amd_dbgapi_event_id_t -process_event_queue (amd_dbgapi_process_id_t process_id, +process_event_queue (amd_dbgapi_inferior_info &info, amd_dbgapi_event_kind_t until_event_kind) { - /* An event of a given type can only be requested from a single - process_id. */ - gdb_assert (until_event_kind == AMD_DBGAPI_EVENT_KIND_NONE - || process_id != AMD_DBGAPI_PROCESS_NONE); + /* Pulling events with forward progress required may result in bad + performance, make sure it is not required. */ + gdb_assert (!info.forward_progress_required); while (true) { @@ -1352,7 +1361,7 @@ process_event_queue (amd_dbgapi_process_id_t process_id, amd_dbgapi_event_kind_t event_kind; amd_dbgapi_status_t status - = amd_dbgapi_process_next_pending_event (process_id, &event_id, + = amd_dbgapi_process_next_pending_event (info.process_id, &event_id, &event_kind); if (status != AMD_DBGAPI_STATUS_SUCCESS) @@ -1368,7 +1377,7 @@ process_event_queue (amd_dbgapi_process_id_t process_id, if (event_id == AMD_DBGAPI_EVENT_NONE || event_kind == until_event_kind) return event_id; - process_one_event (event_id, event_kind); + process_one_event (info, event_id, event_kind); } } @@ -1473,7 +1482,7 @@ amd_dbgapi_target::wait (ptid_t ptid, struct target_waitstatus *ws, /* Drain the events for the current inferior from the amd_dbgapi and preserve the ordering. */ auto info = get_amd_dbgapi_inferior_info (current_inferior ()); - process_event_queue (info->process_id, AMD_DBGAPI_EVENT_KIND_NONE); + process_event_queue (*info); std::tie (event_ptid, gpu_waitstatus) = consume_one_event (ptid.pid ()); if (event_ptid == minus_one_ptid) @@ -1860,13 +1869,14 @@ amd_dbgapi_target::update_thread_list () if (changed == AMD_DBGAPI_CHANGED_NO) continue; + gdb::unique_xmalloc_ptr<amd_dbgapi_wave_id_t> wave_list_holder + (wave_list); + /* Create a set and free the wave list. */ std::set<ptid_t::tid_type> threads; for (size_t i = 0; i < count; ++i) threads.emplace (wave_list[i].handle); - xfree (wave_list); - /* Prune the wave_ids that already have a thread_info. Any thread_info which does not have a corresponding wave_id represents a wave which is gone at this point and should be deleted. */ @@ -2479,10 +2489,7 @@ maybe_reset_amd_dbgapi () get_status_string (status)); } -extern initialize_file_ftype _initialize_amd_dbgapi_target; - -void -_initialize_amd_dbgapi_target () +INIT_GDB_FILE (amd_dbgapi_target) { /* Make sure the loaded debugger library version is greater than or equal to the one used to build GDB. */ @@ -2510,7 +2517,9 @@ _initialize_amd_dbgapi_target () gdb::observers::inferior_created.attach (amd_dbgapi_target_inferior_created, amd_dbgapi_target_inferior_created_observer_token, "amd-dbgapi"); - gdb::observers::inferior_execd.attach (amd_dbgapi_inferior_execd, "amd-dbgapi"); + gdb::observers::inferior_execd.attach + (amd_dbgapi_inferior_execd, amd_dbgapi_target_inferior_execd_observer_token, + "amd-dbgapi"); gdb::observers::inferior_forked.attach (amd_dbgapi_inferior_forked, "amd-dbgapi"); gdb::observers::inferior_exit.attach (amd_dbgapi_inferior_exited, "amd-dbgapi"); gdb::observers::inferior_pre_detach.attach (amd_dbgapi_inferior_pre_detach, "amd-dbgapi"); diff --git a/gdb/amd-dbgapi-target.h b/gdb/amd-dbgapi-target.h index dd37ba3..fe3a50b 100644 --- a/gdb/amd-dbgapi-target.h +++ b/gdb/amd-dbgapi-target.h @@ -54,6 +54,11 @@ using is_amd_dbgapi_handle const gdb::observers::token & get_amd_dbgapi_target_inferior_created_observer_token (); +/* Get the token of amd-dbgapi's inferior_execd observer. */ + +const gdb::observers::token & + get_amd_dbgapi_target_inferior_execd_observer_token (); + /* Comparison operators for amd-dbgapi handle types. */ template <typename T, diff --git a/gdb/amd64-darwin-tdep.c b/gdb/amd64-darwin-tdep.c index dde023e..c687b1f 100644 --- a/gdb/amd64-darwin-tdep.c +++ b/gdb/amd64-darwin-tdep.c @@ -113,12 +113,10 @@ x86_darwin_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->jb_pc_offset = 56; - set_gdbarch_so_ops (gdbarch, &darwin_so_ops); + set_gdbarch_make_solib_ops (gdbarch, make_darwin_solib_ops); } -void _initialize_amd64_darwin_tdep (); -void -_initialize_amd64_darwin_tdep () +INIT_GDB_FILE (amd64_darwin_tdep) { gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_DARWIN, x86_darwin_init_abi_64); diff --git a/gdb/amd64-dicos-tdep.c b/gdb/amd64-dicos-tdep.c index 3e9b8c7..d7c211b 100644 --- a/gdb/amd64-dicos-tdep.c +++ b/gdb/amd64-dicos-tdep.c @@ -45,9 +45,7 @@ amd64_dicos_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_amd64_dicos_tdep (); -void -_initialize_amd64_dicos_tdep () +INIT_GDB_FILE (amd64_dicos_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour, amd64_dicos_osabi_sniffer); diff --git a/gdb/amd64-fbsd-nat.c b/gdb/amd64-fbsd-nat.c index a783a5d1..89bb049 100644 --- a/gdb/amd64-fbsd-nat.c +++ b/gdb/amd64-fbsd-nat.c @@ -324,9 +324,7 @@ amd64_fbsd_nat_target::read_description () return i386_target_description (X86_XSTATE_SSE_MASK, true); } -void _initialize_amd64fbsd_nat (); -void -_initialize_amd64fbsd_nat () +INIT_GDB_FILE (amd64fbsd_nat) { add_inf_child_target (&the_amd64_fbsd_nat_target); diff --git a/gdb/amd64-fbsd-tdep.c b/gdb/amd64-fbsd-tdep.c index eea0105..12f1e22 100644 --- a/gdb/amd64-fbsd-tdep.c +++ b/gdb/amd64-fbsd-tdep.c @@ -328,8 +328,7 @@ amd64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) amd64fbsd_core_read_description); /* FreeBSD uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); @@ -337,9 +336,7 @@ amd64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) amd64fbsd_get_thread_local_address); } -void _initialize_amd64fbsd_tdep (); -void -_initialize_amd64fbsd_tdep () +INIT_GDB_FILE (amd64fbsd_tdep) { gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_FREEBSD, amd64fbsd_init_abi); diff --git a/gdb/amd64-gnu-tdep.c b/gdb/amd64-gnu-tdep.c index 602fa8e..2b7337b 100644 --- a/gdb/amd64-gnu-tdep.c +++ b/gdb/amd64-gnu-tdep.c @@ -218,13 +218,10 @@ amd64_gnu_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->sc_num_regs = ARRAY_SIZE (amd64_gnu_sc_reg_offset); /* Hurd uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); } -void _initialize_amd64_gnu_tdep (); -void -_initialize_amd64_gnu_tdep () +INIT_GDB_FILE (amd64_gnu_tdep) { gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_HURD, amd64_gnu_init_abi); diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index 75e63c6..dbb9b32 100644 --- a/gdb/amd64-linux-nat.c +++ b/gdb/amd64-linux-nat.c @@ -424,9 +424,7 @@ amd64_linux_nat_target::low_siginfo_fixup (siginfo_t *ptrace, return false; } -void _initialize_amd64_linux_nat (); -void -_initialize_amd64_linux_nat () +INIT_GDB_FILE (amd64_linux_nat) { amd64_native_gregset32_reg_offset = amd64_linux_gregset32_reg_offset; amd64_native_gregset32_num_regs = I386_LINUX_NUM_REGS; diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index 494e744..13e9c0e 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -33,7 +33,10 @@ #include "amd64-linux-tdep.h" #include "i386-linux-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" +#include "svr4-tls-tdep.h" #include "gdbsupport/x86-xstate.h" +#include "inferior.h" #include "amd64-tdep.h" #include "solib-svr4.h" @@ -1832,6 +1835,39 @@ amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch, return (addr & amd64_linux_lam_untag_mask ()); } +/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID. + Throw a suitable TLS error if something goes wrong. */ + +static CORE_ADDR +amd64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid, + enum svr4_tls_libc libc) +{ + /* On x86-64, the thread pointer is found in the fsbase register. */ + regcache *regcache + = get_thread_arch_regcache (current_inferior (), ptid, gdbarch); + target_fetch_registers (regcache, AMD64_FSBASE_REGNUM); + ULONGEST fsbase; + if (regcache->cooked_read (AMD64_FSBASE_REGNUM, &fsbase) != REG_VALID) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer")); + + /* The thread pointer (fsbase) points at the TCB (thread control + block). The first two members of this struct are both pointers, + where the first will be a pointer to the TCB (i.e. it points at + itself) and the second will be a pointer to the DTV (dynamic + thread vector). There are many other fields too, but the one + we care about here is the DTV pointer. Compute the address + of the DTV pointer, fetch it, and convert it to an address. */ + CORE_ADDR dtv_ptr_addr = fsbase + gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT; + gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address")); + + const struct builtin_type *builtin = builtin_type (gdbarch); + CORE_ADDR dtv_addr = gdbarch_pointer_to_address + (gdbarch, builtin->builtin_data_ptr, buf.data ()); + return dtv_addr; +} + static void amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch, int num_disp_step_buffers) @@ -1862,6 +1898,9 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch, /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); + set_gdbarch_get_thread_local_address (gdbarch, + svr4_tls_get_thread_local_address); + svr4_tls_register_tls_methods (info, gdbarch, amd64_linux_get_tls_dtv_addr); /* GNU/Linux uses SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); @@ -2092,8 +2131,7 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->i386_syscall_record = amd64_linux_syscall_record; /* GNU/Linux uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops); /* Register DTrace handlers. */ set_gdbarch_dtrace_parse_probe_argument (gdbarch, amd64_dtrace_parse_probe_argument); @@ -2306,13 +2344,10 @@ amd64_x32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->i386_syscall_record = amd64_x32_linux_syscall_record; /* GNU/Linux uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); } -void _initialize_amd64_linux_tdep (); -void -_initialize_amd64_linux_tdep () +INIT_GDB_FILE (amd64_linux_tdep) { gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_LINUX, amd64_linux_init_abi); diff --git a/gdb/amd64-netbsd-nat.c b/gdb/amd64-netbsd-nat.c index 92ad4b2..c960b3c 100644 --- a/gdb/amd64-netbsd-nat.c +++ b/gdb/amd64-netbsd-nat.c @@ -55,9 +55,7 @@ static int amd64nbsd32_r_reg_offset[] = static amd64_bsd_nat_target<nbsd_nat_target> the_amd64_nbsd_nat_target; -void _initialize_amd64nbsd_nat (); -void -_initialize_amd64nbsd_nat () +INIT_GDB_FILE (amd64nbsd_nat) { amd64_native_gregset32_reg_offset = amd64nbsd32_r_reg_offset; amd64_native_gregset32_num_regs = ARRAY_SIZE (amd64nbsd32_r_reg_offset); diff --git a/gdb/amd64-netbsd-tdep.c b/gdb/amd64-netbsd-tdep.c index f4464b7..3dbbdd9 100644 --- a/gdb/amd64-netbsd-tdep.c +++ b/gdb/amd64-netbsd-tdep.c @@ -116,13 +116,10 @@ amd64nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->sc_num_regs = ARRAY_SIZE (amd64nbsd_r_reg_offset); /* NetBSD uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); } -void _initialize_amd64nbsd_tdep (); -void -_initialize_amd64nbsd_tdep () +INIT_GDB_FILE (amd64nbsd_tdep) { /* The NetBSD/amd64 native dependent code makes this assumption. */ gdb_assert (ARRAY_SIZE (amd64nbsd_r_reg_offset) == AMD64_NUM_GREGS); diff --git a/gdb/amd64-obsd-nat.c b/gdb/amd64-obsd-nat.c index 93d38df..8471520 100644 --- a/gdb/amd64-obsd-nat.c +++ b/gdb/amd64-obsd-nat.c @@ -127,9 +127,7 @@ amd64obsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) static amd64_bsd_nat_target<obsd_nat_target> the_amd64_obsd_nat_target; -void _initialize_amd64obsd_nat (); -void -_initialize_amd64obsd_nat () +INIT_GDB_FILE (amd64obsd_nat) { amd64_native_gregset32_reg_offset = amd64obsd32_r_reg_offset; amd64_native_gregset32_num_regs = ARRAY_SIZE (amd64obsd32_r_reg_offset); diff --git a/gdb/amd64-obsd-tdep.c b/gdb/amd64-obsd-tdep.c index 5acc380..6b60e0a 100644 --- a/gdb/amd64-obsd-tdep.c +++ b/gdb/amd64-obsd-tdep.c @@ -443,16 +443,13 @@ amd64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) bsd_uthread_set_collect_uthread (gdbarch, amd64obsd_collect_uthread); /* OpenBSD uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); /* Unwind kernel trap frames correctly. */ frame_unwind_prepend_unwinder (gdbarch, &amd64obsd_trapframe_unwind); } -void _initialize_amd64obsd_tdep (); -void -_initialize_amd64obsd_tdep () +INIT_GDB_FILE (amd64obsd_tdep) { /* The OpenBSD/amd64 native dependent code makes this assumption. */ gdb_assert (ARRAY_SIZE (amd64obsd_r_reg_offset) == AMD64_NUM_GREGS); diff --git a/gdb/amd64-sol2-tdep.c b/gdb/amd64-sol2-tdep.c index 84d5f87..da551a1 100644 --- a/gdb/amd64-sol2-tdep.c +++ b/gdb/amd64-sol2-tdep.c @@ -96,13 +96,10 @@ amd64_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->sc_num_regs = tdep->gregset_num_regs; /* Solaris uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); } -void _initialize_amd64_sol2_tdep (); -void -_initialize_amd64_sol2_tdep () +INIT_GDB_FILE (amd64_sol2_tdep) { gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_SOLARIS, amd64_sol2_init_abi); diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index e495778..82dd1e0 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -2129,6 +2129,30 @@ amd64_alloc_frame_cache (void) return cache; } +/* Check whether PC is at "endbr64" instruction. If so, return PC past it. + Otherwise, return PC passed to this function. */ + +static CORE_ADDR +amd64_skip_endbr (gdbarch *gdbarch, CORE_ADDR pc) +{ + static const gdb_byte endbr64[4] = { 0xf3, 0x0f, 0x1e, 0xfa }; + + bfd_endian byte_order = gdbarch_byte_order (gdbarch); + gdb_byte buf[3]; + gdb_byte op = read_code_unsigned_integer (pc, 1, byte_order); + + /* Check for the `endbr64` instruction, skip it if found. */ + if (op == endbr64[0]) + { + read_code (pc + 1, buf, 3); + + if (memcmp (buf, &endbr64[1], 3) == 0) + return pc + 4; + } + + return pc; +} + /* GCC 4.4 and later, can put code in the prologue to realign the stack pointer. Check whether PC points to such code, and update CACHE accordingly. Return the first instruction after the code @@ -2466,35 +2490,18 @@ amd64_x32_analyze_stack_align (CORE_ADDR pc, CORE_ADDR current_pc, return std::min (pc + offset + 2, current_pc); } -/* Do a limited analysis of the prologue at PC and update CACHE - accordingly. Bail out early if CURRENT_PC is reached. Return the - address where the analysis stopped. - - We will handle only functions beginning with: - - pushq %rbp 0x55 - movq %rsp, %rbp 0x48 0x89 0xe5 (or 0x48 0x8b 0xec) - - or (for the X32 ABI): - - pushq %rbp 0x55 - movl %esp, %ebp 0x89 0xe5 (or 0x8b 0xec) - - The `endbr64` instruction can be found before these sequences, and will be - skipped if found. +/* Analyze frame setup instructions at PC on behalf of amd64_analyze_prologue + and update CACHE accordingly. Bail out early if CURRENT_PC is reached. + Return the address where the analysis stopped. - Any function that doesn't start with one of these sequences will be - assumed to have no prologue and thus no valid frame pointer in - %rbp. */ + See comment on amd64_analyze_prologue for the sequences handled. The + movq/movl after the push of %rbp is considered optional. 'endbr64' is + handled before this function. */ static CORE_ADDR -amd64_analyze_prologue (struct gdbarch *gdbarch, - CORE_ADDR pc, CORE_ADDR current_pc, - struct amd64_frame_cache *cache) +amd64_analyze_frame_setup (gdbarch *gdbarch, CORE_ADDR pc, + CORE_ADDR current_pc, amd64_frame_cache *cache) { - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - /* The `endbr64` instruction. */ - static const gdb_byte endbr64[4] = { 0xf3, 0x0f, 0x1e, 0xfa }; /* There are two variations of movq %rsp, %rbp. */ static const gdb_byte mov_rsp_rbp_1[3] = { 0x48, 0x89, 0xe5 }; static const gdb_byte mov_rsp_rbp_2[3] = { 0x48, 0x8b, 0xec }; @@ -2502,34 +2509,11 @@ amd64_analyze_prologue (struct gdbarch *gdbarch, static const gdb_byte mov_esp_ebp_1[2] = { 0x89, 0xe5 }; static const gdb_byte mov_esp_ebp_2[2] = { 0x8b, 0xec }; + bfd_endian byte_order = gdbarch_byte_order (gdbarch); gdb_byte buf[3]; - gdb_byte op; - - if (current_pc <= pc) - return current_pc; - - if (gdbarch_ptr_bit (gdbarch) == 32) - pc = amd64_x32_analyze_stack_align (pc, current_pc, cache); - else - pc = amd64_analyze_stack_align (pc, current_pc, cache); - - op = read_code_unsigned_integer (pc, 1, byte_order); - - /* Check for the `endbr64` instruction, skip it if found. */ - if (op == endbr64[0]) - { - read_code (pc + 1, buf, 3); - - if (memcmp (buf, &endbr64[1], 3) == 0) - pc += 4; - - op = read_code_unsigned_integer (pc, 1, byte_order); - } - - if (current_pc <= pc) - return current_pc; + gdb_byte op = read_code_unsigned_integer (pc, 1, byte_order); - if (op == 0x55) /* pushq %rbp */ + if (op == 0x55) /* pushq %rbp. */ { /* Take into account that we've executed the `pushq %rbp' that starts this instruction sequence. */ @@ -2569,6 +2553,50 @@ amd64_analyze_prologue (struct gdbarch *gdbarch, return pc; } +/* Do a limited analysis of the prologue at PC and update CACHE + accordingly. Bail out early if CURRENT_PC is reached. Return the + address where the analysis stopped. + + We will handle only functions beginning with: + + pushq %rbp 0x55 + movq %rsp, %rbp 0x48 0x89 0xe5 (or 0x48 0x8b 0xec) + + or (for the X32 ABI): + + pushq %rbp 0x55 + movl %esp, %ebp 0x89 0xe5 (or 0x8b 0xec) + + The `endbr64` instruction can be found before these sequences, and will be + skipped if found. + + Any function that doesn't start with one of these sequences will be + assumed to have no prologue and thus no valid frame pointer in + %rbp. */ + +static CORE_ADDR +amd64_analyze_prologue (gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR current_pc, + amd64_frame_cache *cache) +{ + if (current_pc <= pc) + return current_pc; + + /* If generated, 'endbr64' will be placed before stack alignment too. */ + pc = amd64_skip_endbr (gdbarch, pc); + if (current_pc <= pc) + return current_pc; + + if (gdbarch_ptr_bit (gdbarch) == 32) + pc = amd64_x32_analyze_stack_align (pc, current_pc, cache); + else + pc = amd64_analyze_stack_align (pc, current_pc, cache); + + if (current_pc <= pc) + return current_pc; + + return amd64_analyze_frame_setup (gdbarch, pc, current_pc, cache); +} + /* Work around false termination of prologue - GCC PR debug/48827. START_PC is the first instruction of a function, PC is its minimal already @@ -3740,9 +3768,7 @@ amd64_insn_decode (void) } // namespace selftests #endif /* GDB_SELF_TEST */ -void _initialize_amd64_tdep (); -void -_initialize_amd64_tdep () +INIT_GDB_FILE (amd64_tdep) { gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_NONE, amd64_none_init_abi); diff --git a/gdb/amd64-windows-nat.c b/gdb/amd64-windows-nat.c index 223e078..f5c98f6 100644 --- a/gdb/amd64-windows-nat.c +++ b/gdb/amd64-windows-nat.c @@ -94,9 +94,7 @@ amd64_windows_segment_register_p (int regnum) return regnum >= AMD64_CS_REGNUM && regnum <= AMD64_GS_REGNUM; } -void _initialize_amd64_windows_nat (); -void -_initialize_amd64_windows_nat () +INIT_GDB_FILE (amd64_windows_nat) { x86_set_debug_register_length (8); } diff --git a/gdb/amd64-windows-tdep.c b/gdb/amd64-windows-tdep.c index c4719dc..c7977d2 100644 --- a/gdb/amd64-windows-tdep.c +++ b/gdb/amd64-windows-tdep.c @@ -1404,9 +1404,7 @@ amd64_cygwin_core_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_amd64_windows_tdep (); -void -_initialize_amd64_windows_tdep () +INIT_GDB_FILE (amd64_windows_tdep) { gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, GDB_OSABI_WINDOWS, amd64_windows_init_abi); diff --git a/gdb/amdgpu-tdep.c b/gdb/amdgpu-tdep.c index dc1b32f..26e5a27 100644 --- a/gdb/amdgpu-tdep.c +++ b/gdb/amdgpu-tdep.c @@ -1371,10 +1371,7 @@ amdgpu_register_type_parse_test () #endif -void _initialize_amdgpu_tdep (); - -void -_initialize_amdgpu_tdep () +INIT_GDB_FILE (amdgpu_tdep) { gdbarch_register (bfd_arch_amdgcn, amdgpu_gdbarch_init, NULL, amdgpu_supports_arch_info); diff --git a/gdb/annotate.c b/gdb/annotate.c index ea0bfa9..55db2e2 100644 --- a/gdb/annotate.c +++ b/gdb/annotate.c @@ -627,9 +627,7 @@ breakpoint_changed (struct breakpoint *b) annotate_breakpoints_invalid (); } -void _initialize_annotate (); -void -_initialize_annotate () +INIT_GDB_FILE (annotate) { gdb::observers::breakpoint_created.attach (breakpoint_changed, "annotate"); gdb::observers::breakpoint_deleted.attach (breakpoint_changed, "annotate"); diff --git a/gdb/arc-linux-nat.c b/gdb/arc-linux-nat.c index 091a966..75fc4b7 100644 --- a/gdb/arc-linux-nat.c +++ b/gdb/arc-linux-nat.c @@ -309,9 +309,7 @@ ps_get_thread_area (struct ps_prochandle *ph, lwpid_t lwpid, int idx, } /* Suppress warning from -Wmissing-prototypes. */ -void _initialize_arc_linux_nat (); -void -_initialize_arc_linux_nat () +INIT_GDB_FILE (arc_linux_nat) { /* Register the target. */ linux_target = &the_arc_linux_nat_target; diff --git a/gdb/arc-linux-tdep.c b/gdb/arc-linux-tdep.c index adf6691..edbb3f8 100644 --- a/gdb/arc-linux-tdep.c +++ b/gdb/arc-linux-tdep.c @@ -19,6 +19,7 @@ /* GDB header files. */ #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "objfiles.h" #include "opcode/arc.h" #include "osabi.h" @@ -736,15 +737,10 @@ arc_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch) /* GNU/Linux uses SVR4-style shared libraries, with 32-bit ints, longs and pointers (ILP32). */ - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); } -/* Suppress warning from -Wmissing-prototypes. */ -extern initialize_file_ftype _initialize_arc_linux_tdep; - -void -_initialize_arc_linux_tdep () +INIT_GDB_FILE (arc_linux_tdep) { gdbarch_register_osabi (bfd_arch_arc, 0, GDB_OSABI_LINUX, arc_linux_init_osabi); diff --git a/gdb/arc-newlib-tdep.c b/gdb/arc-newlib-tdep.c index fd80bfa..9a6f7a8 100644 --- a/gdb/arc-newlib-tdep.c +++ b/gdb/arc-newlib-tdep.c @@ -58,9 +58,7 @@ arc_newlib_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_arc_newlib_tdep (); -void -_initialize_arc_newlib_tdep () +INIT_GDB_FILE (arc_newlib_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_arc, bfd_target_elf_flavour, arc_newlib_osabi_sniffer); diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c index 8db6e7b..f5ae11c 100644 --- a/gdb/arc-tdep.c +++ b/gdb/arc-tdep.c @@ -2458,9 +2458,7 @@ dump_arc_instruction_command (const char *args, int from_tty) arc_insn_dump (insn); } -void _initialize_arc_tdep (); -void -_initialize_arc_tdep () +INIT_GDB_FILE (arc_tdep) { gdbarch_register (bfd_arch_arc, arc_gdbarch_init, arc_dump_tdep); diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index be0494f..f320d3d 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -1525,9 +1525,7 @@ core_file_exec_context::environment () const return e; } -void _initialize_gdbarch_utils (); -void -_initialize_gdbarch_utils () +INIT_GDB_FILE (gdbarch_utils) { add_setshow_enum_cmd ("endian", class_support, endian_enum, &set_endian_string, diff --git a/gdb/arm-fbsd-nat.c b/gdb/arm-fbsd-nat.c index 2b7b75a..e068023 100644 --- a/gdb/arm-fbsd-nat.c +++ b/gdb/arm-fbsd-nat.c @@ -104,9 +104,7 @@ arm_fbsd_nat_target::read_description () return desc; } -void _initialize_arm_fbsd_nat (); -void -_initialize_arm_fbsd_nat () +INIT_GDB_FILE (arm_fbsd_nat) { add_inf_child_target (&the_arm_fbsd_nat_target); } diff --git a/gdb/arm-fbsd-tdep.c b/gdb/arm-fbsd-tdep.c index c9a466f..a37d8b7 100644 --- a/gdb/arm-fbsd-tdep.c +++ b/gdb/arm-fbsd-tdep.c @@ -300,8 +300,7 @@ arm_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tramp_frame_prepend_unwinder (gdbarch, &arm_fbsd_sigframe); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); tdep->jb_pc = 24; tdep->jb_elt_size = 4; @@ -322,9 +321,7 @@ arm_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_software_single_step (gdbarch, arm_software_single_step); } -void _initialize_arm_fbsd_tdep (); -void -_initialize_arm_fbsd_tdep () +INIT_GDB_FILE (arm_fbsd_tdep) { gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_FREEBSD, arm_fbsd_init_abi); diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c index 3ff9fc6..813da8c 100644 --- a/gdb/arm-linux-nat.c +++ b/gdb/arm-linux-nat.c @@ -1393,9 +1393,7 @@ arm_linux_nat_target::low_new_fork (struct lwp_info *parent, pid_t child_pid) *child_state = *parent_state; } -void _initialize_arm_linux_nat (); -void -_initialize_arm_linux_nat () +INIT_GDB_FILE (arm_linux_nat) { /* Register the target. */ linux_target = &the_arm_linux_nat_target; diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index 485a5d9..08526d8 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -41,6 +41,7 @@ #include "arm-tdep.h" #include "arm-linux-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "glibc-tdep.h" #include "arch-utils.h" #include "inferior.h" @@ -1801,8 +1802,7 @@ arm_linux_init_abi (struct gdbarch_info info, } tdep->jb_elt_size = ARM_LINUX_JB_ELEMENT_SIZE; - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); /* Single stepping. */ set_gdbarch_software_single_step (gdbarch, arm_linux_software_single_step); @@ -2026,9 +2026,7 @@ arm_linux_init_abi (struct gdbarch_info info, set_gdbarch_gcc_target_options (gdbarch, arm_linux_gcc_target_options); } -void _initialize_arm_linux_tdep (); -void -_initialize_arm_linux_tdep () +INIT_GDB_FILE (arm_linux_tdep) { gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_LINUX, arm_linux_init_abi); diff --git a/gdb/arm-netbsd-nat.c b/gdb/arm-netbsd-nat.c index 3a24f51..ebf4084 100644 --- a/gdb/arm-netbsd-nat.c +++ b/gdb/arm-netbsd-nat.c @@ -354,9 +354,7 @@ arm_netbsd_nat_target::read_description () return arm_read_description (ARM_FP_TYPE_VFPV3, false); } -void _initialize_arm_netbsd_nat (); -void -_initialize_arm_netbsd_nat () +INIT_GDB_FILE (arm_netbsd_nat) { add_inf_child_target (&the_arm_netbsd_nat_target); } diff --git a/gdb/arm-netbsd-tdep.c b/gdb/arm-netbsd-tdep.c index a162054..82ba0c6 100644 --- a/gdb/arm-netbsd-tdep.c +++ b/gdb/arm-netbsd-tdep.c @@ -156,13 +156,10 @@ arm_netbsd_elf_init_abi (struct gdbarch_info info, tdep->fp_model = ARM_FLOAT_SOFT_VFP; /* NetBSD ELF uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); } -void _initialize_arm_netbsd_tdep (); -void -_initialize_arm_netbsd_tdep () +INIT_GDB_FILE (arm_netbsd_tdep) { gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_NETBSD, arm_netbsd_elf_init_abi); diff --git a/gdb/arm-none-tdep.c b/gdb/arm-none-tdep.c index 81db22f..3ffcd2b 100644 --- a/gdb/arm-none-tdep.c +++ b/gdb/arm-none-tdep.c @@ -204,9 +204,7 @@ arm_none_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Initialize ARM bare-metal target support. */ -void _initialize_arm_none_tdep (); -void -_initialize_arm_none_tdep () +INIT_GDB_FILE (arm_none_tdep) { gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_NONE, arm_none_init_abi); diff --git a/gdb/arm-obsd-tdep.c b/gdb/arm-obsd-tdep.c index 6fd4c85..00e4bfd 100644 --- a/gdb/arm-obsd-tdep.c +++ b/gdb/arm-obsd-tdep.c @@ -83,8 +83,7 @@ armobsd_init_abi (struct gdbarch_info info, tramp_frame_prepend_unwinder (gdbarch, &armobsd_sigframe); /* OpenBSD/arm uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver); tdep->jb_pc = 24; @@ -114,9 +113,7 @@ armobsd_init_abi (struct gdbarch_info info, } } -void _initialize_armobsd_tdep (); -void -_initialize_armobsd_tdep () +INIT_GDB_FILE (armobsd_tdep) { gdbarch_register_osabi (bfd_arch_arm, 0, GDB_OSABI_OPENBSD, armobsd_init_abi); diff --git a/gdb/arm-pikeos-tdep.c b/gdb/arm-pikeos-tdep.c index 428bb7d..12fd9c3 100644 --- a/gdb/arm-pikeos-tdep.c +++ b/gdb/arm-pikeos-tdep.c @@ -73,9 +73,7 @@ arm_pikeos_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_arm_pikeos_tdep (); -void -_initialize_arm_pikeos_tdep () +INIT_GDB_FILE (arm_pikeos_tdep) { /* Register the sniffer for the PikeOS targets. */ gdbarch_register_osabi_sniffer (bfd_arch_arm, bfd_target_elf_flavour, diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 5450056..a764825 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -1669,7 +1669,7 @@ arm_analyze_load_stack_chk_guard(CORE_ADDR pc, struct gdbarch *gdbarch, ldr Rn, .Label .... - .Lable: + .Label: .word __stack_chk_guard Since ldr/str is a very popular instruction, we can't use them as @@ -11015,9 +11015,7 @@ static void arm_analyze_prologue_test (); } #endif -void _initialize_arm_tdep (); -void -_initialize_arm_tdep () +INIT_GDB_FILE (arm_tdep) { long length; int i, j; diff --git a/gdb/arm-wince-tdep.c b/gdb/arm-wince-tdep.c index b1e1304..b9ea59c 100644 --- a/gdb/arm-wince-tdep.c +++ b/gdb/arm-wince-tdep.c @@ -152,9 +152,7 @@ arm_wince_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_arm_wince_tdep (); -void -_initialize_arm_wince_tdep () +INIT_GDB_FILE (arm_wince_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_arm, bfd_target_coff_flavour, arm_wince_osabi_sniffer); diff --git a/gdb/auto-load.c b/gdb/auto-load.c index 4fc5b08..114a7d5 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; } @@ -1507,9 +1568,7 @@ found and/or loaded."), gdb::observers::token auto_load_new_objfile_observer_token; -void _initialize_auto_load (); -void -_initialize_auto_load () +INIT_GDB_FILE (auto_load) { struct cmd_list_element *cmd; gdb::unique_xmalloc_ptr<char> scripts_directory_help, gdb_name_help, @@ -1650,4 +1709,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 } @@ -609,9 +609,7 @@ info_auxv_command (const char *cmd, int from_tty) } } -void _initialize_auxv (); -void -_initialize_auxv () +INIT_GDB_FILE (auxv) { add_info ("auxv", info_auxv_command, _("Display the inferior's auxiliary vector.\n\ diff --git a/gdb/avr-tdep.c b/gdb/avr-tdep.c index 36e6ec4..6274529 100644 --- a/gdb/avr-tdep.c +++ b/gdb/avr-tdep.c @@ -1629,9 +1629,7 @@ avr_io_reg_read_command (const char *args, int from_tty) } } -void _initialize_avr_tdep (); -void -_initialize_avr_tdep () +INIT_GDB_FILE (avr_tdep) { gdbarch_register (bfd_arch_avr, avr_gdbarch_init); diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c index e572465..08f542c 100644 --- a/gdb/ax-gdb.c +++ b/gdb/ax-gdb.c @@ -2634,9 +2634,7 @@ maint_agent_printf_command (const char *cmdrest, int from_tty) /* Initialization code. */ -void _initialize_ax_gdb (); -void -_initialize_ax_gdb () +INIT_GDB_FILE (ax_gdb) { add_cmd ("agent", class_maintenance, maint_agent_command, _("\ diff --git a/gdb/bfin-linux-tdep.c b/gdb/bfin-linux-tdep.c index 3ba3146..d84f800 100644 --- a/gdb/bfin-linux-tdep.c +++ b/gdb/bfin-linux-tdep.c @@ -161,9 +161,7 @@ bfin_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) bfin_linux_get_syscall_number); } -void _initialize_bfin_linux_tdep (); -void -_initialize_bfin_linux_tdep () +INIT_GDB_FILE (bfin_linux_tdep) { gdbarch_register_osabi (bfd_arch_bfin, 0, GDB_OSABI_LINUX, bfin_linux_init_abi); diff --git a/gdb/bfin-tdep.c b/gdb/bfin-tdep.c index 32d9371..3a96e9f 100644 --- a/gdb/bfin-tdep.c +++ b/gdb/bfin-tdep.c @@ -837,9 +837,7 @@ bfin_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_bfin_tdep (); -void -_initialize_bfin_tdep () +INIT_GDB_FILE (bfin_tdep) { gdbarch_register (bfd_arch_bfin, bfin_gdbarch_init); } diff --git a/gdb/block.c b/gdb/block.c index fdf209c..1866c0f 100644 --- a/gdb/block.c +++ b/gdb/block.c @@ -924,9 +924,7 @@ maintenance_info_blocks (const char *arg, int from_tty) -void _initialize_block (); -void -_initialize_block () +INIT_GDB_FILE (block) { add_cmd ("blocks", class_maintenance, maintenance_info_blocks, _("\ diff --git a/gdb/bpf-tdep.c b/gdb/bpf-tdep.c index 2c95607..be01e8b 100644 --- a/gdb/bpf-tdep.c +++ b/gdb/bpf-tdep.c @@ -368,9 +368,7 @@ bpf_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_bpf_tdep (); -void -_initialize_bpf_tdep () +INIT_GDB_FILE (bpf_tdep) { gdbarch_register (bfd_arch_bpf, bpf_gdbarch_init); diff --git a/gdb/break-catch-exec.c b/gdb/break-catch-exec.c index 6716437..570018c 100644 --- a/gdb/break-catch-exec.c +++ b/gdb/break-catch-exec.c @@ -213,9 +213,7 @@ catch_exec_command_1 (const char *arg, int from_tty, install_breakpoint (0, std::move (c), 1); } -void _initialize_break_catch_exec (); -void -_initialize_break_catch_exec () +INIT_GDB_FILE (break_catch_exec) { add_catch_command ("exec", _("Catch calls to exec."), catch_exec_command_1, diff --git a/gdb/break-catch-fork.c b/gdb/break-catch-fork.c index cba3753..c8a6330 100644 --- a/gdb/break-catch-fork.c +++ b/gdb/break-catch-fork.c @@ -242,9 +242,7 @@ catch_fork_command_1 (const char *arg, int from_tty, } } -void _initialize_break_catch_fork (); -void -_initialize_break_catch_fork () +INIT_GDB_FILE (break_catch_fork) { add_catch_command ("fork", _("Catch calls to fork."), catch_fork_command_1, diff --git a/gdb/break-catch-load.c b/gdb/break-catch-load.c index 5e290fd..594884e 100644 --- a/gdb/break-catch-load.c +++ b/gdb/break-catch-load.c @@ -24,7 +24,7 @@ #include "cli/cli-decode.h" #include "mi/mi-common.h" #include "progspace.h" -#include "solist.h" +#include "solib.h" #include "target.h" #include "valprint.h" @@ -119,7 +119,7 @@ solib_catchpoint::check_status (struct bpstat *bs) for (solib *iter : current_program_space->added_solibs) { if (!regex - || compiled->exec (iter->so_name.c_str (), 0, nullptr, 0) == 0) + || compiled->exec (iter->name.c_str (), 0, nullptr, 0) == 0) return; } } @@ -264,9 +264,7 @@ catch_unload_command_1 (const char *arg, int from_tty, catch_load_or_unload (arg, from_tty, 0, command); } -void _initialize_break_catch_load (); -void -_initialize_break_catch_load () +INIT_GDB_FILE (break_catch_load) { add_catch_command ("load", _("Catch loads of shared libraries.\n\ Usage: catch load [REGEX]\n\ diff --git a/gdb/break-catch-sig.c b/gdb/break-catch-sig.c index fb5d202..adfc48f 100644 --- a/gdb/break-catch-sig.c +++ b/gdb/break-catch-sig.c @@ -409,9 +409,7 @@ catch_signal_command (const char *arg, int from_tty, create_signal_catchpoint (tempflag, std::move (filter), catch_all); } -void _initialize_break_catch_sig (); -void -_initialize_break_catch_sig () +INIT_GDB_FILE (break_catch_sig) { add_catch_command ("signal", _("\ Catch signals by their names and/or numbers.\n\ diff --git a/gdb/break-catch-syscall.c b/gdb/break-catch-syscall.c index 348ff41..63bfec6 100644 --- a/gdb/break-catch-syscall.c +++ b/gdb/break-catch-syscall.c @@ -423,7 +423,7 @@ catch_syscall_command_1 (const char *arg, int from_tty, struct syscall s; struct gdbarch *gdbarch = get_current_arch (); - /* Checking if the feature if supported. */ + /* Checking if the feature is supported. */ if (gdbarch_get_syscall_number_p (gdbarch) == 0) error (_("The feature 'catch syscall' is not supported on \ this architecture yet.")); @@ -570,9 +570,7 @@ clear_syscall_counts (struct inferior *inf) inf_data->syscalls_counts.clear (); } -void _initialize_break_catch_syscall (); -void -_initialize_break_catch_syscall () +INIT_GDB_FILE (break_catch_syscall) { gdb::observers::inferior_exit.attach (clear_syscall_counts, "break-catch-syscall"); diff --git a/gdb/break-catch-throw.c b/gdb/break-catch-throw.c index 41213e4..cb682a6 100644 --- a/gdb/break-catch-throw.c +++ b/gdb/break-catch-throw.c @@ -499,9 +499,7 @@ static const struct internalvar_funcs exception_funcs = -void _initialize_break_catch_throw (); -void -_initialize_break_catch_throw () +INIT_GDB_FILE (break_catch_throw) { /* Add catch and tcatch sub-commands. */ add_catch_command ("catch", _("\ diff --git a/gdb/break-cond-parse.c b/gdb/break-cond-parse.c index 705f4f6..04a8895 100644 --- a/gdb/break-cond-parse.c +++ b/gdb/break-cond-parse.c @@ -569,10 +569,10 @@ test (const char *input, const char *condition, int thread = -1, gdb::unique_xmalloc_ptr<char> extracted_rest; int extracted_thread, extracted_inferior, extracted_task; bool extracted_force_condition; - std::string exception_msg, error_str; + std::string exception_msg; - if (error_msg != nullptr) - error_str = std::string (error_msg) + "\n"; + if (error_msg == nullptr) + error_msg = ""; try { @@ -584,10 +584,7 @@ test (const char *input, const char *condition, int thread = -1, } catch (const gdb_exception_error &ex) { - string_file buf; - - exception_print (&buf, ex); - exception_msg = buf.release (); + exception_msg = ex.what (); } if ((condition == nullptr) != (extracted_condition.get () == nullptr) @@ -599,7 +596,7 @@ test (const char *input, const char *condition, int thread = -1, || inferior != extracted_inferior || task != extracted_task || force != extracted_force_condition - || exception_msg != error_str) + || exception_msg != error_msg) { if (run_verbose ()) { @@ -687,9 +684,7 @@ create_breakpoint_parse_arg_string_tests () } /* namespace selftests */ #endif /* GDB_SELF_TEST */ -void _initialize_break_cond_parse (); -void -_initialize_break_cond_parse () +INIT_GDB_FILE (break_cond_parse) { #if GDB_SELF_TEST selftests::register_test diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 23e5051..d704ad1 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -50,7 +50,6 @@ #include "cli/cli-script.h" #include "block.h" #include "solib.h" -#include "solist.h" #include "observable.h" #include "memattr.h" #include "ada-lang.h" @@ -255,14 +254,22 @@ DIAGNOSTIC_POP static std::string breakpoint_location_address_str (const bp_location *bl) { - std::string str = string_printf ("Breakpoint %d (%s) at address %s", + std::string str = string_printf ("Breakpoint %d (%s) ", bl->owner->number, - host_address_to_string (bl), - paddress (bl->gdbarch, bl->address)); + host_address_to_string (bl)); - std::string loc_string = bl->to_string (); - if (!loc_string.empty ()) - str += string_printf (" %s", loc_string.c_str ()); + if (bl_address_is_meaningful (bl)) + { + gdb_assert (bl->gdbarch != nullptr); + str += string_printf ("at address %s", + paddress (bl->gdbarch, bl->address)); + + std::string loc_string = bl->to_string (); + if (!loc_string.empty ()) + str += string_printf (" %s", loc_string.c_str ()); + } + else + str += "with dummy location"; return str; } @@ -721,7 +728,8 @@ all_tracepoints () tracepoint_iterator (breakpoint_chain.end ())); } -/* Array is sorted by bp_location_is_less_than - primarily by the ADDRESS. */ +/* Array is sorted by bp_location_ptr_is_less_than - primarily by the + ADDRESS. */ static std::vector<bp_location *> bp_locations; @@ -5136,7 +5144,7 @@ print_solib_event (bool is_catchpoint) if (!first) current_uiout->text (" "); first = false; - current_uiout->field_string ("library", iter->so_name); + current_uiout->field_string ("library", iter->name); current_uiout->text ("\n"); } } @@ -7504,7 +7512,7 @@ breakpoint_locations_match (const struct bp_location *loc1, else /* We compare bp_location.length in order to cover ranged breakpoints. Keep this in sync with - bp_location_is_less_than. */ + bp_location_ptr_is_less_than. */ return (breakpoint_address_match (loc1->pspace->aspace.get (), loc1->address, loc2->pspace->aspace.get (), @@ -8144,7 +8152,7 @@ disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib target_terminal::ours_for_output (); warning (_("Temporarily disabling breakpoints " "for unloaded shared library \"%s\""), - solib.so_name.c_str ()); + solib.name.c_str ()); disabled_shlib_breaks = true; } } @@ -10289,7 +10297,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); } @@ -11205,14 +11213,17 @@ breakpoint_auto_delete (bpstat *bs) delete_breakpoint (&b); } -/* A comparison function for bp_location AP and BP being interfaced to - std::sort. Sort elements primarily by their ADDRESS (no matter what - bl_address_is_meaningful says), secondarily by ordering first - permanent elements and tertiarily just ensuring the array is sorted - stable way despite std::sort being an unstable algorithm. */ +/* A comparison function for bp_location pointers A and B being interfaced to + std::sort, for instance to sort an std::vector<bp_location *>. Sort + elements: + - primarily by their ADDRESS (no matter what bl_address_is_meaningful + says), + - secondarily by ordering first permanent elements, and + - tertiarily just ensuring the array is sorted in a stable way despite + std::sort being an unstable algorithm. */ -static int -bp_location_is_less_than (const bp_location *a, const bp_location *b) +static bool +bp_location_ptr_is_less_than (const bp_location *a, const bp_location *b) { if (a->address != b->address) return a->address < b->address; @@ -11250,6 +11261,15 @@ bp_location_is_less_than (const bp_location *a, const bp_location *b) return a < b; } +/* A comparison function for bp_locations A and B being interfaced to + std::sort, for instance to sort an std::vector<bp_location>. */ + +static bool +bp_location_is_less_than (const bp_location &a, const bp_location &b) +{ + return bp_location_ptr_is_less_than (&a, &b); +} + /* Set bp_locations_placed_address_before_address_max and bp_locations_shadow_len_after_address_max according to the current content of the bp_locations array. */ @@ -11455,7 +11475,7 @@ update_global_location_list (enum ugll_insert_mode insert_mode) handle_automatic_hardware_breakpoints (loc); std::sort (bp_locations.begin (), bp_locations.end (), - bp_location_is_less_than); + bp_location_ptr_is_less_than); bp_locations_target_extensions_update (); @@ -11903,9 +11923,7 @@ breakpoint::add_location (bp_location &loc) auto ub = std::upper_bound (m_locations.begin (), m_locations.end (), loc, - [] (const bp_location &left, - const bp_location &right) - { return left.address < right.address; }); + bp_location_is_less_than); m_locations.insert (ub, loc); } @@ -14747,9 +14765,7 @@ static struct cmd_list_element *enablebreaklist = NULL; cmd_list_element *commands_cmd_element = nullptr; -void _initialize_breakpoint (); -void -_initialize_breakpoint () +INIT_GDB_FILE (breakpoint) { struct cmd_list_element *c; diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 6b421f2..9341112 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -425,7 +425,9 @@ public: simplicity is more important than memory usage for breakpoints. */ /* Architecture associated with this location's address. May be - different from the breakpoint architecture. */ + different from the breakpoint architecture. Not every location has + an address (e.g. see add_dummy_location), so not every location has + an associated gdbarch -- this can be NULL for a valid location. */ struct gdbarch *gdbarch = NULL; /* The program space associated with this breakpoint location diff --git a/gdb/bsd-uthread.c b/gdb/bsd-uthread.c index 51dafed..4509376 100644 --- a/gdb/bsd-uthread.c +++ b/gdb/bsd-uthread.c @@ -25,7 +25,6 @@ #include "observable.h" #include "regcache.h" #include "solib.h" -#include "solist.h" #include "symfile.h" #include "target.h" @@ -280,13 +279,13 @@ bsd_uthread_solib_loaded (solib &so) for (names = bsd_uthread_solib_names; *names; names++) { - if (startswith (so.so_original_name, *names)) + if (startswith (so.original_name, *names)) { solib_read_symbols (so, 0); if (bsd_uthread_activate (so.objfile)) { - bsd_uthread_solib_name = so.so_original_name; + bsd_uthread_solib_name = so.original_name; return; } } @@ -300,7 +299,7 @@ bsd_uthread_solib_unloaded (program_space *pspace, const solib &so, if (bsd_uthread_solib_name.empty () || still_in_use) return; - if (so.so_original_name == bsd_uthread_solib_name) + if (so.original_name == bsd_uthread_solib_name) bsd_uthread_deactivate (); } @@ -536,14 +535,12 @@ 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); } -void _initialize_bsd_uthread (); -void -_initialize_bsd_uthread () +INIT_GDB_FILE (bsd_uthread) { gdb::observers::inferior_created.attach (bsd_uthread_inferior_created, "bsd-uthread"); 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/btrace.c b/gdb/btrace.c index 3d883c5..b23de88 100644 --- a/gdb/btrace.c +++ b/gdb/btrace.c @@ -3501,9 +3501,7 @@ show_maint_btrace_pt_skip_pad (struct ui_file *file, int from_tty, /* Initialize btrace maintenance commands. */ -void _initialize_btrace (); -void -_initialize_btrace () +INIT_GDB_FILE (btrace) { add_cmd ("btrace", class_maintenance, maint_info_btrace_cmd, _("Info about branch tracing data."), &maintenanceinfolist); diff --git a/gdb/build-id.c b/gdb/build-id.c index 02602af..0ecd79f 100644 --- a/gdb/build-id.c +++ b/gdb/build-id.c @@ -29,6 +29,7 @@ #include "gdbsupport/scoped_fd.h" #include "debuginfod-support.h" #include "extension.h" +#include "inferior.h" /* See build-id.h. */ @@ -128,8 +129,9 @@ build_id_to_debug_bfd_1 (const std::string &original_link, if (supports_target_stat != TRIBOOL_FALSE) { struct stat sb; - int res = target_fileio_stat (nullptr, link_on_target, &sb, - &target_errno); + int res = target_fileio_lstat (current_inferior (), + link_on_target, &sb, + &target_errno); if (res != 0 && target_errno != FILEIO_ENOSYS) { @@ -157,7 +159,7 @@ build_id_to_debug_bfd_1 (const std::string &original_link, the path doesn't exist, but we just assume that anything other than EINVAL indicates the path doesn't exist. */ std::optional<std::string> link_target - = target_fileio_readlink (nullptr, link_on_target, + = target_fileio_readlink (current_inferior (), link_on_target, &target_errno); if (link_target.has_value () || target_errno == FILEIO_EINVAL) diff --git a/gdb/c-exp.y b/gdb/c-exp.y index f53a138..14d4b70 100644 --- a/gdb/c-exp.y +++ b/gdb/c-exp.y @@ -3042,10 +3042,6 @@ classify_name (struct parser_state *par_state, const struct block *block, std::string copy = copy_name (yylval.sval); - /* Initialize this in case we *don't* use it in this call; that way - we can refer to it unconditionally below. */ - memset (&is_a_field_of_this, 0, sizeof (is_a_field_of_this)); - bsym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, par_state->language ()->name_of_this () ? &is_a_field_of_this : NULL); @@ -3396,18 +3392,18 @@ c_parse (struct parser_state *par_state) c_parse_state cstate; scoped_restore cstate_restore = make_scoped_restore (&cpstate, &cstate); - gdb::unique_xmalloc_ptr<struct macro_scope> macro_scope; + macro_scope macro_scope; if (par_state->expression_context_block) macro_scope = sal_macro_scope (find_pc_line (par_state->expression_context_pc, 0)); else macro_scope = default_macro_scope (); - if (! macro_scope) + if (!macro_scope.is_valid ()) macro_scope = user_macro_scope (); scoped_restore restore_macro_scope - = make_scoped_restore (&expression_macro_scope, macro_scope.get ()); + = make_scoped_restore (&expression_macro_scope, ¯o_scope); scoped_restore restore_yydebug = make_scoped_restore (&yydebug, par_state->debug); diff --git a/gdb/charset.c b/gdb/charset.c index 3e37ec9..2593625 100644 --- a/gdb/charset.c +++ b/gdb/charset.c @@ -983,9 +983,7 @@ intermediate_encoding (void) #endif /* USE_INTERMEDIATE_ENCODING_FUNCTION */ -void _initialize_charset (); -void -_initialize_charset () +INIT_GDB_FILE (charset) { /* The first element is always "auto". */ charsets.charsets.push_back (xstrdup ("auto")); diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index 9a5021f..5e887f5 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -952,6 +952,21 @@ shell_command (const char *arg, int from_tty) shell_escape (arg, from_tty); } +/* Completion for the shell command. Currently, this just uses filename + completion, but we could, potentially, complete command names from $PATH + for the first word, which would make this even more shell like. */ + +static void +shell_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char * /* word */) +{ + tracker.set_use_custom_word_point (true); + const char *word + = advance_to_filename_maybe_quoted_complete_word_point (tracker, text); + filename_maybe_quoted_completer (ignore, tracker, text, word); +} + static void edit_command (const char *arg, int from_tty) { @@ -1178,8 +1193,32 @@ pipe_command_completer (struct cmd_list_element *ignore, return; } - /* We're past the delimiter. What follows is a shell command, which - we don't know how to complete. */ + /* We're past the delimiter now, or at least, DELIM points to the + delimiter string. Update TEXT to point to the start of whatever + appears after the delimiter. */ + text = skip_spaces (delim + strlen (delimiter)); + + /* We really are past the delimiter now, so offer completions. This is + like GDB's "shell" command, currently we only offer filename + completion, but in the future this could be improved by offering + completion of command names from $PATH. + + What we don't do here is offer completions for the empty string. It + is assumed that the first word after the delimiter is going to be a + command name from $PATH, not a filename, so if the user has typed + nothing (yet) and tries to complete, there's no point offering a list + of files from the current directory. + + Once the user has started to type something though, then we do start + offering filename completions. */ + if (*text == '\0') + return; + + tracker.set_use_custom_word_point (true); + tracker.advance_custom_word_point_by (text - org_text); + const char *word + = advance_to_filename_maybe_quoted_complete_word_point (tracker, text); + filename_maybe_quoted_completer (ignore, tracker, text, word); } /* Helper for the list_command function. Prints the lines around (and @@ -2578,9 +2617,7 @@ shell_internal_fn (struct gdbarch *gdbarch, return value::allocate_optimized_out (int_type); } -void _initialize_cli_cmds (); -void -_initialize_cli_cmds () +INIT_GDB_FILE (cli_cmds) { struct cmd_list_element *c; @@ -2803,7 +2840,7 @@ the previous command number shown."), = add_com ("shell", class_support, shell_command, _("\ Execute the rest of the line as a shell command.\n\ With no arguments, run an inferior shell.")); - set_cmd_completer (shell_cmd, deprecated_filename_completer); + set_cmd_completer_handle_brkchars (shell_cmd, shell_command_completer); add_com_alias ("!", shell_cmd, class_support, 0); diff --git a/gdb/cli/cli-dump.c b/gdb/cli/cli-dump.c index 3055734..afbbea6 100644 --- a/gdb/cli/cli-dump.c +++ b/gdb/cli/cli-dump.c @@ -564,9 +564,7 @@ restore_command (const char *args, int from_tty) } } -void _initialize_cli_dump (); -void -_initialize_cli_dump () +INIT_GDB_FILE (cli_dump) { struct cmd_list_element *c; diff --git a/gdb/cli/cli-interp.c b/gdb/cli/cli-interp.c index 32ba9d9..d7b73df 100644 --- a/gdb/cli/cli-interp.c +++ b/gdb/cli/cli-interp.c @@ -321,9 +321,7 @@ cli_interp_factory (const char *name) /* Standard gdb initialization hook. */ -void _initialize_cli_interp (); -void -_initialize_cli_interp () +INIT_GDB_FILE (cli_interp) { interp_factory_register (INTERP_CONSOLE, cli_interp_factory); } diff --git a/gdb/cli/cli-logging.c b/gdb/cli/cli-logging.c index d225533..d6eb6c2 100644 --- a/gdb/cli/cli-logging.c +++ b/gdb/cli/cli-logging.c @@ -202,9 +202,7 @@ show_logging_enabled (struct ui_file *file, int from_tty, gdb_printf (file, _("off: Logging is disabled.\n")); } -void _initialize_cli_logging (); -void -_initialize_cli_logging () +INIT_GDB_FILE (cli_logging) { static struct cmd_list_element *set_logging_cmdlist, *show_logging_cmdlist; diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c index bdbf850..3ea80a5 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -1751,9 +1751,7 @@ show_user_1 (struct cmd_list_element *c, const char *prefix, const char *name, } -void _initialize_cli_script (); -void -_initialize_cli_script () +INIT_GDB_FILE (cli_script) { struct cmd_list_element *c; diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c index 30c7afb..d6829f0 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,9 +415,86 @@ show_style_disassembler (struct ui_file *file, int from_tty, gdb_printf (file, _("Disassembler output styling is disabled.\n")); } -void _initialize_cli_style (); +/* 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 -_initialize_cli_style () +print_error_prefix (ui_file *file) +{ + if (emojis_ok ()) + gdb_puts (error_prefix.c_str (), file); +} + +INIT_GDB_FILE (cli_style) { add_setshow_prefix_cmd ("style", no_class, _("\ @@ -431,6 +513,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 +716,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/coff-pe-read.c b/gdb/coff-pe-read.c index 3a3611e..0061007 100644 --- a/gdb/coff-pe-read.c +++ b/gdb/coff-pe-read.c @@ -694,9 +694,7 @@ show_debug_coff_pe_read (struct ui_file *file, int from_tty, /* Adds "Set/show debug coff_pe_read" commands. */ -void _initialize_coff_pe_read (); -void -_initialize_coff_pe_read () +INIT_GDB_FILE (coff_pe_read) { add_setshow_zuinteger_cmd ("coff-pe-read", class_maintenance, &debug_coff_pe_read, diff --git a/gdb/coffread.c b/gdb/coffread.c index 8a87e9c..db18c43 100644 --- a/gdb/coffread.c +++ b/gdb/coffread.c @@ -2248,9 +2248,7 @@ static const struct sym_fns coff_sym_fns = NULL, /* sym_probe_fns */ }; -void _initialize_coffread (); -void -_initialize_coffread () +INIT_GDB_FILE (coffread) { add_symtab_fns (bfd_target_coff_flavour, &coff_sym_fns); diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c index 0d32350..ac8888f 100644 --- a/gdb/compile/compile-c-support.c +++ b/gdb/compile/compile-c-support.c @@ -185,18 +185,18 @@ static void write_macro_definitions (const struct block *block, CORE_ADDR pc, struct ui_file *file) { - gdb::unique_xmalloc_ptr<struct macro_scope> scope; + macro_scope scope; if (block != NULL) scope = sal_macro_scope (find_pc_line (pc, 0)); else scope = default_macro_scope (); - if (scope == NULL) + if (!scope.is_valid ()) scope = user_macro_scope (); - if (scope != NULL && scope->file != NULL && scope->file->table != NULL) + if (scope.is_valid () && scope.file->table != nullptr) { - macro_for_each_in_scope (scope->file, scope->line, + macro_for_each_in_scope (scope.file, scope.line, [&] (const char *name, const macro_definition *macro, macro_source_file *source, diff --git a/gdb/compile/compile-cplus-types.c b/gdb/compile/compile-cplus-types.c index 56ef033..cf70fe4 100644 --- a/gdb/compile/compile-cplus-types.c +++ b/gdb/compile/compile-cplus-types.c @@ -1395,9 +1395,7 @@ gcc_cp_plugin::pop_binding_level (const char *debug_name) return pop_binding_level (); } -void _initialize_compile_cplus_types (); -void -_initialize_compile_cplus_types () +INIT_GDB_FILE (compile_cplus_types) { add_setshow_boolean_cmd ("compile-cplus-types", no_class, &debug_compile_cplus_types, _("\ diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c index ab4cb0b..229e155 100644 --- a/gdb/compile/compile.c +++ b/gdb/compile/compile.c @@ -868,9 +868,7 @@ compile_command (const char *args, int from_tty) /* See compile.h. */ cmd_list_element *compile_cmd_element = nullptr; -void _initialize_compile (); -void -_initialize_compile () +INIT_GDB_FILE (compile) { compile_cmd_element = add_prefix_cmd ("compile", class_obscure, compile_command, diff --git a/gdb/complaints.c b/gdb/complaints.c index 2f58af2..3c7f9bc 100644 --- a/gdb/complaints.c +++ b/gdb/complaints.c @@ -188,9 +188,7 @@ test_complaints () } /* namespace selftests */ #endif /* GDB_SELF_TEST */ -void _initialize_complaints (); -void -_initialize_complaints () +INIT_GDB_FILE (complaints) { add_setshow_zinteger_cmd ("complaints", class_support, &stop_whining, _("\ diff --git a/gdb/completer.c b/gdb/completer.c index 658a0c6..0d68e76 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -3513,9 +3513,7 @@ skip_over_slash_fmt (completion_tracker &tracker, const char **args) return false; } -void _initialize_completer (); -void -_initialize_completer () +INIT_GDB_FILE (completer) { /* Setup some readline completion globals. */ rl_completion_word_break_hook = gdb_completion_word_break_characters; diff --git a/gdb/config.in b/gdb/config.in index 86ff67d..149aeaf 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -39,6 +39,9 @@ /* Define to BFD's default target vector. */ #undef DEFAULT_BFD_VEC +/* defined if dwarf format was requested. */ +#undef DWARF_FORMAT_AVAILABLE + /* Handle .ctf type-info sections */ #undef ENABLE_LIBCTF @@ -93,6 +96,9 @@ /* Define if amd-dbgapi is being linked in. */ #undef HAVE_AMD_DBGAPI +/* Define to 1 if you have the <asm/termios.h> header file. */ +#undef HAVE_ASM_TERMIOS_H + /* Define to 1 if you have the `btowc' function. */ #undef HAVE_BTOWC @@ -244,6 +250,9 @@ /* Define to 1 if you have the <inttypes.h> header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the <IOKit/serial/ioss.h> header file. */ +#undef HAVE_IOKIT_SERIAL_IOSS_H + /* Define to 1 if you have the `kinfo_getfile' function. */ #undef HAVE_KINFO_GETFILE @@ -639,6 +648,9 @@ */ #undef LT_OBJDIR +/* defined if mdebug format was requested. */ +#undef MDEBUG_FORMAT_AVAILABLE + /* Name of this package. */ #undef PACKAGE @@ -722,9 +734,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..8fc3b04 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,8 @@ with_relocated_sources with_auto_load_dir with_auto_load_safe_path enable_targets -enable_64_bit_bfd +enable_gdb_mdebug_support +enable_gdb_dwarf_support with_amd_dbgapi enable_tui enable_gdbtk @@ -1646,7 +1645,12 @@ 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-gdb-mdebug-support + Enable support for the mdebug debuginfo format + (default 'yes') + --enable-gdb-dwarf-support + Enable support for the dwarf debuginfo format + (default 'yes') --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 +11507,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 11510 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11609,7 +11613,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 11616 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -24879,70 +24883,90 @@ 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 +# Check whether to support mdebug/ecoff debug information. +# Check whether --enable-gdb-mdebug-support was given. +if test "${enable_gdb_mdebug_support+set}" = set; then : + enableval=$enable_gdb_mdebug_support; + case $enableval in + yes | no) + ;; + *) + as_fn_error $? "bad value $enableval for --enable-gdb-mdebug-support" "$LINENO" 5 + ;; + esac + else - enable_64_bit_bfd=no + enable_gdb_mdebug_support=yes 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 : +if test "x${enable_gdb_mdebug_support}" != "xno"; then + CONFIG_SRCS="$CONFIG_SRCS mdebugread.c" + CONFIG_OBS="$CONFIG_OBS mdebugread.o" -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 +$as_echo "#define MDEBUG_FORMAT_AVAILABLE 1" >>confdefs.h fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5 -$as_echo "$ac_cv_sizeof_void_p" >&6; } +# Check whether to support dwarf debug information +# Check whether --enable-gdb-dwarf-support was given. +if test "${enable_gdb_dwarf_support+set}" = set; then : + enableval=$enable_gdb_dwarf_support; + case $enableval in + yes | no) + ;; + *) + as_fn_error $? "bad value $enableval for --enable-gdb-dwarf-support" "$LINENO" 5 + ;; + esac +else + enable_gdb_dwarf_support=yes +fi -cat >>confdefs.h <<_ACEOF -#define SIZEOF_VOID_P $ac_cv_sizeof_void_p -_ACEOF +if test "x${enable_gdb_dwarf_support}" != "xno"; then - if test "x$ac_cv_sizeof_void_p" = "x8"; then : - enable_64_bit_bfd=yes -fi +$as_echo "#define DWARF_FORMAT_AVAILABLE 1" >>confdefs.h + CONFIG_SRCS="$CONFIG_SRCS \$(DWARF2_SRCS)" + CONFIG_OBS="$CONFIG_OBS \$(DWARF2_OBS)" fi - if test "x$enable_64_bit_bfd" = "xyes"; then - ENABLE_BFD_64_BIT_TRUE= - ENABLE_BFD_64_BIT_FALSE='#' +# 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 (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 +25016,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 +25029,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 @@ -28210,8 +28225,8 @@ int main () { - #if PY_MAJOR_VERSION != 3 - # error "We only support Python 3" + #if PY_VERSION_HEX < 0x03040000 + # error "Minimum supported Python version is 3.4" #endif Py_Initialize (); @@ -28987,11 +29002,20 @@ if test "${enable_gdb_compile+set}" = set; then : esac else - enable_gdb_compile=yes + enable_gdb_compile=default fi -if test "${enable_gdb_compile}" = yes; then +if test "${enable_gdb_compile}" = "default"; then + enable_gdb_compile=${enable_gdb_dwarf_support} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Defaulting compile support to '${enable_gdb_dwarf_support}'" >&5 +$as_echo "$as_me: WARNING: Defaulting compile support to '${enable_gdb_dwarf_support}'" >&2;} +fi + +if test "${enable_gdb_compile}" = "yes"; then + if test "${enable_gdb_dwarf_support}" = "no"; then + as_fn_error $? "enabling gdb compile requires dwarf support" "$LINENO" 5 + fi $as_echo "#define HAVE_COMPILE 1" >>confdefs.h @@ -29239,6 +29263,8 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi for ac_header in \ + IOKit/serial/ioss.h \ + asm/termios.h \ machine/reg.h \ nlist.h \ ptrace.h \ @@ -31659,7 +31685,7 @@ if test "$gdb_cv_var_elf" = yes; then $as_echo "#define HAVE_ELF 1" >>confdefs.h - # -ldl is provided by bfd/Makfile.am (LIBDL) <PLUGINS>. + # -ldl is provided by bfd/Makefile.am (LIBDL) <PLUGINS>. if test "$plugins" = "yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5 $as_echo_n "checking for library containing dlopen... " >&6; } @@ -33946,10 +33972,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..226e27e 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -192,6 +192,34 @@ AS_HELP_STRING([--enable-targets=TARGETS], [alternative target configurations]), esac]) +# Check whether to support mdebug/ecoff debug information. +AC_ARG_ENABLE(gdb-mdebug-support, +AS_HELP_STRING([--enable-gdb-mdebug-support], + [Enable support for the mdebug debuginfo format (default 'yes')]), +[GDB_CHECK_YES_NO_VAL([$enableval], [--enable-gdb-mdebug-support])], +[enable_gdb_mdebug_support=yes]) + +if test "x${enable_gdb_mdebug_support}" != "xno"; then + CONFIG_SRCS="$CONFIG_SRCS mdebugread.c" + CONFIG_OBS="$CONFIG_OBS mdebugread.o" + AC_DEFINE(MDEBUG_FORMAT_AVAILABLE, 1, + [defined if mdebug format was requested.]) +fi + +# Check whether to support dwarf debug information +AC_ARG_ENABLE(gdb-dwarf-support, +AS_HELP_STRING([--enable-gdb-dwarf-support], + [Enable support for the dwarf debuginfo format (default 'yes')]), +[GDB_CHECK_YES_NO_VAL([$enableval], [--enable-gdb-dwarf-support])], +[enable_gdb_dwarf_support=yes]) + +if test "x${enable_gdb_dwarf_support}" != "xno"; then + AC_DEFINE(DWARF_FORMAT_AVAILABLE, 1, + [defined if dwarf format was requested.]) + CONFIG_SRCS="$CONFIG_SRCS \$(DWARF2_SRCS)" + CONFIG_OBS="$CONFIG_OBS \$(DWARF2_OBS)" +fi + BFD_64_BIT # Provide defaults for some variables set by the per-host and per-target @@ -241,7 +269,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 +282,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 @@ -749,8 +768,8 @@ AC_DEFUN([AC_TRY_LIBPYTHON], found_usable_python=no AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include "Python.h"]], [[ - #if PY_MAJOR_VERSION != 3 - # error "We only support Python 3" + #if PY_VERSION_HEX < 0x03040000 + # error "Minimum supported Python version is 3.4" #endif Py_Initialize (); ]])], @@ -1229,9 +1248,17 @@ AC_ARG_ENABLE([gdb-compile], AS_HELP_STRING([--enable-gdb-compile], [enable support for the compile subsystem, default 'yes']), [GDB_CHECK_YES_NO_VAL([$enableval], [--enable-gdb-compile])], - [enable_gdb_compile=yes]) + [enable_gdb_compile=default]) + +if test "${enable_gdb_compile}" = "default"; then + enable_gdb_compile=${enable_gdb_dwarf_support} + AC_MSG_WARN([Defaulting compile support to '${enable_gdb_dwarf_support}']) +fi -if test "${enable_gdb_compile}" = yes; then +if test "${enable_gdb_compile}" = "yes"; then + if test "${enable_gdb_dwarf_support}" = "no"; then + AC_MSG_ERROR([enabling gdb compile requires dwarf support]) + fi AC_DEFINE(HAVE_COMPILE, 1, [Define if compiling support to gdb compile.]) CONFIG_OBS="$CONFIG_OBS \$(SUBDIR_GCC_COMPILE_OBS)" else @@ -1330,6 +1357,8 @@ AC_SUBST(SRCHIGH_CFLAGS) AC_HEADER_STDC AC_CHECK_HEADERS([ \ + IOKit/serial/ioss.h \ + asm/termios.h \ machine/reg.h \ nlist.h \ ptrace.h \ @@ -1931,7 +1960,7 @@ if test "$gdb_cv_var_elf" = yes; then gcore-elf.o elf-none-tdep.o" AC_DEFINE(HAVE_ELF, 1, [Define if ELF support should be included.]) - # -ldl is provided by bfd/Makfile.am (LIBDL) <PLUGINS>. + # -ldl is provided by bfd/Makefile.am (LIBDL) <PLUGINS>. if test "$plugins" = "yes"; then AC_SEARCH_LIBS(dlopen, dl) fi diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 44da225..255c77e 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 ;; @@ -151,13 +151,14 @@ aarch64*-*-linux*) arch/arm.o arch/arm-linux.o arch/arm-get-next-pcs.o \ arm-tdep.o arm-linux-tdep.o \ glibc-tdep.o linux-tdep.o solib-svr4.o \ + solib-svr4-linux.o svr4-tls-tdep.o \ symfile-mem.o linux-record.o" ;; alpha*-*-linux*) # Target: Little-endian Alpha running Linux gdb_target_obs="alpha-mdebug-tdep.o alpha-linux-tdep.o \ - linux-tdep.o solib-svr4.o" + linux-tdep.o solib-svr4.o solib-svr4-linux.o" ;; alpha*-*-netbsd* | alpha*-*-knetbsd*-gnu) # Target: NetBSD/alpha @@ -179,7 +180,7 @@ amdgcn*-*-*) am33_2.0*-*-linux*) # Target: Matsushita mn10300 (AM33) running Linux gdb_target_obs="mn10300-tdep.o mn10300-linux-tdep.o linux-tdep.o \ - solib-svr4.o" + solib-svr4.o solib-svr4-linux.o" ;; arc*-*-elf32) @@ -189,7 +190,8 @@ arc*-*-elf32) arc*-*-linux*) # Target: ARC machine running Linux - gdb_target_obs="arc-linux-tdep.o linux-tdep.o solib-svr4.o" + gdb_target_obs="arc-linux-tdep.o linux-tdep.o solib-svr4.o \ + solib-svr4-linux.o" ;; arm*-wince-pe | arm*-*-mingw32ce*) @@ -199,7 +201,8 @@ arm*-wince-pe | arm*-*-mingw32ce*) arm*-*-linux*) # Target: ARM based machine running GNU/Linux gdb_target_obs="arch/arm-linux.o arm-linux-tdep.o glibc-tdep.o \ - solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o" + solib-svr4.o solib-svr4-linux.o symfile-mem.o \ + linux-tdep.o linux-record.o" ;; arm*-*-freebsd*) # Target: FreeBSD/arm @@ -239,13 +242,14 @@ bpf-*-*) cris*) # Target: CRIS - gdb_target_obs="cris-tdep.o cris-linux-tdep.o linux-tdep.o solib-svr4.o" + gdb_target_obs="cris-tdep.o cris-linux-tdep.o linux-tdep.o \ + solib-svr4.o solib-svr4-linux.o" ;; csky*-*-linux*) # Target: CSKY running GNU/Linux gdb_target_obs="csky-tdep.o csky-linux-tdep.o glibc-tdep.o \ - linux-tdep.o solib-svr4.o" + linux-tdep.o solib-svr4.o solib-svr4-linux.o" ;; csky*-*-*) @@ -270,7 +274,8 @@ h8300-*-*) hppa*-*-linux*) # Target: HP PA-RISC running Linux gdb_target_obs="hppa-linux-tdep.o glibc-tdep.o \ - linux-tdep.o solib-svr4.o symfile-mem.o" + linux-tdep.o solib-svr4.o solib-svr4-linux.o \ + symfile-mem.o" ;; hppa*-*-netbsd*) # Target: NetBSD/hppa @@ -284,7 +289,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 @@ -315,11 +320,11 @@ i[34567]86-*-linux*) # Target: Intel 386 running GNU/Linux gdb_target_obs="i386-linux-tdep.o \ glibc-tdep.o \ - solib-svr4.o symfile-mem.o \ + solib-svr4.o solib-svr4-linux.o symfile-mem.o \ 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}" @@ -345,7 +350,7 @@ i[34567]86-*-go32* | i[34567]86-*-msdosdjgpp*) ia64-*-linux*) # Target: Intel IA-64 running GNU/Linux gdb_target_obs="ia64-linux-tdep.o linux-tdep.o \ - solib-svr4.o symfile-mem.o" + solib-svr4.o solib-svr4-linux.o symfile-mem.o" ;; ia64-*-*vms*) # Target: Intel IA-64 running OpenVMS @@ -363,7 +368,8 @@ lm32-*-*) loongarch*-*-linux*) # Target: LoongArch running Linux gdb_target_obs="loongarch-linux-tdep.o glibc-tdep.o \ - linux-tdep.o solib-svr4.o linux-record.o" + linux-tdep.o solib-svr4.o solib-svr4-linux.o \ + linux-record.o" ;; m32c-*-*) @@ -374,8 +380,8 @@ m32c-*-*) m32r*-*-linux*) # Target: Renesas M32R running GNU/Linux gdb_target_obs="m32r-tdep.o m32r-linux-tdep.o \ - glibc-tdep.o solib-svr4.o symfile-mem.o \ - linux-tdep.o" + glibc-tdep.o solib-svr4.o solib-svr4-linux.o \ + symfile-mem.o linux-tdep.o" ;; m32r*-*-*) # Target: Renesas m32r processor @@ -395,7 +401,8 @@ fido-*-elf*) m68*-*-linux*) # Target: Motorola m68k with a.out and ELF gdb_target_obs="m68k-tdep.o m68k-linux-tdep.o solib-svr4.o \ - linux-tdep.o glibc-tdep.o symfile-mem.o" + solib-svr4-linux.o linux-tdep.o glibc-tdep.o \ + symfile-mem.o" ;; m68*-*-netbsd* | m68*-*-knetbsd*-gnu) # Target: NetBSD/m68k @@ -415,7 +422,7 @@ mep-*-*) microblaze*-linux-*|microblaze*-*-linux*) # Target: Xilinx MicroBlaze running Linux gdb_target_obs="microblaze-tdep.o microblaze-linux-tdep.o solib-svr4.o \ - symfile-mem.o linux-tdep.o" + solib-svr4-linux.o symfile-mem.o linux-tdep.o" ;; microblaze*-*-*) # Target: Xilinx MicroBlaze running standalone @@ -425,7 +432,8 @@ microblaze*-*-*) mips*-*-linux*) # Target: Linux/MIPS gdb_target_obs="mips-tdep.o mips-linux-tdep.o glibc-tdep.o \ - solib-svr4.o symfile-mem.o linux-tdep.o" + solib-svr4.o solib-svr4-linux.o symfile-mem.o \ + linux-tdep.o" ;; mips*-*-netbsd* | mips*-*-knetbsd*-gnu) # Target: MIPS running NetBSD @@ -469,7 +477,8 @@ nds32*-*-elf) or1k*-*-linux*) # Target: OpenCores OpenRISC 1000 32-bit running Linux gdb_target_obs="or1k-tdep.o or1k-linux-tdep.o solib-svr4.o \ - symfile-mem.o glibc-tdep.o linux-tdep.o" + solib-svr4-linux.o symfile-mem.o glibc-tdep.o \ + linux-tdep.o" ;; or1k-*-* | or1knd-*-*) @@ -503,7 +512,8 @@ powerpc-*-aix* | rs6000-*-* | powerpc64-*-aix*) powerpc*-*-linux*) # Target: PowerPC running Linux gdb_target_obs="rs6000-tdep.o ppc-linux-tdep.o ppc-sysv-tdep.o \ - ppc64-tdep.o solib-svr4.o \ + ppc64-tdep.o solib-svr4.o solib-svr4-linux.o \ + svr4-tls-tdep.o \ glibc-tdep.o symfile-mem.o linux-tdep.o \ ravenscar-thread.o ppc-ravenscar-thread.o \ linux-record.o \ @@ -524,7 +534,9 @@ powerpc*-*-*) s390*-*-linux*) # Target: S390 running Linux gdb_target_obs="s390-linux-tdep.o s390-tdep.o solib-svr4.o \ - linux-tdep.o linux-record.o symfile-mem.o" + solib-svr4-linux.o \ + linux-tdep.o linux-record.o symfile-mem.o \ + svr4-tls-tdep.o" ;; riscv*-*-freebsd*) @@ -535,8 +547,9 @@ riscv*-*-freebsd*) riscv*-*-linux*) # Target: Linux/RISC-V gdb_target_obs="riscv-linux-tdep.o riscv-canonicalize-syscall-gen.o \ - glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o \ - linux-record.o" + glibc-tdep.o linux-tdep.o solib-svr4.o solib-svr4-linux.o \ + symfile-mem.o \ + linux-record.o svr4-tls-tdep.o" ;; riscv*-*-*) @@ -557,7 +570,7 @@ rx-*-*) sh*-*-linux*) # Target: GNU/Linux Super-H gdb_target_obs="sh-tdep.o sh-linux-tdep.o \ - solib-svr4.o symfile-mem.o \ + solib-svr4.o solib-svr4-linux.o symfile-mem.o \ glibc-tdep.o linux-tdep.o" ;; sh*-*-netbsd* | sh*-*-knetbsd*-gnu) @@ -576,10 +589,11 @@ sh*) sparc-*-linux*) # Target: GNU/Linux SPARC gdb_target_obs="sparc-tdep.o \ - sparc-linux-tdep.o solib-svr4.o symfile-mem.o \ + sparc-linux-tdep.o solib-svr4.o solib-svr4-linux.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}" @@ -589,7 +603,8 @@ sparc64-*-linux*) # Target: GNU/Linux UltraSPARC gdb_target_obs="sparc64-tdep.o \ sparc64-linux-tdep.o sparc-tdep.o \ - sparc-linux-tdep.o solib-svr4.o linux-tdep.o \ + sparc-linux-tdep.o solib-svr4.o solib-svr4-linux.o \ + linux-tdep.o \ ravenscar-thread.o sparc-ravenscar-thread.o" ;; sparc*-*-freebsd* | sparc*-*-kfreebsd*-gnu) @@ -657,6 +672,7 @@ tic6x-*-*) tilegx-*-linux*) # Target: TILE-Gx gdb_target_obs="tilegx-tdep.o tilegx-linux-tdep.o solib-svr4.o \ + solib-svr4-linux.o \ symfile-mem.o glibc-tdep.o linux-tdep.o" ;; @@ -706,8 +722,9 @@ x86_64-*-elf*) x86_64-*-linux*) # Target: GNU/Linux x86-64 gdb_target_obs="amd64-linux-tdep.o ${i386_tobjs} \ - i386-linux-tdep.o glibc-tdep.o \ - solib-svr4.o symfile-mem.o linux-tdep.o linux-record.o \ + i386-linux-tdep.o glibc-tdep.o svr4-tls-tdep.o \ + solib-svr4.o solib-svr4-linux.o symfile-mem.o \ + linux-tdep.o linux-record.o \ arch/i386-linux-tdesc.o arch/amd64-linux-tdesc.o \ arch/x86-linux-tdesc-features.o" ;; diff --git a/gdb/contrib/ari/create-web-ari-in-src.sh b/gdb/contrib/ari/create-web-ari-in-src.sh index 4ea0398..b479ce5 100644 --- a/gdb/contrib/ari/create-web-ari-in-src.sh +++ b/gdb/contrib/ari/create-web-ari-in-src.sh @@ -50,7 +50,7 @@ if [ -z "${tempdir}" ] ; then fi fi -# Default location of generate index.hmtl web page. +# Default location of generated index.html web page. if [ -z "${webdir}" ] ; then # Use 'branch' subdir name if Tag contains branch if [ -f "${srcdir}/gdb/CVS/Tag" ] ; then diff --git a/gdb/contrib/ari/gdb_ari.sh b/gdb/contrib/ari/gdb_ari.sh index f539ed1..e10bbe0 100755 --- a/gdb/contrib/ari/gdb_ari.sh +++ b/gdb/contrib/ari/gdb_ari.sh @@ -60,7 +60,7 @@ Options: -Werror Treat all problems as errors. -Wall Report all problems. -Wari Report problems that should be fixed in new code. - -WCATEGORY Report problems in the specifed category. The category + -WCATEGORY Report problems in the specified category. The category can be prefixed with "no-". Valid categories are: ${all} EOF @@ -159,7 +159,7 @@ BEGIN { PWD = "'`pwd`'" } -# Print the error message for BUG. Append SUPLEMENT if non-empty. +# Print the error message for BUG. Append SUPPLEMENT if non-empty. function print_bug(file,line,prefix,category,bug,doc,supplement, suffix,idx) { if (print_idx) { idx = bug ": " diff --git a/gdb/contrib/ari/update-web-ari.sh b/gdb/contrib/ari/update-web-ari.sh index 6e7b5ff..b9ef184 100644 --- a/gdb/contrib/ari/update-web-ari.sh +++ b/gdb/contrib/ari/update-web-ari.sh @@ -176,7 +176,7 @@ fi # THIS HAS SUFFERED BIT ROT if ${check_indent_p} && test -d "${srcdir}" then - printf "Analizing file indentation:" 1>&2 + printf "Analyzing file indentation:" 1>&2 ( cd "${srcdir}" && /bin/sh ${aridir}/gdb_find.sh ${project} | while read f do if /bin/sh ${aridir}/gdb_indent.sh < ${f} 2>/dev/null | cmp -s - ${f} @@ -550,7 +550,7 @@ function print_heading (nb_file, where, bug_i) { for (bug_i = 1; bug_i <= nr_bug; bug_i++) { bug = i2bug[bug_i]; printf "<th>" - # The title names are offset by one. Otherwize, when the browser + # The title names are offset by one. Otherwise, when the browser # jumps to the name it leaves out half the relevant column. #printf "<a name=\",%s\"> </a>", bug printf "<a name=\",%s\"> </a>", i2bug[bug_i-1] @@ -851,7 +851,7 @@ EOF print_toc 0 -1 deprecate Deprecate <<EOF Mechanisms that are a candidate for being made obsolete. Once core GDB no longer depends on these mechanisms and/or there is a -replacement available, these mechanims can be deprecated (adding the +replacement available, these mechanisms can be deprecated (adding the deprecated prefix) obsoleted (put into category obsolete) or deleted. See obsolete and deprecated. EOF diff --git a/gdb/contrib/codespell-dictionary.txt b/gdb/contrib/codespell-dictionary.txt new file mode 100644 index 0000000..09bc309 --- /dev/null +++ b/gdb/contrib/codespell-dictionary.txt @@ -0,0 +1 @@ +gdbsever->gdbserver diff --git a/gdb/contrib/codespell-ignore-words.txt b/gdb/contrib/codespell-ignore-words.txt index 2d6e13a..881b1d2 100644 --- a/gdb/contrib/codespell-ignore-words.txt +++ b/gdb/contrib/codespell-ignore-words.txt @@ -1,2 +1,3 @@ configury SME +Synopsys 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..5541a01 100644 --- a/gdb/contrib/setup.cfg +++ b/gdb/contrib/setup.cfg @@ -4,6 +4,16 @@ skip = */ChangeLog*,*/configure,gdbsupport/Makefile.in,*.dat,*.eps,gdb/features/*.c,gdb/ada-casefold.h,gdb/copying.c,gdb/gdbarch-gen.h,gdb/gdbarch-gen.c,gdb/target-delegates-gen.c ignore-words = gdb/contrib/codespell-ignore-words.txt +dictionary = gdb/contrib/codespell-dictionary.txt,- # Ignore all URLs. uri-ignore-words-list = * + +# This codespell issue ( +# https://github.com/codespell-project/codespell/issues/3381 ) reports the +# need to have support for ignoring blocks of code. +# A suggestion there is to use ignore-multiline-regex, which does work, but +# has a bug: using -w drops all newlines in the updated files ( +# https://github.com/codespell-project/codespell/issues/3642 ). +# Consequently, disabled. To be enabled when the bug is fixed. +#ignore-multiline-regex = codespell:ignore-begin.*codespell:ignore-end diff --git a/gdb/copying.c b/gdb/copying.c index af465e5..a198030 100644 --- a/gdb/copying.c +++ b/gdb/copying.c @@ -639,9 +639,7 @@ show_warranty_command (const char *ignore, int from_tty) gdb_printf ("\n"); } -void _initialize_copying (); -void -_initialize_copying () +INIT_GDB_FILE (copying) { add_cmd ("copying", no_set_class, show_copying_command, _("Conditions for redistributing copies of GDB."), diff --git a/gdb/corefile.c b/gdb/corefile.c index 9f27f14..a2c75c0 100644 --- a/gdb/corefile.c +++ b/gdb/corefile.c @@ -391,9 +391,7 @@ set_gnutarget (const char *newtarget) set_gnutarget_command (NULL, 0, NULL); } -void _initialize_core (); -void -_initialize_core () +INIT_GDB_FILE (core) { cmd_list_element *core_file_cmd = add_cmd ("core-file", class_files, core_file_command, _("\ diff --git a/gdb/corelow.c b/gdb/corelow.c index 4518781..24b949b 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -37,7 +37,6 @@ #include "exec.h" #include "readline/tilde.h" #include "solib.h" -#include "solist.h" #include "filenames.h" #include "progspace.h" #include "objfiles.h" @@ -957,7 +956,7 @@ locate_exec_from_corefile_exec_context (bfd *cbfd, execbfd = open_and_check_build_id (exec_name); else { - std::string p = (ldirname (bfd_get_filename (cbfd)) + std::string p = (gdb_ldirname (bfd_get_filename (cbfd)) + '/' + exec_name); execbfd = open_and_check_build_id (p.c_str ()); @@ -971,7 +970,7 @@ locate_exec_from_corefile_exec_context (bfd *cbfd, if (execbfd == nullptr) { const char *base_name = lbasename (exec_name); - std::string p = (ldirname (bfd_get_filename (cbfd)) + std::string p = (gdb_ldirname (bfd_get_filename (cbfd)) + '/' + base_name); execbfd = open_and_check_build_id (p.c_str ()); @@ -1181,7 +1180,7 @@ core_target_open (const char *arg, int from_tty) if (current_program_space->exec_bfd () == nullptr) set_gdbarch_from_file (current_program_space->core_bfd ()); - post_create_inferior (from_tty); + post_create_inferior (from_tty, true); /* Now go through the target stack looking for threads since there may be a thread_stratum target loaded on top of target core by @@ -2158,9 +2157,7 @@ core_target_find_mapped_file (const char *filename, return targ->lookup_mapped_file_info (filename, addr); } -void _initialize_corelow (); -void -_initialize_corelow () +INIT_GDB_FILE (corelow) { add_target (core_target_info, core_target_open, filename_maybe_quoted_completer); diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c index 8f2067b..d8cfc08 100644 --- a/gdb/cp-abi.c +++ b/gdb/cp-abi.c @@ -384,9 +384,7 @@ show_cp_abi_cmd (const char *args, int from_tty) uiout->text (").\n"); } -void _initialize_cp_abi (); -void -_initialize_cp_abi () +INIT_GDB_FILE (cp_abi) { struct cmd_list_element *c; diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y index 10f6e2d..cafd6b2 100644 --- a/gdb/cp-name-parser.y +++ b/gdb/cp-name-parser.y @@ -374,6 +374,22 @@ function | colon_ext_only function_arglist start_opt { $$ = state->fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp); if ($3) $$ = state->fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $3); } + | colon_ext_only + { + /* This production is a hack to handle + something like "name::operator new[]" -- + without arguments, this ordinarily would + not parse, but canonicalizing it is + important. So we infer the "()" and then + remove it when converting back to string. + Note that this works because this + production is terminal. */ + demangle_component *comp + = state->fill_comp (DEMANGLE_COMPONENT_FUNCTION_TYPE, + nullptr, nullptr); + $$ = state->fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, comp); + state->demangle_info->added_parens = true; + } | conversion_op_name start_opt { $$ = $1.comp; @@ -2047,6 +2063,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) @@ -2106,13 +2127,17 @@ canonicalize_tests () should_be_the_same ("Foozle<int>::fogey<Empty<int> > (Empty<int>)", "Foozle<int>::fogey<Empty<int>> (Empty<int>)"); + + should_be_the_same ("something :: operator new [ ]", + "something::operator new[]"); + should_be_the_same ("something :: operator new", + "something::operator new"); + should_be_the_same ("operator()", "operator ()"); } #endif -void _initialize_cp_name_parser (); -void -_initialize_cp_name_parser () +INIT_GDB_FILE (cp_name_parser) { #if GDB_SELF_TEST selftests::register_test ("canonicalize", canonicalize_tests); diff --git a/gdb/cp-namespace.c b/gdb/cp-namespace.c index d285e86..009ea4b 100644 --- a/gdb/cp-namespace.c +++ b/gdb/cp-namespace.c @@ -1049,9 +1049,7 @@ maintenance_cplus_namespace (const char *args, int from_tty) gdb_printf (_("The `maint namespace' command was removed.\n")); } -void _initialize_cp_namespace (); -void -_initialize_cp_namespace () +INIT_GDB_FILE (cp_namespace) { struct cmd_list_element *cmd; diff --git a/gdb/cp-support.c b/gdb/cp-support.c index c71b8d9..cab7110 100644 --- a/gdb/cp-support.c +++ b/gdb/cp-support.c @@ -573,6 +573,17 @@ replace_typedefs (struct demangle_parse_info *info, } } +/* A helper to strip a trailing "()" from PTR. The string is modified + in place. */ + +static void +maybe_strip_parens (char *ptr) +{ + size_t len = strlen (ptr); + if (len > 2 && ptr[len - 2] == '(' && ptr[len - 1] == ')') + ptr[len - 2] = '\0'; +} + /* Parse STRING and convert it to canonical form, resolving any typedefs. If parsing fails, or if STRING is already canonical, return nullptr. Otherwise return the canonical form. If @@ -599,6 +610,9 @@ cp_canonicalize_string_full (const char *string, estimated_len); gdb_assert (us); + if (info->added_parens) + maybe_strip_parens (us.get ()); + /* Finally, compare the original string with the computed name, returning NULL if they are the same. */ if (strcmp (us.get (), string) == 0) @@ -647,6 +661,9 @@ cp_canonicalize_string (const char *string) return nullptr; } + if (info->added_parens) + maybe_strip_parens (us.get ()); + if (strcmp (us.get (), string) == 0) return nullptr; @@ -2370,9 +2387,7 @@ find_toplevel_char (const char *s, char c) return 0; } -void _initialize_cp_support (); -void -_initialize_cp_support () +INIT_GDB_FILE (cp_support) { cmd_list_element *maintenance_cplus = add_basic_prefix_cmd ("cplus", class_maintenance, diff --git a/gdb/cp-support.h b/gdb/cp-support.h index 3ed354c..ffe0ba1 100644 --- a/gdb/cp-support.h +++ b/gdb/cp-support.h @@ -63,6 +63,10 @@ struct demangle_parse_info /* Any memory used during processing. */ auto_obstack obstack; + /* True if the parser had to add a dummy '()' at the end of the + input text to make it parse. */ + bool added_parens = false; + /* Any other objects referred to by this object, and whose storage lifetime must be linked. */ std::vector<std::unique_ptr<demangle_parse_info>> infos; diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c index 2117718..252072b 100644 --- a/gdb/cp-valprint.c +++ b/gdb/cp-valprint.c @@ -797,9 +797,7 @@ test_print_fields (gdbarch *arch) #endif -void _initialize_cp_valprint (); -void -_initialize_cp_valprint () +INIT_GDB_FILE (cp_valprint) { #if GDB_SELF_TEST selftests::register_test_foreach_arch ("print-fields", test_print_fields); diff --git a/gdb/cris-linux-tdep.c b/gdb/cris-linux-tdep.c index b2ac80d..a39441e 100644 --- a/gdb/cris-linux-tdep.c +++ b/gdb/cris-linux-tdep.c @@ -23,6 +23,7 @@ #include "osabi.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "solib-svr4.h" #include "symtab.h" #include "gdbarch.h" @@ -41,14 +42,10 @@ cris_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_ilp32_fetch_link_map_offsets); - + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); } -void _initialize_cris_linux_tdep (); -void -_initialize_cris_linux_tdep () +INIT_GDB_FILE (cris_linux_tdep) { gdbarch_register_osabi (bfd_arch_cris, 0, GDB_OSABI_LINUX, cris_linux_init_abi); diff --git a/gdb/cris-tdep.c b/gdb/cris-tdep.c index 722c0a4..b49e641 100644 --- a/gdb/cris-tdep.c +++ b/gdb/cris-tdep.c @@ -3816,9 +3816,7 @@ static void cris_iterate_over_regset_sections (struct gdbarch *gdbarch, &cris_regset, NULL, cb_data); } -void _initialize_cris_tdep (); -void -_initialize_cris_tdep () +INIT_GDB_FILE (cris_tdep) { gdbarch_register (bfd_arch_cris, cris_gdbarch_init, cris_dump_tdep); diff --git a/gdb/csky-linux-tdep.c b/gdb/csky-linux-tdep.c index 2afb358..0651645 100644 --- a/gdb/csky-linux-tdep.c +++ b/gdb/csky-linux-tdep.c @@ -22,6 +22,7 @@ #include "osabi.h" #include "glibc-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "gdbarch.h" #include "solib-svr4.h" #include "regset.h" @@ -407,8 +408,7 @@ csky_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Shared library handling. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, @@ -426,9 +426,7 @@ csky_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) &csky_linux_rt_sigreturn_tramp_frame_kernel_4x); } -void _initialize_csky_linux_tdep (); -void -_initialize_csky_linux_tdep () +INIT_GDB_FILE (csky_linux_tdep) { gdbarch_register_osabi (bfd_arch_csky, 0, GDB_OSABI_LINUX, csky_linux_init_abi); diff --git a/gdb/csky-tdep.c b/gdb/csky-tdep.c index 350612f..e6c6e2c 100644 --- a/gdb/csky-tdep.c +++ b/gdb/csky-tdep.c @@ -2882,9 +2882,7 @@ csky_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_csky_tdep (); -void -_initialize_csky_tdep () +INIT_GDB_FILE (csky_tdep) { gdbarch_register (bfd_arch_csky, csky_gdbarch_init); diff --git a/gdb/darwin-nat-info.c b/gdb/darwin-nat-info.c index b5784be..3ec49af 100644 --- a/gdb/darwin-nat-info.c +++ b/gdb/darwin-nat-info.c @@ -839,9 +839,7 @@ info_mach_exceptions_command (const char *args, int from_tty) } } -void _initialize_darwin_info_commands (); -void -_initialize_darwin_info_commands () +INIT_GDB_FILE (darwin_info_commands) { add_info ("mach-tasks", info_mach_tasks_command, _("Get list of tasks in system.")); diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c index 6bc0e4f..7acf639 100644 --- a/gdb/darwin-nat.c +++ b/gdb/darwin-nat.c @@ -1853,7 +1853,7 @@ copy_shell_to_cache (const char *shell, const std::string &new_name) error (_("Could not open shell (%s) for reading: %s"), shell, safe_strerror (errno)); - std::string new_dir = ldirname (new_name.c_str ()); + std::string new_dir = gdb_ldirname (new_name.c_str ()); if (!mkdir_recursive (new_dir.c_str ())) error (_("Could not make cache directory \"%s\": %s"), new_dir.c_str (), safe_strerror (errno)); @@ -2469,9 +2469,7 @@ darwin_nat_target::supports_multi_process () return true; } -void _initialize_darwin_nat (); -void -_initialize_darwin_nat () +INIT_GDB_FILE (darwin_nat) { kern_return_t kret; diff --git a/gdb/dbxread.c b/gdb/dbxread.c index 8d0ebab..260f583 100644 --- a/gdb/dbxread.c +++ b/gdb/dbxread.c @@ -248,9 +248,7 @@ static const struct sym_fns aout_sym_fns = NULL, /* sym_probe_fns */ }; -void _initialize_dbxread (); -void -_initialize_dbxread () +INIT_GDB_FILE (dbxread) { add_symtab_fns (bfd_target_aout_flavour, &aout_sym_fns); } diff --git a/gdb/dcache.c b/gdb/dcache.c index 2c5484d..7a15278 100644 --- a/gdb/dcache.c +++ b/gdb/dcache.c @@ -679,9 +679,7 @@ set_dcache_line_size (const char *args, int from_tty, target_dcache_invalidate (current_program_space->aspace); } -void _initialize_dcache (); -void -_initialize_dcache () +INIT_GDB_FILE (dcache) { add_setshow_boolean_cmd ("remotecache", class_support, &dcache_enabled_p, _("\ diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c index d573b8d..8f28fd5 100644 --- a/gdb/debuginfod-support.c +++ b/gdb/debuginfod-support.c @@ -594,9 +594,7 @@ maint_get_debuginfod_download_sections () /* Register debuginfod commands. */ -void _initialize_debuginfod (); -void -_initialize_debuginfod () +INIT_GDB_FILE (debuginfod) { /* set/show debuginfod */ add_setshow_prefix_cmd ("debuginfod", class_run, @@ -190,8 +190,6 @@ extern std::string relocate_gdb_directory (const char *initial, bool relocatable /* From top.c */ -typedef void initialize_file_ftype (void); - extern char *gdb_readline_wrapper (const char *); extern const char *command_line_input (std::string &cmd_line_buffer, @@ -407,4 +405,10 @@ DEF_ENUM_FLAGS_TYPE (enum user_selected_what_flag, user_selected_what); #include "utils.h" +/* File initialization macro. This is found by make-init-c and used + to construct the gdb initialization function. */ +#define INIT_GDB_FILE(NAME) \ + extern void _initialize_ ## NAME (); \ + void _initialize_ ## NAME () + #endif /* GDB_DEFS_H */ diff --git a/gdb/dicos-tdep.c b/gdb/dicos-tdep.c index 4dfac76..d6bc9a5 100644 --- a/gdb/dicos-tdep.c +++ b/gdb/dicos-tdep.c @@ -27,7 +27,7 @@ void dicos_init_abi (struct gdbarch *gdbarch) { - set_gdbarch_so_ops (gdbarch, &solib_target_so_ops); + set_gdbarch_make_solib_ops (gdbarch, make_target_solib_ops); /* Every process, although has its own address space, sees the same list of shared libraries. There's no "main executable" in DICOS, diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c index 3ccc174..0d06065 100644 --- a/gdb/disasm-selftests.c +++ b/gdb/disasm-selftests.c @@ -418,9 +418,7 @@ disassemble_insn (gdbarch *gdbarch, gdb::byte_vector &insn, } /* namespace selftests */ -void _initialize_disasm_selftests (); -void -_initialize_disasm_selftests () +INIT_GDB_FILE (disasm_selftests) { selftests::register_test_foreach_arch ("print_one_insn", selftests::print_one_insn_test); diff --git a/gdb/disasm.c b/gdb/disasm.c index 2a7a80c..b731192 100644 --- a/gdb/disasm.c +++ b/gdb/disasm.c @@ -1470,9 +1470,7 @@ disassembler_options_completer (struct cmd_list_element *ignore, /* Initialization code. */ -void _initialize_disasm (); -void -_initialize_disasm () +INIT_GDB_FILE (disasm) { /* Add the command that controls the disassembler options. */ set_show_commands set_show_disas_opts diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c index 7df45d6..6699296 100644 --- a/gdb/displaced-stepping.c +++ b/gdb/displaced-stepping.c @@ -315,9 +315,7 @@ displaced_step_buffers::restore_in_ptid (ptid_t ptid) } } -void _initialize_displaced_stepping (); -void -_initialize_displaced_stepping () +INIT_GDB_FILE (displaced_stepping) { add_setshow_boolean_cmd ("displaced", class_maintenance, &debug_displaced, _("\ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 48b193b..b6d626b 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: @@ -4090,6 +4098,56 @@ When @samp{on} @value{GDBN} will print additional messages when threads are created and deleted. @end table +@cindex thread local storage +@cindex @acronym{TLS} +For some debugging targets, @value{GDBN} has support for accessing +variables that reside in Thread Local Storage (@acronym{TLS}). +@acronym{TLS} variables are similar to global variables, except that +each thread has its own copy of the variable. While often used in +multi-threaded programs, @acronym{TLS} variables can also be used in +programs without threads. The C library variable @var{errno} is, +perhaps, the most prominent example of a @acronym{TLS} variable that +is frequently used in non-threaded programs. For targets where +@value{GDBN} does not have good @acronym{TLS} support, printing or +changing the value of @var{errno} might not be directly possible. + +@sc{gnu}/Linux and FreeBSD targets have support for accessing +@acronym{TLS} variables. On @sc{gnu}/Linux, the helper library, +@code{libthread_db}, is used to help resolve the addresses of +@acronym{TLS} variables. Some FreeBSD and some @sc{gnu}/Linux targets +also have @value{GDBN}-internal @acronym{TLS} resolution code. +@sc{gnu}/Linux targets will attempt to use the @acronym{TLS} address +lookup functionality provided by @code{libthread_db}, but will fall +back to using its internal @acronym{TLS} support when +@code{libthread_db} is not available. This can happen in +cross-debugging scenarios or when debugging programs that are linked +in such a way that @code{libthread_db} support is unavailable -- this +includes statically linked programs, linking against @acronym{GLIBC} +versions earlier than 2.34, but not with @code{libpthread}, and use of +other (non-@acronym{GLIBC}) C libraries. + +@table @code +@kindex maint set force-internal-tls-address-lookup +@kindex maint show force-internal-tls-address-lookup +@cindex internal @acronym{TLS} address lookup +@item maint set force-internal-tls-address-lookup +@itemx maint show force-internal-tls-address-lookup +Turns on or off forced use of @value{GDBN}-internal @acronym{TLS} +address lookup code. Use @code{on} to enable and @code{off} to +disable. The default for this setting is @code{off}. + +When disabled, @value{GDBN} will attempt to use a helper +@code{libthread_db} library if possible, but will fall back to use of +its own internal @acronym{TLS} address lookup mechanisms if necessary. + +When enabled, @value{GDBN} will only use @value{GDBN}'s internal +@acronym{TLS} address lookup mechanisms, if they exist. + +This command is only available for @sc{gnu}/Linux targets. Its +primary use is for testing -- certain tests in the @value{GDBN} test +suite use this command to force use of internal TLS address lookup. +@end table + @node Forks @section Debugging Forks @@ -13048,15 +13106,15 @@ environment variable. @vindex $_active_linker_namespaces@r{, convenience variable} @item $_active_linker_namespaces -Number of active linkage namespaces in the inferior. In systems with no -support for linkage namespaces, this variable will always be set to @samp{1}. +Number of active linker namespaces in the inferior (@pxref{Files}). In systems +with no support for linker namespaces, this variable will always be set to +@samp{1}. -@vindex $_current_linker_namespace@r{, convenience variable} -@item $_current_linker_namespace -The namespace which contains the current location in the inferior. This -returns GDB's internal identifier for namespaces, which is @samp{[[@var{n}]]} -where @var{n} is a zero-based namespace number. In systems with no support -for linkage namespaces, this variable will always be set to @samp{[[0]]}. +@vindex $_linker_namespace@r{, convenience variable} +@item $_linker_namespace +The namespace which contains the current location in the inferior. This returns +@value{GDBN}'s internal numbering for the namespace. In systems with no support +for linker namespaces, this variable will always be set to @samp{0}. @end table @@ -22184,11 +22242,18 @@ be determined then the address range for the @code{.text} section from the library will be listed. If the @code{.text} section cannot be found then no addresses will be listed. -On systems that support linkage namespaces, the output includes an +On systems that support linker namespaces, the output includes an additional column @code{NS} if the inferior has more than one active -namespace when the command is used. This column the linkage namespace +namespace when the command is used. This column the linker namespace that the shared library was loaded into. +@cindex linker namespaces +@dfn{Linker namespaces} are a feature of some standard libraries, that allow +shared objects to be loaded in isolated environment, eliminating the +possibility that those objects may cross-talk. Each set of isolated +shared objects is said to belong to a ``namespace'', and linker related +actions such as relocations do not cross namespace boundaries. + @kindex info dll @item info dll @var{regex} This is an alias of @code{info sharedlibrary}. @@ -24717,6 +24782,10 @@ future connections is shown. The available settings are: @tab @code{vFile:stat} @tab Host I/O +@item @code{hostio-lstat-packet} +@tab @code{vFile:lstat} +@tab Host I/O + @item @code{hostio-setfs-packet} @tab @code{vFile:setfs} @tab Host I/O @@ -26580,8 +26649,9 @@ Show whether AArch64 debugging messages are displayed. @end table -@subsubsection AArch64 SVE. -@cindex AArch64 SVE. +@subsubsection AArch64 Scalable Vector Extension +@cindex Scalable Vector Extension, AArch64 +@cindex SVE, AArch64 When @value{GDBN} is debugging the AArch64 architecture, if the Scalable Vector Extension (SVE) is present, then @value{GDBN} will provide the vector registers @@ -26620,11 +26690,10 @@ internally by @value{GDBN} and the Linux Kernel. @end itemize -@subsubsection AArch64 SME. +@subsubsection AArch64 Scalable Matrix Extension @anchor{AArch64 SME} -@cindex SME -@cindex AArch64 SME -@cindex Scalable Matrix Extension +@cindex Scalable Matrix Extension, AArch64 +@cindex SME, AArch64 The Scalable Matrix Extension (@url{https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/scalable-matrix-extension-armv9-a-architecture, @acronym{SME}}) is an AArch64 architecture extension that expands on the concept of the @@ -26816,11 +26885,10 @@ incorrect values for SVE registers (when in streaming mode). This is the same limitation we have for the @acronym{SVE} registers, and there are plans to address this limitation going forward. -@subsubsection AArch64 SME2. +@subsubsection AArch64 Scalable Matrix Extension 2 @anchor{AArch64 SME2} -@cindex SME2 -@cindex AArch64 SME2 -@cindex Scalable Matrix Extension 2 +@cindex Scalable Matrix Extension 2, AArch64 +@cindex SME2, AArch64 The Scalable Matrix Extension 2 is an AArch64 architecture extension that further expands the @acronym{SME} extension with the following: @@ -26860,8 +26928,9 @@ For more information about @acronym{SME2}, please refer to the official @url{https://developer.arm.com/documentation/ddi0487/latest, architecture documentation}. -@subsubsection AArch64 Pointer Authentication. -@cindex AArch64 Pointer Authentication. +@subsubsection AArch64 Pointer Authentication +@cindex Pointer Authentication, AArch64 +@cindex PAC, AArch64 @anchor{AArch64 PAC} When @value{GDBN} is debugging the AArch64 architecture, and the program is @@ -26871,8 +26940,9 @@ When GDB prints a backtrace, any addresses that required unmasking will be postfixed with the marker [PAC]. When using the MI, this is printed as part of the @code{addr_flags} field. -@subsubsection AArch64 Memory Tagging Extension. -@cindex AArch64 Memory Tagging Extension. +@subsubsection AArch64 Memory Tagging Extension +@cindex Memory Tagging Extension, AArch64 +@cindex MTE, AArch64 When @value{GDBN} is debugging the AArch64 architecture, the program is using the v8.5-A feature Memory Tagging Extension (MTE) and there is support @@ -27924,6 +27994,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 @@ -27940,6 +28023,18 @@ then it will be used. @item show style sources Show the current state of source code styling. +@anchor{warning-prefix} +@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}. @@ -31242,138 +31337,13 @@ appropriate @code{set style} commands. @xref{Output Styling}. @cindex Emacs @cindex @sc{gnu} Emacs -A special interface allows you to use @sc{gnu} Emacs to view (and -edit) the source files for the program you are debugging with -@value{GDBN}. - -To use this interface, use the command @kbd{M-x gdb} in Emacs. Give the -executable file you want to debug as an argument. This command starts -@value{GDBN} as a subprocess of Emacs, with input and output through a newly -created Emacs buffer. -@c (Do not use the @code{-tui} option to run @value{GDBN} from Emacs.) -Running @value{GDBN} under Emacs can be just like running @value{GDBN} normally except for two -things: +In @sc{gnu} Emacs there is a special interface to @value{GDBN}, which +facilitates viewing the source code for the program you are debugging. +There is also an IDE-like interface to GDB, with specialized buffers for +breakpoints, stack frames and other aspects of the debugger state. -@itemize @bullet -@item -All ``terminal'' input and output goes through an Emacs buffer, called -the GUD buffer. - -This applies both to @value{GDBN} commands and their output, and to the input -and output done by the program you are debugging. - -This is useful because it means that you can copy the text of previous -commands and input them again; you can even use parts of the output -in this way. - -All the facilities of Emacs' Shell mode are available for interacting -with your program. In particular, you can send signals the usual -way---for example, @kbd{C-c C-c} for an interrupt, @kbd{C-c C-z} for a -stop. - -@item -@value{GDBN} displays source code through Emacs. - -Each time @value{GDBN} displays a stack frame, Emacs automatically finds the -source file for that frame and puts an arrow (@samp{=>}) at the -left margin of the current line. Emacs uses a separate buffer for -source display, and splits the screen to show both your @value{GDBN} session -and the source. - -Explicit @value{GDBN} @code{list} or search commands still produce output as -usual, but you probably have no reason to use them from Emacs. -@end itemize - -We call this @dfn{text command mode}. Emacs 22.1, and later, also uses -a graphical mode, enabled by default, which provides further buffers -that can control the execution and describe the state of your program. -@xref{GDB Graphical Interface,,, Emacs, The @sc{gnu} Emacs Manual}. - -If you specify an absolute file name when prompted for the @kbd{M-x -gdb} argument, then Emacs sets your current working directory to where -your program resides. If you only specify the file name, then Emacs -sets your current working directory to the directory associated -with the previous buffer. In this case, @value{GDBN} may find your -program by searching your environment's @env{PATH} variable, but on -some operating systems it might not find the source. So, although the -@value{GDBN} input and output session proceeds normally, the auxiliary -buffer does not display the current source and line of execution. - -The initial working directory of @value{GDBN} is printed on the top -line of the GUD buffer and this serves as a default for the commands -that specify files for @value{GDBN} to operate on. @xref{Files, -,Commands to Specify Files}. - -By default, @kbd{M-x gdb} calls the program called @file{gdb}. If you -need to call @value{GDBN} by a different name (for example, if you -keep several configurations around, with different names) you can -customize the Emacs variable @code{gud-gdb-command-name} to run the -one you want. - -In the GUD buffer, you can use these special Emacs commands in -addition to the standard Shell mode commands: - -@table @kbd -@item C-h m -Describe the features of Emacs' GUD Mode. - -@item C-c C-s -Execute to another source line, like the @value{GDBN} @code{step} command; also -update the display window to show the current file and location. - -@item C-c C-n -Execute to next source line in this function, skipping all function -calls, like the @value{GDBN} @code{next} command. Then update the display window -to show the current file and location. - -@item C-c C-i -Execute one instruction, like the @value{GDBN} @code{stepi} command; update -display window accordingly. - -@item C-c C-f -Execute until exit from the selected stack frame, like the @value{GDBN} -@code{finish} command. - -@item C-c C-r -Continue execution of your program, like the @value{GDBN} @code{continue} -command. - -@item C-c < -Go up the number of frames indicated by the numeric argument -(@pxref{Arguments, , Numeric Arguments, Emacs, The @sc{gnu} Emacs Manual}), -like the @value{GDBN} @code{up} command. - -@item C-c > -Go down the number of frames indicated by the numeric argument, like the -@value{GDBN} @code{down} command. -@end table - -In any source file, the Emacs command @kbd{C-x @key{SPC}} (@code{gud-break}) -tells @value{GDBN} to set a breakpoint on the source line point is on. - -In text command mode, if you type @kbd{M-x speedbar}, Emacs displays a -separate frame which shows a backtrace when the GUD buffer is current. -Move point to any frame in the stack and type @key{RET} to make it -become the current frame and display the associated source in the -source buffer. Alternatively, click @kbd{Mouse-2} to make the -selected frame become the current one. In graphical mode, the -speedbar displays watch expressions. - -If you accidentally delete the source-display buffer, an easy way to get -it back is to type the command @code{f} in the @value{GDBN} buffer, to -request a frame display; when you run under Emacs, this recreates -the source buffer if necessary to show you the context of the current -frame. - -The source files displayed in Emacs are in ordinary Emacs buffers -which are visiting the source files in the usual way. You can edit -the files with these buffers if you wish; but keep in mind that @value{GDBN} -communicates with Emacs in terms of line numbers. If you add or -delete lines from the text, the line numbers that @value{GDBN} knows cease -to correspond properly with the code. - -A more detailed description of Emacs' interaction with @value{GDBN} is +A detailed description of Emacs' interaction with @value{GDBN} is given in the Emacs manual (@pxref{Debuggers,,, Emacs, The @sc{gnu} Emacs Manual}). @@ -41384,7 +41354,7 @@ libpython is present and found at configure time.) Python makes @value{GDBN} scripting much more powerful than the restricted CLI scripting language. If your host does not have Python installed, you can find it on @url{http://www.python.org/download/}. The oldest version -of Python supported by GDB is 3.0.1. The optional argument @var{python} +of Python supported by GDB is 3.4. The optional argument @var{python} is used to find the Python headers and libraries. It can be either the name of a Python executable, or the name of the directory in which Python is installed. @@ -41582,6 +41552,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 @@ -42778,6 +42755,23 @@ reports and error and the command is aborted. @item show watchdog Show the current setting of the target wait timeout. + +@kindex maint set console-translation-mode +@kindex maint show console-translation-mode +@item maint set console-translation-mode @r{[}binary|text@r{]} +@itemx maint show console-translation-mode +Controls the translation mode of @value{GDBN} stdout/stderr. MS-Windows +only. Useful for running the @value{GDBN} testsuite. + +The translation mode values are as follows: +@table @code +@item binary +No translation. +@item text +Translate @samp{\n} (LF, a single Line Feed) into @samp{\r\n} (CR-LF, a +Carriage Return-Line Feed combination). +@end table + @end table @node Remote Protocol @@ -46754,12 +46748,28 @@ If an error occurs the return value is -1. The format of the returned binary attachment is as described in @ref{struct stat}. @item vFile:stat: @var{filename} -Get information about the file @var{filename} on the target. -On success the information is returned as a binary attachment -and the return value is the size of this attachment in bytes. -If an error occurs the return value is -1. The format of the +Get information about the file @var{filename} on the target as if from +a @samp{stat} call. On success the information is returned as a binary +attachment and the return value is the size of this attachment in +bytes. If an error occurs the return value is -1. The format of the returned binary attachment is as described in @ref{struct stat}. +If @var{filename} is a symbolic link, then the information returned is +about the file the link refers to, this is inline with the @samp{stat} +library call. + +@item vFile:lstat: @var{filename} +Get information about the file @var{filename} on the target as if from +an @samp{lstat} call. On success the information is returned as a +binary attachment and the return value is the size of this attachment +in bytes. If an error occurs the return value is -1. The format of +the returned binary attachment is as described in @ref{struct stat}. + +This packet is identical to @samp{vFile:stat}, except if +@var{filename} is a symbolic link, then this packet returns +information about the link itself, not the file that the link refers +to, this is inline with the @samp{lstat} library call. + @item vFile:unlink: @var{filename} Delete the file at @var{filename} on the target. Return 0, or -1 if an error occurs. The @var{filename} is a string. diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi index c6808ef..9677229 100644 --- a/gdb/doc/guile.texi +++ b/gdb/doc/guile.texi @@ -1772,6 +1772,16 @@ invoking it interactively. If this function throws an exception, it is turned into a @value{GDBN} @code{error} call. Otherwise, the return value is ignored. +For non-prefix commands, @var{invoke} is required. For prefix +commands @var{invoke} is optional. Only prefix commands that need to +handle unknown sub-commands should supply @var{invoke}. + +For prefix commands that don't supply @var{invoke}, if the prefix +command is used without a sub-command name then @value{GDBN} will +display the help text for every sub-command, unless the prefix command +is a @kbd{show} sub-command, in which case @value{GDBN} will list the +values of all sub-commands. + The argument @var{command-class} is one of the @samp{COMMAND_} constants defined below. This argument tells @value{GDBN} how to categorize the new command in the help system. The default is @code{COMMAND_NONE}. @@ -2098,8 +2108,10 @@ is the @code{<gdb:parameter>} object representing the parameter, and This function must return a string, and will be displayed to the user. @value{GDBN} will add a trailing newline. -The argument @var{doc} is the help text for the new parameter. -If there is no documentation string, a default value is used. +The argument @var{doc} is the help text for the new parameter. If +there is no documentation string, a default value is used. If the +documentation string is empty, then @value{GDBN} will print just the +@var{set-doc} and @var{show-doc} strings (see below). The argument @var{set-doc} is the help text for this parameter's @code{set} command. @@ -3899,6 +3911,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 cba3a0d..6fa2285 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -257,7 +257,7 @@ Python code must not override these, or even change the options using signals, @value{GDBN} will most likely stop working correctly. Note that it is unfortunately common for GUI toolkits to install a @code{SIGCHLD} handler. When creating a new Python thread, you can -use @code{gdb.block_signals} or @code{gdb.Thread} to handle this +use @code{gdb.blocked_signals} or @code{gdb.Thread} to handle this correctly; see @ref{Threading in GDB}. @item @@ -479,7 +479,7 @@ call this function and will automatically direct the output to the relevant stream. @end defun -@defun gdb.flush (@r{[}, stream@r{]}) +@defun gdb.flush (@r{[}stream@r{]}) Flush the buffer of a @value{GDBN} paginated stream so that the contents are displayed immediately. @value{GDBN} will flush the contents of a stream automatically when it encounters a newline in the @@ -509,6 +509,17 @@ Flushing @code{sys.stdout} or @code{sys.stderr} will automatically call this function for the relevant stream. @end defun +@defun gdb.warning (text) +Print a warning message to @value{GDBN}'s standard output stream. The +warning message is the warning prefix (@pxref{warning-prefix}), the +string @w{@samp{warning: }}, and then @var{text}, which must be a +non-empty string. + +Due to the warning prefix, @var{text} should not begin with a capital +letter (except for proper nouns), and @var{text} should end with a +period. +@end defun + @defun gdb.target_charset () Return the name of the current target character set (@pxref{Character Sets}). This differs from @code{gdb.parameter('target-charset')} in @@ -654,22 +665,22 @@ threads, you must be careful to only call @value{GDBN}-specific functions in the @value{GDBN} thread. @value{GDBN} provides some functions to help with this. -@defun gdb.block_signals () +@defun gdb.blocked_signals () As mentioned earlier (@pxref{Basic Python}), certain signals must be -delivered to the @value{GDBN} main thread. The @code{block_signals} +delivered to the @value{GDBN} main thread. The @code{blocked_signals} function returns a context manager that will block these signals on entry. This can be used when starting a new thread to ensure that the signals are blocked there, like: @smallexample -with gdb.block_signals(): +with gdb.blocked_signals(): start_new_thread() @end smallexample @end defun @deftp {class} gdb.Thread This is a subclass of Python's @code{threading.Thread} class. It -overrides the @code{start} method to call @code{block_signals}, making +overrides the @code{start} method to call @code{blocked_signals}, making this an easy-to-use drop-in replacement for creating threads that will work well in @value{GDBN}. @end deftp @@ -4525,6 +4536,7 @@ You can implement new @value{GDBN} CLI commands in Python. A CLI command is implemented using an instance of the @code{gdb.Command} class, most commonly using a subclass. +@anchor{Command.__init__} @defun Command.__init__ (name, command_class @r{[}, completer_class @r{[}, prefix@r{]]}) The object initializer for @code{Command} registers the new command with @value{GDBN}. This initializer is normally invoked from the @@ -4558,6 +4570,7 @@ documentation string is provided, the default value @samp{This command is not documented.} is used. @end defun +@anchor{Command.dont_repeat} @cindex don't repeat Python command @defun Command.dont_repeat () By default, a @value{GDBN} command is repeated when the user enters a @@ -4568,6 +4581,7 @@ exception). This is similar to the user command @code{dont-repeat}, see @ref{Define, dont-repeat}. @end defun +@anchor{Command.invoke} @defun Command.invoke (argument, from_tty) This method is called by @value{GDBN} when this command is invoked. @@ -4581,6 +4595,17 @@ that the command came from elsewhere. If this method throws an exception, it is turned into a @value{GDBN} @code{error} call. Otherwise, the return value is ignored. +For non-prefix commands (@pxref{Command.__init__}), the @code{invoke} +method is required. For prefix commands the @code{invoke} method is +optional. Only prefix commands that need to handle unknown +sub-commands should implement the @code{invoke} method. + +For prefix commands that don't implement @code{invoke}, if the prefix +command is used without a sub-command name then @value{GDBN} will +display the help text for every sub-command, unless the prefix command +is a @kbd{show} sub-command, in which case @value{GDBN} will list the +values of all sub-commands. + @findex gdb.string_to_argv To break @var{argument} up into an argv-like string use @code{gdb.string_to_argv}. This function behaves identically to @@ -5079,7 +5104,9 @@ string from the parameter's class, if there is one. If there is no documentation string, a default value is used. The documentation string is included in the output of the parameters @code{help set} and @code{help show} commands, and should be written taking this into -account. +account. If the documentation string for the parameter's class is the +empty string then @value{GDBN} will only use @code{Parameter.set_doc} +or @code{Parameter.show_doc} (see below) in the @kbd{help} output. @end defun @defvar Parameter.set_doc @@ -5258,6 +5285,99 @@ constants provided when the parameter is created. The value is @code{gdb.Color} instance. @end table +When creating multiple new parameters using @code{gdb.Parameter}, it +is often desirable to create a prefix command that can be used to +group related parameters together, for example, if you wished to add +the parameters @kbd{plugin-name feature-1} and @kbd{plugin-name +feature-2}, then the @kbd{plugin-name} would need to be a prefix +command (@pxref{CLI Commands In Python}). + +However, when creating parameters, you will almost always need to +create two prefix commands, one as a @kbd{set} sub-command, and one as +a @kbd{show} sub-command. @value{GDBN} provides the +@code{gdb.ParameterPrefix} helper class to make creation of these two +prefixes easier. + +@defun ParameterPrefix.__init__ (name, command_class, doc = @code{None}) +The object initializer for @code{ParameterPrefix} registers two new +@code{gdb.Command} prefixes, one as a @kbd{set} sub-command, and the +other as a @kbd{show} sub-command. + +@var{name}, a string, is the name of the new prefix, without either +@kbd{set} or @kbd{show}, similar to the @var{name} passed to +@code{gdb.Parameter} (@pxref{Parameters In Python}). For example, to +create the prefixes @kbd{set plugin-name} and @kbd{show plugin-name}, +you would pass the string @kbd{plugin-name}. + +@var{command_class} should be one of the @samp{COMMAND_} constants +(@pxref{CLI Commands In Python}). This argument tells @value{GDBN} how to +categorize the new parameter prefixes in the help system. + +There are a number of ways in which the help text for the two new +prefix commands can be provided. If the @var{doc} parameter is not +@code{None}, then this will be used as the documentation string for +both prefix commands. + +If @var{doc} is @code{None}, but @code{gdb.ParameterPrefix} has been +sub-classed, then the prefix command documentation will be taken from +sub-classes documentation string (i.e., the @code{__doc__} attribute). + +If @var{doc} is @code{None}, and there is no @code{__doc__} string, +then the default value @samp{This command is not documented.} is used. + +When writing the help text, keep in mind that the same text is used +for both the @kbd{set} and @kbd{show} prefix commands. +@end defun + +@defun ParameterPrefix.invoke_set (argument, from_tty) +If a sub-class defines this method, then @value{GDBN} will call this +when the prefix command is used with an unknown sub-command. The +@var{argument} and @var{from_tty} parameters are the same as for +@code{gdb.Command.invoke} (@pxref{Command.invoke}). + +If this method throws an exception, it is turned into a @value{GDBN} +@code{error} call. Otherwise, the return value is ignored. + +It is not required that a @code{ParameterPrefix} sub-class override +this method. Usually, a parameter prefix only exists as a means to +group related parameters together. @value{GDBN} handles this use case +automatically with no need to implement @code{invoke_set}. +@end defun + +@defun ParameterPrefix.invoke_show (argument, from_tty) +This is like the @code{invoke_set} method, but for the @kbd{show} +prefix command. As with @code{invoke_set}, implementation of this +method is optional, and usually not required. +@end defun + +@cindex don't repeat Python command +@defun ParameterPrefix.dont_repeat () +Like @code{Command.dont_repeat} (@pxref{Command.dont_repeat}), this +can be called from @code{ParameterPrefix.invoke_set} or +@code{ParameterPrefix.invoke_show} to prevent the prefix commands from +being repeated. +@end defun + +Here is a small example that uses @code{gdb.ParameterPrefix} along +with @code{gdb.Parameter} to create two new parameters +@kbd{plugin-name feature-1} and @kbd{plugin-name feature-2}. As +neither @code{invoke_set} or @code{invoke_show} is needed, this +example does not sub-class @code{gdb.ParameterPrefix}: + +@smallexample +class ExampleParam(gdb.Parameter): + def __init__ (self, name): + super ().__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN) + self.value = True + +gdb.ParameterPrefix("plugin-name", gdb.COMMAND_NONE, + """An example parameter prefix. + + This groups together some parameters.""") +ExampleParam("plugin-name feature-1") +ExampleParam("plugin-name feature-2") +@end smallexample + @node Functions In Python @subsubsection Writing new convenience functions @@ -7101,6 +7221,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/dtrace-probe.c b/gdb/dtrace-probe.c index fa60c34..c663571 100644 --- a/gdb/dtrace-probe.c +++ b/gdb/dtrace-probe.c @@ -889,9 +889,7 @@ info_probes_dtrace_command (const char *arg, int from_tty) info_probes_for_spops (arg, from_tty, &dtrace_static_probe_ops); } -void _initialize_dtrace_probe (); -void -_initialize_dtrace_probe () +INIT_GDB_FILE (dtrace_probe) { all_static_probe_ops.push_back (&dtrace_static_probe_ops); diff --git a/gdb/dummy-frame.c b/gdb/dummy-frame.c index f8440b3..445a7b4 100644 --- a/gdb/dummy-frame.c +++ b/gdb/dummy-frame.c @@ -425,9 +425,7 @@ maintenance_print_dummy_frames (const char *args, int from_tty) } } -void _initialize_dummy_frame (); -void -_initialize_dummy_frame () +INIT_GDB_FILE (dummy_frame) { add_cmd ("dummy-frames", class_maintenance, maintenance_print_dummy_frames, _("Print the contents of the internal dummy-frame stack."), diff --git a/gdb/dwarf2/attribute.c b/gdb/dwarf2/attribute.c index 2d14ebd..d2b5364 100644 --- a/gdb/dwarf2/attribute.c +++ b/gdb/dwarf2/attribute.c @@ -186,6 +186,52 @@ attribute::unsigned_constant () const /* See attribute.h. */ +std::optional<LONGEST> +attribute::signed_constant () const +{ + if (form_is_strictly_signed ()) + return u.snd; + + switch (form) + { + case DW_FORM_data8: + case DW_FORM_udata: + /* Not sure if DW_FORM_udata should be handled or not. Anyway + for DW_FORM_data8, there's no need to sign-extend. */ + return u.snd; + + case DW_FORM_data1: + return sign_extend (u.unsnd, 8); + case DW_FORM_data2: + return sign_extend (u.unsnd, 16); + case DW_FORM_data4: + return sign_extend (u.unsnd, 32); + } + + /* For DW_FORM_data16 see attribute::form_is_constant. */ + complaint (_("Attribute value is not a constant (%s)"), + dwarf_form_name (form)); + return {}; +} + +/* See attribute.h. */ + +std::optional<LONGEST> +attribute::confused_constant () const +{ + if (form_is_strictly_signed ()) + return u.snd; + else if (form_is_constant ()) + return u.unsnd; + + /* For DW_FORM_data16 see attribute::form_is_constant. */ + complaint (_("Attribute value is not a constant (%s)"), + dwarf_form_name (form)); + return {}; +} + +/* See attribute.h. */ + bool attribute::form_is_unsigned () const { @@ -296,5 +342,8 @@ attribute::as_boolean () const return true; else if (form == DW_FORM_flag) return u.unsnd != 0; - return constant_value (0) != 0; + /* Using signed_constant here will work even for the weird case + where a negative value is provided. Probably doesn't matter but + also seems harmless. */ + return signed_constant ().value_or (0) != 0; } diff --git a/gdb/dwarf2/attribute.h b/gdb/dwarf2/attribute.h index ec4f3d8..234de4e 100644 --- a/gdb/dwarf2/attribute.h +++ b/gdb/dwarf2/attribute.h @@ -114,6 +114,34 @@ struct attribute returned. */ std::optional<ULONGEST> unsigned_constant () const; + /* Return a signed constant value. This only handles constant forms + (i.e., form_is_constant -- and not the extended list of + "unsigned" forms) and assumes a signed value is desired. This + function will sign-extend DW_FORM_data* values. + + If non-constant form is used, then complaint is issued and an + empty value is returned. */ + std::optional<LONGEST> signed_constant () const; + + /* Return a signed constant value. However, for narrow forms like + DW_FORM_data1, sign extension is not done. + + DWARF advises compilers to generally use DW_FORM_[su]data to + avoid ambiguity. However, both GCC and LLVM ignore this for + certain attributes. Furthermore in DWARF, whether a narrower + form causes sign-extension depends on the attribute -- for + attributes that can only assume non-negative values, sign + extension is not done. + + Unfortunately, both compilers also emit certain attributes in a + "confused" way, using DW_FORM_sdata for signed values, and + possibly choosing a narrow form (e.g., DW_FORM_data1) otherwise + -- assuming that sign-extension will not be done. + + This method should only be called when this "confused" treatment + is necessary. */ + std::optional<LONGEST> confused_constant () const; + /* Return non-zero if ATTR's value falls in the 'constant' class, or zero otherwise. When this function returns true, you can apply the constant_value method to it. @@ -166,6 +194,15 @@ struct attribute false. */ bool form_is_strictly_signed () const; + /* Check if the attribute's form is an unsigned constant form. This + only returns true for forms that are strictly unsigned -- that + is, for a context-dependent form like DW_FORM_data1, this returns + false. */ + bool form_is_strictly_unsigned () const + { + return form == DW_FORM_udata; + } + /* Check if the attribute's form is a form that requires "reprocessing". */ bool form_requires_reprocessing () const; diff --git a/gdb/dwarf2/call-site.h b/gdb/dwarf2/call-site.h index e02cc5e..2cc4883 100644 --- a/gdb/dwarf2/call-site.h +++ b/gdb/dwarf2/call-site.h @@ -198,7 +198,7 @@ struct call_site struct call_site *tail_call_next = nullptr; /* * Describe DW_AT_call_target. Missing attribute uses - FIELD_LOC_KIND_DWARF_BLOCK with FIELD_DWARF_BLOCK == NULL. */ + m_loc_kind == DWARF_BLOCK with m_loc.dwarf_block == nullptr. */ struct call_site_target target {}; diff --git a/gdb/dwarf2/cooked-index-entry.c b/gdb/dwarf2/cooked-index-entry.c index 52db851..863ddd6 100644 --- a/gdb/dwarf2/cooked-index-entry.c +++ b/gdb/dwarf2/cooked-index-entry.c @@ -33,6 +33,7 @@ to_string (cooked_index_flag flags) MAP_ENUM_FLAG (IS_LINKAGE), MAP_ENUM_FLAG (IS_TYPE_DECLARATION), MAP_ENUM_FLAG (IS_PARENT_DEFERRED), + MAP_ENUM_FLAG (IS_SYNTHESIZED), }; return flags.to_string (mapping); @@ -233,8 +234,7 @@ cooked_index_entry::write_scope (struct obstack *storage, obstack_grow (storage, sep, strlen (sep)); } -void _initialize_dwarf2_entry (); -void _initialize_dwarf2_entry () +INIT_GDB_FILE (dwarf2_entry) { #if GDB_SELF_TEST selftests::register_test ("cooked_index_entry::compare", test_compare); diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c index da51a8c..09d80ef 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", m_per_command_time); + + 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-worker.h b/gdb/dwarf2/cooked-index-worker.h index df5c31d..8b9766c 100644 --- a/gdb/dwarf2/cooked-index-worker.h +++ b/gdb/dwarf2/cooked-index-worker.h @@ -25,6 +25,8 @@ #include "dwarf2/cooked-index-shard.h" #include "dwarf2/types.h" #include "dwarf2/read.h" +#include "maint.h" +#include "run-on-main-thread.h" #if CXX_STD_THREAD #include <mutex> @@ -107,11 +109,20 @@ public: return &m_parent_map; } - /* Add an exception to the list of exceptions caught while reading. - These are passed forward and printed by the main thread. */ - void note_error (gdb_exception &&except) + /* Catch exceptions from calling F (), and add them to the list of caught + exceptions. These are passed forward and printed by the main thread. */ + template <typename F> + void + catch_error (F &&f) { - m_exceptions.push_back (std::move (except)); + try + { + f (); + } + catch (gdb_exception &ex) + { + m_exceptions.push_back (std::move (ex)); + } } /* Called when the thread using this object is done with its work. @@ -207,8 +218,12 @@ public: explicit cooked_index_worker (dwarf2_per_objfile *per_objfile) : m_per_objfile (per_objfile), - m_cache_store (global_index_cache, per_objfile->per_bfd) - { } + m_cache_store (global_index_cache, per_objfile->per_bfd), + m_per_command_time (per_command_time) + { + /* Make sure we capture per_command_time from the main thread. */ + gdb_assert (is_main_thread ()); + } virtual ~cooked_index_worker () { } DISABLE_COPY_AND_ASSIGN (cooked_index_worker); @@ -298,6 +313,9 @@ protected: std::optional<gdb_exception> m_failed; /* An object used to write to the index cache. */ index_cache_store_context m_cache_store; + + /* Captured value of per_command_time. */ + bool m_per_command_time; }; using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>; diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c index 7948ffa..6209590 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,12 @@ 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", + m_state->m_per_command_time); + this_shard->finalize (parent_maps); + }); } finalizers.start (); @@ -321,9 +327,7 @@ maintenance_wait_for_index_cache (const char *args, int from_tty) wait_for_index_cache (0); } -void _initialize_cooked_index (); -void -_initialize_cooked_index () +INIT_GDB_FILE (cooked_index) { add_cmd ("wait-for-index-cache", class_maintenance, maintenance_wait_for_index_cache, _("\ 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..69f396c 100644 --- a/gdb/dwarf2/cu.h +++ b/gdb/dwarf2/cu.h @@ -21,7 +21,7 @@ #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" @@ -99,8 +99,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; @@ -360,7 +369,7 @@ public: right place. And since the DW_TAG_compile_unit DIE in the split-unit can't have a DW_AT_ranges attribute, we can use the - die->tag != DW_AT_compile_unit + die->tag != DW_TAG_compile_unit to determine whether the base should be added or not. */ ULONGEST gnu_ranges_base = 0; diff --git a/gdb/dwarf2/dwz.c b/gdb/dwarf2/dwz.c index 583103b..1aa0d03 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); + } } } @@ -341,7 +343,7 @@ dwz_file::read_dwz_file (dwarf2_per_objfile *per_objfile) { gdb::unique_xmalloc_ptr<char> abs = gdb_realpath (per_bfd->filename ()); - filename = ldirname (abs.get ()) + SLASH_STRING + filename; + filename = gdb_ldirname (abs.get ()) + SLASH_STRING + filename; } /* First try the file name given in the section. If that doesn't @@ -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/frame-tailcall.c b/gdb/dwarf2/frame-tailcall.c index 6464ffb..e31ae10 100644 --- a/gdb/dwarf2/frame-tailcall.c +++ b/gdb/dwarf2/frame-tailcall.c @@ -478,9 +478,7 @@ const struct frame_unwind_legacy dwarf2_tailcall_frame_unwind ( tailcall_frame_prev_arch ); -void _initialize_tailcall_frame (); -void -_initialize_tailcall_frame () +INIT_GDB_FILE (tailcall_frame) { cache_htab = htab_create_alloc (50, cache_hash, cache_eq, NULL, xcalloc, xfree); diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c index 1b4de0d..7a37285 100644 --- a/gdb/dwarf2/frame.c +++ b/gdb/dwarf2/frame.c @@ -2247,9 +2247,7 @@ dwarf2_build_frame_info (struct objfile *objfile) set_comp_unit (objfile, unit.release ()); } -void _initialize_dwarf2_frame (); -void -_initialize_dwarf2_frame () +INIT_GDB_FILE (dwarf2_frame) { #if GDB_SELF_TEST selftests::register_test_foreach_arch ("execute_cfa_program", diff --git a/gdb/dwarf2/frame.h b/gdb/dwarf2/frame.h index f8fd8e8..9357cc1 100644 --- a/gdb/dwarf2/frame.h +++ b/gdb/dwarf2/frame.h @@ -198,6 +198,15 @@ struct dwarf2_frame_state bool armcc_cfa_offsets_reversed = false; }; +/* If DWARF supoprt was requested, create the real prototype for the + append_unwinders function. Otherwise, create a fake inline function. + + There is no need to emit a warning for some of these, because they aren't + actively reading DWARF when this is called, they're just initializing GDB. + + These should probably be moved to dwarf2/public.h. */ +#if defined(DWARF_FORMAT_AVAILABLE) + /* Set the architecture-specific register state initialization function for GDBARCH to INIT_REG. */ @@ -287,4 +296,56 @@ extern void *dwarf2_frame_get_fn_data (const frame_info_ptr &this_frame, void **this_cache, fn_prev_register cookie); +#else /* DWARF_FORMAT_AVAILABLE */ + +static inline void dwarf2_append_unwinders (struct gdbarch *gdbarch) { } + +static inline void dwarf2_frame_set_init_reg ( + gdbarch *gdbarch, void (*init_reg) (struct gdbarch *,int, + dwarf2_frame_state_reg *, + const frame_info_ptr &)) { } + +static inline const struct frame_base * + dwarf2_frame_base_sniffer (const frame_info_ptr &this_frame) +{ + warning (_("No dwarf support available.")); + return nullptr; +} + +static inline void dwarf2_frame_set_signal_frame_p + (gdbarch *gdbarch, int (*signal_frame_p) (struct gdbarch *, + const frame_info_ptr &)) { } + +static inline void *dwarf2_frame_get_fn_data (const frame_info_ptr &this_frame, + void **this_cache, + fn_prev_register cookie) +{ + return nullptr; +} + +static inline void *dwarf2_frame_allocate_fn_data + (const frame_info_ptr &this_frame, void **this_cache, + fn_prev_register cookie, unsigned long size) +{ + return nullptr; +} + +static inline int dwarf2_fetch_cfa_info (struct gdbarch *gdbarch, CORE_ADDR pc, + struct dwarf2_per_cu_data *data, + int *regnum_out, LONGEST *offset_out, + CORE_ADDR *text_offset_out, + const gdb_byte **cfa_start_out, + const gdb_byte **cfa_end_out) +{ + return 0; +} + +static inline void + dwarf2_frame_set_adjust_regnum (struct gdbarch *gdbarch, + int (*adjust_regnum) (struct gdbarch *, + int, int)) +{} + +#endif /* DWARF_FORMAT_AVAILABLE */ + #endif /* GDB_DWARF2_FRAME_H */ diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c index 1715beb..cfe8ce9 100644 --- a/gdb/dwarf2/index-cache.c +++ b/gdb/dwarf2/index-cache.c @@ -342,9 +342,7 @@ show_index_cache_stats_command (const char *arg, int from_tty) indent, global_index_cache.n_misses ()); } -void _initialize_index_cache (); -void -_initialize_index_cache () +INIT_GDB_FILE (index_cache) { /* Set the default index cache directory. */ std::string cache_dir = get_standard_cache_dir (); diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c index 614bdcd..0a3b9d0 100644 --- a/gdb/dwarf2/index-write.c +++ b/gdb/dwarf2/index-write.c @@ -1768,9 +1768,7 @@ gdb_index () } /* selftests namespace. */ #endif -void _initialize_dwarf_index_write (); -void -_initialize_dwarf_index_write () +INIT_GDB_FILE (dwarf_index_write) { #if GDB_SELF_TEST selftests::register_test ("gdb_index", selftests::gdb_index); 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..37c85d8 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; } @@ -4129,9 +4154,7 @@ const struct symbol_computed_ops dwarf2_loclist_funcs = { loclist_generate_c_location }; -void _initialize_dwarf2loc (); -void -_initialize_dwarf2loc () +INIT_GDB_FILE (dwarf2loc) { add_setshow_zuinteger_cmd ("entry-values", class_maintenance, &entry_values_debug, diff --git a/gdb/dwarf2/loc.h b/gdb/dwarf2/loc.h index 30c528b..c672320 100644 --- a/gdb/dwarf2/loc.h +++ b/gdb/dwarf2/loc.h @@ -20,6 +20,7 @@ #ifndef GDB_DWARF2_LOC_H #define GDB_DWARF2_LOC_H +#include "gdbtypes.h" #include "dwarf2/expr.h" struct symbol_computed_ops; @@ -99,31 +100,9 @@ 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 - the property is evaluated; if NULL, the selected frame (if any) is used - instead. - - ADDR_STACK is the stack of addresses that might be needed to evaluate the - property. When evaluating a property that is not related to a type, it can - be NULL. - - Returns true if PROP could be converted and the static value is passed - back into VALUE, otherwise returns false. - - Any values in PUSH_VALUES will be pushed before evaluating the location - expression, PUSH_VALUES[0] will be pushed first, then PUSH_VALUES[1], - etc. This means the during evaluation PUSH_VALUES[0] will be at the - bottom of the stack. */ - -bool dwarf2_evaluate_property (const struct dynamic_prop *prop, - const frame_info_ptr &frame, - const property_addr_info *addr_stack, - CORE_ADDR *value, - gdb::array_view<CORE_ADDR> push_values = {}); - /* A helper for the compiler interface that compiles a single dynamic property to C code. @@ -167,6 +146,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 +157,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 +201,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 +223,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; }; }; @@ -307,8 +289,53 @@ extern struct value *indirect_synthetic_pointer Function always returns non-NULL value. It throws NO_ENTRY_VALUE_ERROR if it cannot resolve the parameter for any reason. */ +#if defined(DWARF_FORMAT_AVAILABLE) + +/* Converts a dynamic property into a static one. FRAME is the frame in which + the property is evaluated; if NULL, the selected frame (if any) is used + instead. + + ADDR_STACK is the stack of addresses that might be needed to evaluate the + property. When evaluating a property that is not related to a type, it can + be NULL. + + Returns true if PROP could be converted and the static value is passed + back into VALUE, otherwise returns false. + + Any values in PUSH_VALUES will be pushed before evaluating the location + expression, PUSH_VALUES[0] will be pushed first, then PUSH_VALUES[1], + etc. This means the during evaluation PUSH_VALUES[0] will be at the + bottom of the stack. */ + +bool dwarf2_evaluate_property (const struct dynamic_prop *prop, + const frame_info_ptr &frame, + const property_addr_info *addr_stack, + CORE_ADDR *value, + gdb::array_view<CORE_ADDR> push_values = {}); + extern struct value *value_of_dwarf_reg_entry (struct type *type, const frame_info_ptr &frame, enum call_site_parameter_kind kind, union call_site_parameter_u kind_u); + +#else /* DWARF_FORMAT_AVAILABLE */ + +static inline bool +dwarf2_evaluate_property (const struct dynamic_prop *, const frame_info_ptr &, + const property_addr_info *, CORE_ADDR *, + gdb::array_view<CORE_ADDR> = {}) +{ + return false; +} + +static inline struct value * +value_of_dwarf_reg_entry (struct type *type, const frame_info_ptr &frame, + enum call_site_parameter_kind kind, + union call_site_parameter_u kind_u) +{ + error (_("No dwarf support available.")); +} + +#endif /* DWARF_FORMAT_AVAILABLE */ + #endif /* GDB_DWARF2_LOC_H */ diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h index ed504c6..f9e7488 100644 --- a/gdb/dwarf2/public.h +++ b/gdb/dwarf2/public.h @@ -30,6 +30,8 @@ enum class dw_index_kind DEBUG_NAMES, }; +#if defined(DWARF_FORMAT_AVAILABLE) + /* Try to locate the sections we need for DWARF 2 debugging information. If these are found, begin reading the DWARF and return true. Otherwise, return false. NAMES points to the dwarf2 @@ -44,4 +46,27 @@ extern bool dwarf2_initialize_objfile extern void dwarf2_build_frame_info (struct objfile *); +/* Append the DWARF-2 frame unwinders to GDBARCH's list. */ + +void dwarf2_append_unwinders (struct gdbarch *gdbarch); + +#else /* DWARF_FORMAT_AVAILABLE */ + +static inline bool +dwarf2_initialize_objfile (struct objfile *, + const struct dwarf2_debug_sections * = nullptr, + bool = false) +{ + warning (_("No dwarf support available.")); + return false; +} + +static inline void +dwarf2_build_frame_info (struct objfile *) +{ + warning (_("No dwarf support available.")); +} + +#endif /* DWARF_FORMAT_AVAILABLE */ + #endif /* GDB_DWARF2_PUBLIC_H */ diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c index 11de986..4b3f385 100644 --- a/gdb/dwarf2/read-debug-names.c +++ b/gdb/dwarf2/read-debug-names.c @@ -241,7 +241,7 @@ mapped_debug_names_reader::scan_one_entry (const char *name, continue; } } - per_cu = per_objfile->per_bfd->get_cu (ull); + per_cu = per_objfile->per_bfd->get_unit (ull); break; case DW_IDX_type_unit: /* Don't crash on bad data. */ @@ -255,7 +255,7 @@ mapped_debug_names_reader::scan_one_entry (const char *name, } { int nr_cus = per_objfile->per_bfd->all_comp_units.size (); - per_cu = per_objfile->per_bfd->get_cu (nr_cus + ull); + per_cu = per_objfile->per_bfd->get_unit (nr_cus + ull); } break; case DW_IDX_die_offset: @@ -263,7 +263,7 @@ mapped_debug_names_reader::scan_one_entry (const char *name, /* In a per-CU index (as opposed to a per-module index), index entries without CU attribute implicitly refer to the single CU. */ if (per_cu == NULL) - per_cu = per_objfile->per_bfd->get_cu (0); + per_cu = per_objfile->per_bfd->get_unit (0); break; case DW_IDX_parent: parent = ull; @@ -416,15 +416,11 @@ cooked_index_worker_debug_names::do_reading () { complaint_interceptor complaint_handler; - try + /* Arbitrarily put all exceptions into the first result. */ + m_map.indices[0].catch_error ([&] () { m_map.scan_all_names (); - } - catch (gdb_exception &exc) - { - /* Arbitrarily put all exceptions into the first result. */ - m_map.indices[0].note_error (std::move (exc)); - } + }); bool first = true; for (auto &iter : m_map.indices) @@ -470,7 +466,7 @@ check_signatured_type_table_from_debug_names bool found = false; for (; j < nr_cus_tus; j++) - if (per_bfd->get_cu (j)->sect_off == sect_off) + if (per_bfd->get_unit (j)->sect_off == sect_off) { found = true; break; @@ -481,7 +477,7 @@ check_signatured_type_table_from_debug_names " ignoring .debug_names.")); return false; } - per_bfd->all_comp_units_index_tus.push_back (per_bfd->get_cu (j)); + per_bfd->all_comp_units_index_tus.push_back (per_bfd->get_unit (j)); } return true; } @@ -723,7 +719,7 @@ check_cus_from_debug_names_list (dwarf2_per_bfd *per_bfd, map.dwarf5_byte_order)); bool found = false; for (; j < nr_cus; j++) - if (per_bfd->get_cu (j)->sect_off == sect_off) + if (per_bfd->get_unit (j)->sect_off == sect_off) { found = true; break; @@ -734,7 +730,7 @@ check_cus_from_debug_names_list (dwarf2_per_bfd *per_bfd, " ignoring .debug_names.")); return false; } - per_bfd->all_comp_units_index_cus.push_back (per_bfd->get_cu (j)); + per_bfd->all_comp_units_index_cus.push_back (per_bfd->get_unit (j)); } return true; } @@ -753,7 +749,7 @@ check_cus_from_debug_names_list (dwarf2_per_bfd *per_bfd, (map.cu_table_reordered + i * map.offset_size, map.offset_size, map.dwarf5_byte_order)); - if (sect_off != per_bfd->get_cu (i)->sect_off) + if (sect_off != per_bfd->get_unit (i)->sect_off) { warning (_("Section .debug_names has incorrect entry in CU table," " ignoring .debug_names.")); diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c index 2029c3e..464fbdd 100644 --- a/gdb/dwarf2/read-gdb-index.c +++ b/gdb/dwarf2/read-gdb-index.c @@ -1113,7 +1113,7 @@ dw2_expand_marked_cus (dwarf2_per_objfile *per_objfile, offset_type idx, continue; } - dwarf2_per_cu *per_cu = per_objfile->per_bfd->get_cu (cu_index); + dwarf2_per_cu *per_cu = per_objfile->per_bfd->get_unit (cu_index); if (!dw2_expand_symtabs_matching_one (per_cu, per_objfile, file_matcher, expansion_notify, lang_matcher)) @@ -1426,7 +1426,7 @@ create_addrmap_from_gdb_index (dwarf2_per_objfile *per_objfile, continue; } - mutable_map.set_empty (lo, hi - 1, per_bfd->get_cu (cu_index)); + mutable_map.set_empty (lo, hi - 1, per_bfd->get_unit (cu_index)); } index->index_addrmap @@ -1562,10 +1562,7 @@ dwarf2_read_gdb_index return true; } -void _initialize_read_gdb_index (); - -void -_initialize_read_gdb_index () +INIT_GDB_FILE (read_gdb_index) { add_setshow_boolean_cmd ("use-deprecated-index-sections", no_class, &use_deprecated_index_sections, _("\ diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 7b019f9..5e18e45 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; }; @@ -288,24 +289,29 @@ struct dwo_sections struct dwo_unit { /* Backlink to the containing struct dwo_file. */ - struct dwo_file *dwo_file; + struct dwo_file *dwo_file = nullptr; /* The "id" that distinguishes this CU/TU. .debug_info calls this "dwo_id", .debug_types calls this "signature". Since signatures came first, we stick with it for consistency. */ - ULONGEST signature; + ULONGEST signature = 0; /* The section this CU/TU lives in, in the DWO file. */ - struct dwarf2_section_info *section; + dwarf2_section_info *section = nullptr; + + /* This is set if SECTION is owned by this dwo_unit. */ + dwarf2_section_info_up section_holder; /* Same as dwarf2_per_cu::{sect_off,length} but in the DWO section. */ - sect_offset sect_off; - unsigned int length; + sect_offset sect_off {}; + unsigned int length = 0; /* For types, offset in the type's DIE of the type defined by this TU. */ cu_offset type_offset_in_tu; }; +using dwo_unit_up = std::unique_ptr<dwo_unit>; + /* Hash function for dwo_unit objects, based on the signature. */ struct dwo_unit_hash @@ -315,7 +321,7 @@ struct dwo_unit_hash std::size_t operator() (ULONGEST signature) const noexcept { return signature; } - std::size_t operator() (const dwo_unit *unit) const noexcept + std::size_t operator() (const dwo_unit_up &unit) const noexcept { return (*this) (unit->signature); } }; @@ -329,16 +335,16 @@ struct dwo_unit_eq { using is_transparent = void; - bool operator() (ULONGEST sig, const dwo_unit *unit) const noexcept + bool operator() (ULONGEST sig, const dwo_unit_up &unit) const noexcept { return sig == unit->signature; } - bool operator() (const dwo_unit *a, const dwo_unit *b) const noexcept + bool operator() (const dwo_unit_up &a, const dwo_unit_up &b) const noexcept { return (*this) (a->signature, b); } }; /* Set of dwo_unit object, using their signature as identity. */ -using dwo_unit_set = gdb::unordered_set<dwo_unit *, dwo_unit_hash, dwo_unit_eq>; +using dwo_unit_set = gdb::unordered_set<dwo_unit_up, dwo_unit_hash, dwo_unit_eq>; /* include/dwarf2.h defines the DWP section codes. It defines a max value but it doesn't define a min value, which we @@ -598,6 +604,11 @@ struct dwp_file dwo_unit_set loaded_cus; dwo_unit_set loaded_tus; +#if CXX_STD_THREAD + /* Mutex to synchronize access to LOADED_CUS and LOADED_TUS. */ + std::mutex loaded_cutus_lock; +#endif + /* Table to map ELF section numbers to their sections. This is only needed for the DWP V1 file format. */ unsigned int num_sections = 0; @@ -622,15 +633,21 @@ struct variant_field /* A variant can contain other variant parts. */ std::vector<variant_part_builder> variant_parts; - /* If we see a DW_TAG_variant, then this will be set if this is the - default branch. */ - bool default_branch = false; /* If we see a DW_AT_discr_value, then this will be the discriminant - value. */ - ULONGEST discriminant_value = 0; + value. Just the attribute is stored here, because we have to + defer deciding whether the value is signed or unsigned until the + end. */ + const attribute *discriminant_attr = nullptr; /* If we see a DW_AT_discr_list, then this is a pointer to the list data. */ struct dwarf_block *discr_list_data = nullptr; + + /* If both DW_AT_discr_value and DW_AT_discr_list are absent, then + this is the default branch. */ + bool is_default () const + { + return discriminant_attr == nullptr && discr_list_data == nullptr; + } }; /* This represents a DW_TAG_variant_part. */ @@ -727,9 +744,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); @@ -742,9 +756,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 *, @@ -1318,7 +1332,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)) @@ -1887,13 +1901,13 @@ dwarf2_base_index_functions::print_stats (struct objfile *objfile, for (int i = 0; i < total; ++i) { - dwarf2_per_cu *per_cu = per_objfile->per_bfd->get_cu (i); + dwarf2_per_cu *per_cu = per_objfile->per_bfd->get_unit (i); if (!per_objfile->symtab_set_p (per_cu)) ++count; } - gdb_printf (_(" Number of read CUs: %d\n"), total - count); - gdb_printf (_(" Number of unread CUs: %d\n"), count); + gdb_printf (_(" Number of read units: %d\n"), total - count); + gdb_printf (_(" Number of unread units: %d\n"), count); } void @@ -2385,109 +2399,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 @@ -2586,7 +2497,7 @@ lookup_dwo_signatured_type (struct dwarf2_cu *cu, ULONGEST sig) if (it == dwo_file->tus.end ()) return nullptr; - dwo_unit *dwo_entry = *it; + dwo_unit *dwo_entry = it->get (); /* If the global table doesn't have an entry for this TU, add one. */ if (sig_type_it == per_bfd->signatured_types.end ()) @@ -2789,9 +2700,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) @@ -2808,7 +2719,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. */ @@ -2817,10 +2728,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. */ @@ -2877,13 +2787,6 @@ dwo_unit * cutu_reader::lookup_dwo_unit (dwarf2_cu *cu, die_info *comp_unit_die, const char *dwo_name) { -#if CXX_STD_THREAD - /* We need a lock here to handle the DWO hash table. */ - static std::mutex dwo_lock; - - std::lock_guard<std::mutex> guard (dwo_lock); -#endif - dwarf2_per_cu *per_cu = cu->per_cu; struct dwo_unit *dwo_unit; const char *comp_dir; @@ -3038,26 +2941,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 @@ -3070,10 +2973,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 ()); @@ -3205,11 +3107,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; @@ -3283,33 +3185,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; @@ -3322,11 +3321,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; @@ -3360,26 +3357,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; @@ -3445,38 +3425,17 @@ 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); + storage->catch_error ([&] () + { + 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; @@ -3498,82 +3457,29 @@ 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", m_per_command_time); + /* 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) - for (dwo_unit *unit : file->tus) - process_skeletonless_type_unit (unit, per_objfile, storage); + for (const dwo_unit_up &unit : file->tus) + storage->catch_error ([&] () + { + process_skeletonless_type_unit (unit.get (), 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 (); }; @@ -3586,14 +3492,10 @@ cooked_index_worker_debug_info::process_cus (size_t task_number, { dwarf2_per_cu *per_cu = inner->get (); - try - { - process_psymtab_comp_unit (per_cu, m_per_objfile, &thread_storage); - } - catch (gdb_exception &except) + thread_storage.catch_error ([&] () { - thread_storage.note_error (std::move (except)); - } + process_unit (per_cu, m_per_objfile, &thread_storage); + }); } thread_storage.done_reading (complaint_handler.release ()); @@ -3618,7 +3520,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, @@ -3671,7 +3573,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", m_per_command_time); + process_units (task_count, iter, last); }); ++task_count; @@ -3688,7 +3591,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; @@ -3708,9 +3611,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 (); @@ -3723,7 +3626,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; @@ -3765,17 +3668,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 ()) { @@ -5955,7 +5858,7 @@ find_file_and_directory (struct die_info *die, struct dwarf2_cu *cu) && res.get_name () != nullptr && IS_ABSOLUTE_PATH (res.get_name ())) { - res.set_comp_dir (ldirname (res.get_name ())); + res.set_comp_dir (gdb_ldirname (res.get_name ())); res.set_name (make_unique_xstrdup (lbasename (res.get_name ()))); } @@ -6302,84 +6205,158 @@ static dwo_file * lookup_dwo_file (dwarf2_per_bfd *per_bfd, const char *dwo_name, const char *comp_dir) { +#if CXX_STD_THREAD + std::lock_guard<std::mutex> guard (per_bfd->dwo_files_lock); +#endif + auto it = per_bfd->dwo_files.find (dwo_file_search {dwo_name, comp_dir}); 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. */ +/* Add DWO_FILE to the per-BFD DWO file hash table. + + Return the dwo_file actually kept in the hash table. + + If another thread raced with this one, opening the exact same DWO file and + inserting it first in the hash table, then keep that other thread's copy + and DWO_FILE gets freed. */ + +static dwo_file * +add_dwo_file (dwarf2_per_bfd *per_bfd, dwo_file_up dwo_file) +{ +#if CXX_STD_THREAD + std::lock_guard<std::mutex> lock (per_bfd->dwo_files_lock); +#endif + + return per_bfd->dwo_files.emplace (std::move (dwo_file)).first->get (); +} 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); + auto dwo_unit = std::make_unique<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; - dwarf_read_debug_printf (" offset %s, dwo_id %s", - sect_offset_str (sect_off), - hex_string (dwo_unit->signature)); + 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 (std::move (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; + } - 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)); + case DW_UT_type: + case DW_UT_split_type: + { + dwo_unit->type_offset_in_tu = header.type_offset_in_tu; + + 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.tus.emplace (std::move (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; + } + } } } @@ -6851,7 +6828,7 @@ locate_v1_virtual_dwo_sections (asection *sectp, COMP_DIR is the DW_AT_comp_dir attribute of the referencing CU. This is for DWP version 1 files. */ -static struct dwo_unit * +static dwo_unit_up create_dwo_unit_in_dwp_v1 (dwarf2_per_bfd *per_bfd, struct dwp_file *dwp_file, uint32_t unit_index, @@ -6974,19 +6951,17 @@ create_dwo_unit_in_dwp_v1 (dwarf2_per_bfd *per_bfd, types we'll grow the vector and eventually have to reallocate space for it, invalidating all copies of pointers into the previous contents. */ - auto [it, inserted] - = per_bfd->dwo_files.emplace (std::move (new_dwo_file)); - gdb_assert (inserted); - dwo_file = it->get (); + dwo_file = add_dwo_file (per_bfd, std::move (new_dwo_file)); } else dwarf_read_debug_printf ("Using existing virtual DWO: %s", virtual_dwo_name.c_str ()); - dwo_unit *dwo_unit = OBSTACK_ZALLOC (&per_bfd->obstack, struct dwo_unit); + auto dwo_unit = std::make_unique<struct dwo_unit> (); dwo_unit->dwo_file = dwo_file; dwo_unit->signature = signature; - dwo_unit->section = XOBNEW (&per_bfd->obstack, struct dwarf2_section_info); + dwo_unit->section_holder = std::make_unique<dwarf2_section_info> (); + dwo_unit->section = dwo_unit->section_holder.get (); *dwo_unit->section = sections.info_or_types; /* dwo_unit->{offset,length,type_offset_in_tu} are set later. */ @@ -7044,7 +7019,7 @@ create_dwp_v2_or_v5_section (dwarf2_per_bfd *per_bfd, COMP_DIR is the DW_AT_comp_dir attribute of the referencing CU. This is for DWP version 2 files. */ -static struct dwo_unit * +static dwo_unit_up create_dwo_unit_in_dwp_v2 (dwarf2_per_bfd *per_bfd, struct dwp_file *dwp_file, uint32_t unit_index, @@ -7179,19 +7154,17 @@ create_dwo_unit_in_dwp_v2 (dwarf2_per_bfd *per_bfd, types we'll grow the vector and eventually have to reallocate space for it, invalidating all copies of pointers into the previous contents. */ - auto [it, inserted] - = per_bfd->dwo_files.emplace (std::move (new_dwo_file)); - gdb_assert (inserted); - dwo_file = it->get (); + dwo_file = add_dwo_file (per_bfd, std::move (new_dwo_file)); } else dwarf_read_debug_printf ("Using existing virtual DWO: %s", virtual_dwo_name.c_str ()); - dwo_unit *dwo_unit = OBSTACK_ZALLOC (&per_bfd->obstack, struct dwo_unit); + auto dwo_unit = std::make_unique<struct dwo_unit> (); dwo_unit->dwo_file = dwo_file; dwo_unit->signature = signature; - dwo_unit->section = XOBNEW (&per_bfd->obstack, struct dwarf2_section_info); + dwo_unit->section_holder = std::make_unique<dwarf2_section_info> (); + dwo_unit->section = dwo_unit->section_holder.get (); *dwo_unit->section = create_dwp_v2_or_v5_section (per_bfd, is_debug_types @@ -7209,7 +7182,7 @@ create_dwo_unit_in_dwp_v2 (dwarf2_per_bfd *per_bfd, COMP_DIR is the DW_AT_comp_dir attribute of the referencing CU. This is for DWP version 5 files. */ -static struct dwo_unit * +static dwo_unit_up create_dwo_unit_in_dwp_v5 (dwarf2_per_bfd *per_bfd, struct dwp_file *dwp_file, uint32_t unit_index, @@ -7349,16 +7322,13 @@ create_dwo_unit_in_dwp_v5 (dwarf2_per_bfd *per_bfd, types we'll grow the vector and eventually have to reallocate space for it, invalidating all copies of pointers into the previous contents. */ - auto [it, inserted] - = per_bfd->dwo_files.emplace (std::move (new_dwo_file)); - gdb_assert (inserted); - dwo_file = it->get (); + dwo_file = add_dwo_file (per_bfd, std::move (new_dwo_file)); } else dwarf_read_debug_printf ("Using existing virtual DWO: %s", virtual_dwo_name.c_str ()); - dwo_unit *dwo_unit = OBSTACK_ZALLOC (&per_bfd->obstack, struct dwo_unit); + auto dwo_unit = std::make_unique<struct dwo_unit> (); dwo_unit->dwo_file = dwo_file; dwo_unit->signature = signature; dwo_unit->section @@ -7389,9 +7359,15 @@ lookup_dwo_unit_in_dwp (dwarf2_per_bfd *per_bfd, auto &dwo_unit_set = is_debug_types ? dwp_file->loaded_tus : dwp_file->loaded_cus; - if (auto it = dwo_unit_set.find (signature); - it != dwo_unit_set.end ()) - return *it; + { +#if CXX_STD_THREAD + std::lock_guard<std::mutex> guard (dwp_file->loaded_cutus_lock); +#endif + + if (auto it = dwo_unit_set.find (signature); + it != dwo_unit_set.end ()) + return it->get (); + } /* Use a for loop so that we don't loop forever on bad debug info. */ for (unsigned int i = 0; i < dwp_htab->nr_slots; ++i) @@ -7405,7 +7381,7 @@ lookup_dwo_unit_in_dwp (dwarf2_per_bfd *per_bfd, uint32_t unit_index = read_4_bytes (dbfd, dwp_htab->unit_table + hash * sizeof (uint32_t)); - dwo_unit *dwo_unit; + dwo_unit_up dwo_unit; if (dwp_file->version == 1) dwo_unit @@ -7420,9 +7396,14 @@ lookup_dwo_unit_in_dwp (dwarf2_per_bfd *per_bfd, = create_dwo_unit_in_dwp_v5 (per_bfd, dwp_file, unit_index, comp_dir, signature, is_debug_types); - auto [it, inserted] = dwo_unit_set.emplace (dwo_unit); - gdb_assert (inserted); - return *it; + /* If another thread raced with this one, opening the exact same + DWO unit, then we'll keep that other thread's copy. */ +#if CXX_STD_THREAD + std::lock_guard<std::mutex> guard (dwp_file->loaded_cutus_lock); +#endif + + auto it = dwo_unit_set.emplace (std::move (dwo_unit)).first; + return it->get (); } if (signature_in_table == 0) @@ -7478,7 +7459,7 @@ try_open_dwop_file (dwarf2_per_bfd *per_bfd, const char *file_name, int is_dwp, search_path = per_bfd->captured_debug_dir.c_str (); /* Add the path for the executable binary to the list of search paths. */ - std::string objfile_dir = ldirname (per_bfd->filename ()); + std::string objfile_dir = gdb_ldirname (per_bfd->filename ()); search_path_holder.reset (concat (objfile_dir.c_str (), dirname_separator_string, search_path, nullptr)); @@ -7499,14 +7480,23 @@ try_open_dwop_file (dwarf2_per_bfd *per_bfd, const char *file_name, int is_dwp, if (sym_bfd == NULL) return NULL; - if (!bfd_check_format (sym_bfd.get (), bfd_object)) - return NULL; + { +#if CXX_STD_THREAD + /* The operations below are not thread-safe, use a lock to synchronize + concurrent accesses. */ + static std::mutex mutex; + std::lock_guard<std::mutex> lock (mutex); +#endif + + if (!bfd_check_format (sym_bfd.get (), bfd_object)) + return NULL; - /* Success. Record the bfd as having been included by the objfile's bfd. + /* Success. Record the bfd as having been included by the objfile's bfd. This is important because things like demangled_names_hash lives in the objfile's per_bfd space and may have references to things like symbol names that live in the DWO/DWP file's per_bfd space. PR 16426. */ - gdb_bfd_record_inclusion (per_bfd->obfd, sym_bfd.get ()); + gdb_bfd_record_inclusion (per_bfd->obfd, sym_bfd.get ()); + } return sym_bfd; } @@ -7556,47 +7546,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); + } } } @@ -7609,7 +7618,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); @@ -7625,19 +7633,13 @@ 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); + 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); @@ -7673,6 +7675,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); @@ -7718,6 +7724,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); @@ -7761,6 +7771,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); @@ -7822,7 +7836,7 @@ open_and_init_dwp_file (dwarf2_per_objfile *per_objfile) struct objfile *backlink = objfile->separate_debug_objfile_backlink; const char *backlink_basename = lbasename (backlink->original_name); - dwp_name = ldirname (objfile->original_name) + SLASH_STRING + backlink_basename; + dwp_name = gdb_ldirname (objfile->original_name) + SLASH_STRING + backlink_basename; } else dwp_name = objfile->original_name; @@ -7972,12 +7986,7 @@ cutu_reader::lookup_dwo_cutu (dwarf2_cu *cu, const char *dwo_name, = open_and_init_dwo_file (cu, dwo_name, comp_dir); if (new_dwo_file != nullptr) - { - auto [it, inserted] - = per_bfd->dwo_files.emplace (std::move (new_dwo_file)); - gdb_assert (inserted); - dwo_file = (*it).get (); - } + dwo_file = add_dwo_file (per_bfd, std::move (new_dwo_file)); } if (dwo_file != NULL) @@ -7988,13 +7997,13 @@ cutu_reader::lookup_dwo_cutu (dwarf2_cu *cu, const char *dwo_name, { if (auto it = dwo_file->tus.find (signature); it != dwo_file->tus.end ()) - dwo_cutu = *it; + dwo_cutu = it->get (); } else if (!is_debug_types && !dwo_file->cus.empty ()) { if (auto it = dwo_file->cus.find (signature); it != dwo_file->cus.end ()) - dwo_cutu = *it; + dwo_cutu = it->get (); } if (dwo_cutu != NULL) @@ -8101,8 +8110,8 @@ queue_and_load_all_dwo_tus (dwarf2_cu *cu) dwo_file = dwo_unit->dwo_file; - for (struct dwo_unit *unit : dwo_file->tus) - queue_and_load_dwo_tu (unit, cu); + for (const dwo_unit_up &unit : dwo_file->tus) + queue_and_load_dwo_tu (unit.get (), cu); } /* Read in various DIEs. */ @@ -8778,7 +8787,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; @@ -8877,7 +8887,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 @@ -9245,7 +9255,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)); @@ -9901,54 +9911,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->constant_value (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->constant_value (0); - return 1; - } - } - - return 0; -} - /* Look for DW_AT_data_member_location or DW_AT_data_bit_offset and store the results in FIELD. */ @@ -9961,9 +9923,28 @@ 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->confused_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->constant_value (0); + LONGEST offset = attr->confused_constant ().value_or (0); /* Work around this GCC 11 bug, where it would erroneously use -1 data member locations, instead of 0: @@ -9978,9 +9959,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; @@ -9990,9 +9971,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 @@ -10002,28 +9994,71 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu, dlbaton->per_objfile = per_objfile; dlbaton->per_cu = cu->per_cu; - field->set_loc_dwarf_block (dlbaton); + field->set_loc_dwarf_block_addr (dlbaton); } } else - dwarf2_complex_location_expr_complaint (); + complaint (_("Unsupported form %s for DW_AT_data_member_location"), + dwarf_form_name (attr->form)); } else { attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu); if (attr != nullptr) - field->set_loc_bitpos (attr->constant_value (0)); + { + if (attr->form_is_constant ()) + field->set_loc_bitpos (attr->unsigned_constant ().value_or (0)); + else if (attr->form_is_block ()) + { + /* This is a DWARF extension. See + https://dwarfstd.org/issues/250501.1.html. */ + dwarf2_per_objfile *per_objfile = cu->per_objfile; + dwarf2_locexpr_baton *dlbaton + = OBSTACK_ZALLOC (&per_objfile->objfile->objfile_obstack, + dwarf2_locexpr_baton); + dlbaton->data = attr->as_block ()->data; + dlbaton->size = attr->as_block ()->size; + dlbaton->per_objfile = per_objfile; + dlbaton->per_cu = cu->per_cu; + + field->set_loc_dwarf_block_bitpos (dlbaton); + } + else + complaint (_("Unsupported form %s for DW_AT_data_bit_offset"), + dwarf_form_name (attr->form)); + } } } +/* A helper that computes the location of a field. The CU and the + DW_TAG_member DIE are passed in. The results are stored in + *FP. */ + +static void +compute_field_location (dwarf2_cu *cu, die_info *die, field *fp) +{ + /* Get type of field. */ + fp->set_type (die_type (die, cu)); + + fp->set_loc_bitpos (0); + + /* Get bit size of field (zero if none). */ + attribute *attr = dwarf2_attr (die, DW_AT_bit_size, cu); + if (attr != nullptr) + fp->set_bitsize (attr->unsigned_constant ().value_or (0)); + else + fp->set_bitsize (0); + + /* Get bit offset of field. */ + handle_member_location (die, cu, fp); +} + /* Add an aggregate field to the field list. */ 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; @@ -10073,64 +10108,7 @@ dwarf2_add_field (struct field_info *fip, struct die_info *die, } /* Data member other than a C++ static data member. */ - /* Get type of field. */ - fp->set_type (die_type (die, cu)); - - fp->set_loc_bitpos (0); - - /* Get bit size of field (zero if none). */ - attr = dwarf2_attr (die, DW_AT_bit_size, cu); - if (attr != nullptr) - fp->set_bitsize (attr->constant_value (0)); - else - fp->set_bitsize (0); - - /* 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 ()) - { - 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 () + attr->constant_value (0)); - } - 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; - int bit_offset = attr->constant_value (0); - - 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->constant_value (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 ()); - } - } + compute_field_location (cu, die, fp); /* Get name of field. */ fieldname = dwarf2_name (die, cu); @@ -10269,13 +10247,19 @@ convert_variant_range (struct obstack *obstack, const variant_field &variant, { std::vector<discriminant_range> ranges; - if (variant.default_branch) + if (variant.is_default ()) return {}; if (variant.discr_list_data == nullptr) { - discriminant_range r - = {variant.discriminant_value, variant.discriminant_value}; + ULONGEST value; + + if (is_unsigned) + value = variant.discriminant_attr->unsigned_constant ().value_or (0); + else + value = variant.discriminant_attr->signed_constant ().value_or (0); + + discriminant_range r = { value, value }; ranges.push_back (r); } else @@ -11089,7 +11073,7 @@ read_structure_type (struct die_info *die, struct dwarf2_cu *cu) if (attr != nullptr) { if (attr->form_is_constant ()) - type->set_length (attr->constant_value (0)); + type->set_length (attr->unsigned_constant ().value_or (0)); else { struct dynamic_prop prop; @@ -11234,12 +11218,14 @@ handle_variant (struct die_info *die, struct type *type, { discr = dwarf2_attr (die, DW_AT_discr_list, cu); if (discr == nullptr || discr->as_block ()->size == 0) - variant.default_branch = true; + { + /* Nothing to do here -- default branch. */ + } else variant.discr_list_data = discr->as_block (); } else - variant.discriminant_value = discr->constant_value (0); + variant.discriminant_attr = discr; for (die_info *variant_child : die->children ()) handle_struct_member_die (variant_child, type, fi, template_args, cu); @@ -11301,6 +11287,21 @@ 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. DIE is the field's DIE. The + baton's "field" member is filled in, but the other members of the + baton are not. The new property baton is returned. */ + +static dwarf2_property_baton * +find_field_create_baton (dwarf2_cu *cu, die_info *die) +{ + dwarf2_property_baton *result + = XOBNEW (&cu->per_objfile->objfile->objfile_obstack, + struct dwarf2_property_baton); + memset (&result->field, 0, sizeof (result->field)); + compute_field_location (cu, die, &result->field); + return result; +} + /* 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 @@ -11565,25 +11566,30 @@ die_byte_order (die_info *die, dwarf2_cu *cu, enum bfd_endian *byte_order) /* Assuming DIE is an enumeration type, and TYPE is its associated type, update TYPE using some information only available in DIE's - children. In particular, the fields are computed. */ + children. In particular, the fields are computed. If IS_UNSIGNED + is set, the enumeration type's sign is already known (a true value + means unsigned), and so examining the constants to determine the + sign isn't needed; when this is unset, the enumerator constants are + read as signed values. */ static void update_enumeration_type_from_children (struct die_info *die, struct type *type, - struct dwarf2_cu *cu) + struct dwarf2_cu *cu, + std::optional<bool> is_unsigned) { - int unsigned_enum = 1; - int flag_enum = 1; + /* This is used to check whether the enum is signed or unsigned; for + simplicity, it is always correct regardless of whether + IS_UNSIGNED is set. */ + bool unsigned_enum = is_unsigned.value_or (true); + bool flag_enum = true; - auto_obstack obstack; std::vector<struct field> fields; for (die_info *child_die : die->children ()) { struct attribute *attr; LONGEST value; - const gdb_byte *bytes; - struct dwarf2_locexpr_baton *baton; const char *name; if (child_die->tag != DW_TAG_enumerator) @@ -11597,19 +11603,26 @@ update_enumeration_type_from_children (struct die_info *die, if (name == NULL) name = "<anonymous enumerator>"; - dwarf2_const_value_attr (attr, type, name, &obstack, cu, - &value, &bytes, &baton); - if (value < 0) - { - unsigned_enum = 0; - flag_enum = 0; - } + /* Can't check UNSIGNED_ENUM here because that is + optimistic. */ + if (is_unsigned.has_value () && *is_unsigned) + value = attr->unsigned_constant ().value_or (0); else { - if (count_one_bits_ll (value) >= 2) - flag_enum = 0; + /* Read as signed, either because we don't know the sign or + because we know it is definitely signed. */ + value = attr->signed_constant ().value_or (0); + + if (value < 0) + { + unsigned_enum = false; + flag_enum = false; + } } + if (flag_enum && count_one_bits_ll (value) >= 2) + flag_enum = false; + struct field &field = fields.emplace_back (); field.set_name (dwarf2_physname (name, child_die, cu)); field.set_loc_enumval (value); @@ -11618,13 +11631,10 @@ update_enumeration_type_from_children (struct die_info *die, if (!fields.empty ()) type->copy_fields (fields); else - flag_enum = 0; - - if (unsigned_enum) - type->set_is_unsigned (true); + flag_enum = false; - if (flag_enum) - type->set_is_flag_enum (true); + type->set_is_unsigned (unsigned_enum); + type->set_is_flag_enum (flag_enum); } /* Given a DW_AT_enumeration_type die, set its type. We do not @@ -11668,7 +11678,7 @@ read_enumeration_type (struct die_info *die, struct dwarf2_cu *cu) attr = dwarf2_attr (die, DW_AT_byte_size, cu); if (attr != nullptr) - type->set_length (attr->constant_value (0)); + type->set_length (attr->unsigned_constant ().value_or (0)); else type->set_length (0); @@ -11682,6 +11692,11 @@ read_enumeration_type (struct die_info *die, struct dwarf2_cu *cu) if (die_is_declaration (die, cu)) type->set_is_stub (true); + /* If the underlying type is known, and is unsigned, then we'll + assume the enumerator constants are unsigned. Otherwise we have + to assume they are signed. */ + std::optional<bool> is_unsigned; + /* If this type has an underlying type that is not a stub, then we may use its attributes. We always use the "unsigned" attribute in this situation, because ordinarily we guess whether the type @@ -11694,7 +11709,8 @@ read_enumeration_type (struct die_info *die, struct dwarf2_cu *cu) struct type *underlying_type = type->target_type (); underlying_type = check_typedef (underlying_type); - type->set_is_unsigned (underlying_type->is_unsigned ()); + is_unsigned = underlying_type->is_unsigned (); + type->set_is_unsigned (*is_unsigned); if (type->length () == 0) type->set_length (underlying_type->length ()); @@ -11714,7 +11730,7 @@ read_enumeration_type (struct die_info *die, struct dwarf2_cu *cu) Note that, as usual, this must come after set_die_type to avoid infinite recursion when trying to compute the names of the enumerators. */ - update_enumeration_type_from_children (die, type, cu); + update_enumeration_type_from_children (die, type, cu, is_unsigned); return type; } @@ -12064,7 +12080,7 @@ read_array_type (struct die_info *die, struct dwarf2_cu *cu) attr = dwarf2_attr (die, DW_AT_bit_stride, cu); if (attr != NULL) - bit_stride = attr->constant_value (0); + bit_stride = attr->unsigned_constant ().value_or (0); /* Irix 6.2 native cc creates array types without children for arrays with unspecified length. */ @@ -12279,7 +12295,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); @@ -12288,7 +12305,7 @@ mark_common_block_symbol_computed (struct symbol *sym, if (member_loc->form_is_constant ()) { - offset = member_loc->constant_value (0); + offset = member_loc->unsigned_constant ().value_or (0); baton->size += 1 /* DW_OP_addr */ + cu->header.addr_size; } else @@ -12585,7 +12602,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; @@ -12603,7 +12620,8 @@ read_tag_pointer_type (struct die_info *die, struct dwarf2_cu *cu) attr_byte_size = dwarf2_attr (die, DW_AT_byte_size, cu); if (attr_byte_size) - byte_size = attr_byte_size->constant_value (cu_header->addr_size); + byte_size = (attr_byte_size->unsigned_constant () + .value_or (cu_header->addr_size)); else byte_size = cu_header->addr_size; @@ -12699,7 +12717,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; @@ -12715,7 +12733,8 @@ read_tag_reference_type (struct die_info *die, struct dwarf2_cu *cu, type = lookup_reference_type (target_type, refcode); attr = dwarf2_attr (die, DW_AT_byte_size, cu); if (attr != nullptr) - type->set_length (attr->constant_value (cu_header->addr_size)); + type->set_length (attr->unsigned_constant () + .value_or (cu_header->addr_size)); else type->set_length (cu_header->addr_size); @@ -12879,9 +12898,7 @@ read_tag_string_type (struct die_info *die, struct dwarf2_cu *cu) len = dwarf2_attr (die, DW_AT_byte_size, cu); if (len != nullptr && len->form_is_constant ()) { - /* Pass 0 as the default as we know this attribute is constant - and the default value will not be returned. */ - LONGEST sz = len->constant_value (0); + LONGEST sz = len->unsigned_constant ().value_or (0); prop_type = objfile_int_type (objfile, sz, true); } else @@ -12900,15 +12917,14 @@ read_tag_string_type (struct die_info *die, struct dwarf2_cu *cu) else if (attr != nullptr) { /* This DW_AT_string_length just contains the length with no - indirection. There's no need to create a dynamic property in this - case. Pass 0 for the default value as we know it will not be - returned in this case. */ - length = attr->constant_value (0); + indirection. There's no need to create a dynamic property in + this case. */ + length = attr->unsigned_constant ().value_or (0); } else if ((attr = dwarf2_attr (die, DW_AT_byte_size, cu)) != nullptr) { /* We don't currently support non-constant byte sizes for strings. */ - length = attr->constant_value (1); + length = attr->unsigned_constant ().value_or (1); } else { @@ -13183,7 +13199,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. */ @@ -13217,10 +13233,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_unsigned ()) - *value = gdb_mpz (attr->as_unsigned ()); else - *value = gdb_mpz (attr->constant_value (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 @@ -13249,8 +13266,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 @@ -13399,14 +13416,14 @@ finish_fixed_point_type (struct type *type, const char *suffix, } else if (attr->name == DW_AT_binary_scale) { - LONGEST scale_exp = attr->constant_value (0); + LONGEST scale_exp = attr->signed_constant ().value_or (0); gdb_mpz &num_or_denom = scale_exp > 0 ? scale_num : scale_denom; num_or_denom <<= std::abs (scale_exp); } else if (attr->name == DW_AT_decimal_scale) { - LONGEST scale_exp = attr->constant_value (0); + LONGEST scale_exp = attr->signed_constant ().value_or (0); gdb_mpz &num_or_denom = scale_exp > 0 ? scale_num : scale_denom; num_or_denom = gdb_mpz::pow (10, std::abs (scale_exp)); @@ -13606,7 +13623,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); @@ -13616,9 +13632,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) + bit_size = attr->unsigned_constant (); + + attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu); + std::optional<ULONGEST> bit_offset; if (attr != nullptr) - bits = attr->constant_value (0) * TARGET_CHAR_BIT; + 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")); @@ -13764,29 +13804,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->constant_value (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. */ - if (attr == nullptr - || (attr->form_is_constant () - && attr->constant_value (0) >= 0 - && (attr->constant_value (0) + real_bit_size - <= 8 * type->length ()))) - { - 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 - = attr->constant_value (0); - } - } + TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_size = *bit_size; + TYPE_MAIN_TYPE (type)->type_specific.int_stuff.bit_offset = offset; } } @@ -13927,17 +13959,13 @@ 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); + 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); + prop->set_field (baton); break; } } @@ -14097,8 +14125,13 @@ read_subrange_type (struct die_info *die, struct dwarf2_cu *cu) LONGEST bias = 0; struct attribute *bias_attr = dwarf2_attr (die, DW_AT_GNU_bias, cu); - if (bias_attr != nullptr && bias_attr->form_is_constant ()) - bias = bias_attr->constant_value (0); + if (bias_attr != nullptr) + { + if (base_type->is_unsigned ()) + bias = (LONGEST) bias_attr->unsigned_constant ().value_or (0); + else + bias = bias_attr->signed_constant ().value_or (0); + } /* Normally, the DWARF producers are expected to use a signed constant form (Eg. DW_FORM_sdata) to express negative bounds. @@ -14185,7 +14218,7 @@ read_subrange_type (struct die_info *die, struct dwarf2_cu *cu) attr = dwarf2_attr (die, DW_AT_byte_size, cu); if (attr != nullptr) - range_type->set_length (attr->constant_value (0)); + range_type->set_length (attr->unsigned_constant ().value_or (0)); maybe_set_alignment (cu, die, range_type); @@ -14866,7 +14899,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; @@ -15174,8 +15207,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); @@ -15199,7 +15231,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 (); @@ -15332,9 +15364,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 @@ -16531,7 +16570,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 @@ -17164,40 +17203,6 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu, return (sym); } -/* Given an attr with a DW_FORM_dataN value in host byte order, - zero-extend it as appropriate for the symbol's type. The DWARF - standard (v4) is not entirely clear about the meaning of using - DW_FORM_dataN for a constant with a signed type, where the type is - wider than the data. The conclusion of a discussion on the DWARF - list was that this is unspecified. We choose to always zero-extend - because that is the interpretation long in use by GCC. */ - -static gdb_byte * -dwarf2_const_value_data (const struct attribute *attr, struct obstack *obstack, - struct dwarf2_cu *cu, LONGEST *value, int bits) -{ - struct objfile *objfile = cu->per_objfile->objfile; - enum bfd_endian byte_order = bfd_big_endian (objfile->obfd.get ()) ? - BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; - LONGEST l = attr->constant_value (0); - - if (bits < sizeof (*value) * 8) - { - l &= ((LONGEST) 1 << bits) - 1; - *value = l; - } - else if (bits == sizeof (*value) * 8) - *value = l; - else - { - gdb_byte *bytes = (gdb_byte *) obstack_alloc (obstack, bits / 8); - store_unsigned_integer (bytes, bits / 8, byte_order, l); - return bytes; - } - - return NULL; -} - /* Read a constant value from an attribute. Either set *VALUE, or if the value does not fit in *VALUE, set *BYTES - either already allocated on the objfile obstack, or newly allocated on OBSTACK, @@ -17213,7 +17218,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); @@ -17237,7 +17242,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); @@ -17281,25 +17286,13 @@ dwarf2_const_value_attr (const struct attribute *attr, struct type *type, converted to host endianness, so we just need to sign- or zero-extend it as appropriate. */ case DW_FORM_data1: - *bytes = dwarf2_const_value_data (attr, obstack, cu, value, 8); - break; case DW_FORM_data2: - *bytes = dwarf2_const_value_data (attr, obstack, cu, value, 16); - break; case DW_FORM_data4: - *bytes = dwarf2_const_value_data (attr, obstack, cu, value, 32); - break; case DW_FORM_data8: - *bytes = dwarf2_const_value_data (attr, obstack, cu, value, 64); - break; - case DW_FORM_sdata: case DW_FORM_implicit_const: - *value = attr->as_signed (); - break; - case DW_FORM_udata: - *value = attr->as_unsigned (); + *value = attr->confused_constant ().value_or (0); break; default: @@ -18189,18 +18182,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, @@ -18232,8 +18225,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. @@ -18492,47 +18484,19 @@ dwarf2_fetch_constant_bytes (sect_offset sect_off, symbol's value "represented as it would be on the target architecture." By the time we get here, it's already been converted to host endianness, so we just need to sign- or - zero-extend it as appropriate. */ + zero-extend it as appropriate. + + Both GCC and LLVM agree that these are always signed, though. */ case DW_FORM_data1: - type = die_type (die, cu); - result = dwarf2_const_value_data (attr, obstack, cu, &value, 8); - if (result == NULL) - result = write_constant_as_bytes (obstack, byte_order, - type, value, len); - break; case DW_FORM_data2: - type = die_type (die, cu); - result = dwarf2_const_value_data (attr, obstack, cu, &value, 16); - if (result == NULL) - result = write_constant_as_bytes (obstack, byte_order, - type, value, len); - break; case DW_FORM_data4: - type = die_type (die, cu); - result = dwarf2_const_value_data (attr, obstack, cu, &value, 32); - if (result == NULL) - result = write_constant_as_bytes (obstack, byte_order, - type, value, len); - break; case DW_FORM_data8: - type = die_type (die, cu); - result = dwarf2_const_value_data (attr, obstack, cu, &value, 64); - if (result == NULL) - result = write_constant_as_bytes (obstack, byte_order, - type, value, len); - break; - case DW_FORM_sdata: case DW_FORM_implicit_const: - type = die_type (die, cu); - result = write_constant_as_bytes (obstack, byte_order, - type, attr->as_signed (), len); - break; - case DW_FORM_udata: type = die_type (die, cu); - result = write_constant_as_bytes (obstack, byte_order, - type, attr->as_unsigned (), len); + value = attr->confused_constant ().value_or (0); + result = write_constant_as_bytes (obstack, byte_order, type, value, len); break; default: @@ -18609,8 +18573,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. */ @@ -18619,7 +18583,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; @@ -19197,7 +19161,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); @@ -19229,7 +19194,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) @@ -19237,8 +19202,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; } @@ -19267,7 +19231,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; @@ -19730,9 +19694,7 @@ show_check_physname (struct ui_file *file, int from_tty, value); } -void _initialize_dwarf2_read (); -void -_initialize_dwarf2_read () +INIT_GDB_FILE (dwarf2_read) { add_setshow_prefix_cmd ("dwarf", class_maintenance, _("\ diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index f3e043c..a5cfb31 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -20,9 +20,12 @@ #ifndef GDB_DWARF2_READ_H #define GDB_DWARF2_READ_H +#if CXX_STD_THREAD +#include <mutex> +#endif #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 +236,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 +274,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. */ @@ -512,8 +515,8 @@ struct dwarf2_per_bfd const char *filename () const { return bfd_get_filename (this->obfd); } - /* Return the CU given its index. */ - dwarf2_per_cu *get_cu (int index) const + /* Return the unit given its index. */ + dwarf2_per_cu *get_unit (int index) const { return this->all_units[index].get (); } @@ -522,7 +525,7 @@ struct dwarf2_per_bfd dwarf2_per_cu *get_index_cu (int index) const { if (this->all_comp_units_index_cus.empty ()) - return get_cu (index); + return get_unit (index); return this->all_comp_units_index_cus[index]; } @@ -634,6 +637,11 @@ public: /* Set of dwo_file objects. */ dwo_file_up_set dwo_files; +#if CXX_STD_THREAD + /* Mutex to synchronize access to DWO_FILES. */ + std::mutex dwo_files_lock; +#endif + /* The DWP file if there is one, or NULL. */ dwp_file_up dwp_file; @@ -702,7 +710,7 @@ public: dwarf2_per_cu *operator* () const { - return m_per_bfd->get_cu (m_index); + return m_per_bfd->get_unit (m_index); } bool operator== (const all_units_iterator &other) const @@ -812,7 +820,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 +942,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 +1037,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/section.h b/gdb/dwarf2/section.h index b9d3c31..fd6e34d 100644 --- a/gdb/dwarf2/section.h +++ b/gdb/dwarf2/section.h @@ -112,4 +112,6 @@ struct dwarf2_section_info bool is_virtual; }; +using dwarf2_section_info_up = std::unique_ptr<dwarf2_section_info>; + #endif /* GDB_DWARF2_SECTION_H */ 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/elfread.c b/gdb/elfread.c index 7dd8fc7..ae74978 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -1513,9 +1513,7 @@ static const struct gnu_ifunc_fns elf_gnu_ifunc_fns = elf_gnu_ifunc_resolver_return_stop }; -void _initialize_elfread (); -void -_initialize_elfread () +INIT_GDB_FILE (elfread) { add_symtab_fns (bfd_target_elf_flavour, &elf_sym_fns); @@ -994,9 +994,10 @@ add_struct_fields (struct type *type, completion_list &output, output.emplace_back (concat (prefix, type->field (i).name (), nullptr)); } - else if (type->field (i).type ()->code () == TYPE_CODE_UNION) + else if (type->field (i).type ()->code () == TYPE_CODE_UNION + || type->field (i).type ()->code () == TYPE_CODE_STRUCT) { - /* Recurse into anonymous unions. */ + /* Recurse into anonymous unions and structures. */ add_struct_fields (type->field (i).type (), output, fieldname, namelen, prefix); } @@ -2557,27 +2558,26 @@ unop_extract_operation::evaluate (struct type *expect_type, } - /* Helper for evaluate_subexp_for_address. */ static value * -evaluate_subexp_for_address_base (struct expression *exp, enum noside noside, - value *x) +evaluate_subexp_for_address_base (enum noside noside, value *x) { if (noside == EVAL_AVOID_SIDE_EFFECTS) { struct type *type = check_typedef (x->type ()); + enum type_code typecode = type->code (); if (TYPE_IS_REFERENCE (type)) return value::zero (lookup_pointer_type (type->target_type ()), - not_lval); - else if (x->lval () == lval_memory || value_must_coerce_to_target (x)) - return value::zero (lookup_pointer_type (x->type ()), - not_lval); + not_lval); + else if (x->lval () == lval_memory || value_must_coerce_to_target (x) + || typecode == TYPE_CODE_STRUCT || typecode == TYPE_CODE_UNION) + return value::zero (lookup_pointer_type (x->type ()), not_lval); else - error (_("Attempt to take address of " - "value not located in memory.")); + error (_("Attempt to take address of value not located in memory.")); } + return value_addr (x); } @@ -2597,7 +2597,7 @@ value * operation::evaluate_for_address (struct expression *exp, enum noside noside) { value *val = evaluate (nullptr, exp, noside); - return evaluate_subexp_for_address_base (exp, noside, val); + return evaluate_subexp_for_address_base (noside, val); } value * @@ -2624,7 +2624,7 @@ unop_ind_base_operation::evaluate_for_address (struct expression *exp, if (unop_user_defined_p (UNOP_IND, x)) { x = value_x_unop (x, UNOP_IND, noside); - return evaluate_subexp_for_address_base (exp, noside, x); + return evaluate_subexp_for_address_base (noside, x); } return coerce_array (x); diff --git a/gdb/event-top.c b/gdb/event-top.c index c533e74..f96982a 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 @@ -1649,9 +1650,7 @@ show_debug_event_loop_command (struct ui_file *file, int from_tty, gdb_printf (file, _("Event loop debugging is %s.\n"), value); } -void _initialize_event_top (); -void -_initialize_event_top () +INIT_GDB_FILE (event_top) { add_setshow_enum_cmd ("event-loop", class_maintenance, debug_event_loop_enum, 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); @@ -44,7 +44,7 @@ #include <ctype.h> #include <sys/stat.h> -#include "solist.h" +#include "solib.h" #include <algorithm> #include "gdbsupport/pathstuff.h" #include "cli/cli-style.h" @@ -218,28 +218,32 @@ validate_exec_file (int from_tty) if (exec_file_mismatch_mode == exec_file_mismatch_off) return; + /* If there's no current executable, then there's nothing to + validate against, so we're done. */ const char *current_exec_file = current_program_space->exec_filename (); - struct inferior *inf = current_inferior (); - /* Try to determine a filename from the process itself. */ - const char *pid_exec_file = target_pid_to_exec_file (inf->pid); - bool build_id_mismatch = false; - - /* If we cannot validate the exec file, return. */ - if (current_exec_file == NULL || pid_exec_file == NULL) + if (current_exec_file == nullptr) return; - /* Try validating via build-id, if available. This is the most - reliable check. */ + /* Try to determine a filename from the process itself. If we + cannot get an executable from the process, then no validation is + possible. */ + const char *pid_exec_file + = target_pid_to_exec_file (current_inferior ()->pid); + if (pid_exec_file == nullptr) + return; - /* In case current_exec_file was changed, reopen_exec_file ensures - an up to date build_id (will do nothing if the file timestamp - did not change). If exec file changed, reopen_exec_file has - allocated another file name, so get_exec_file again. */ + /* In case current_exec_file was changed, reopen_exec_file ensures an up + to date build_id (will do nothing if the file timestamp did not + change). If exec file changed, reopen_exec_file has allocated another + file name, so get_exec_file again. */ reopen_exec_file (); current_exec_file = current_program_space->exec_filename (); + /* Try validating via build-id, if available. This is the most reliable + check. */ const bfd_build_id *exec_file_build_id = build_id_bfd_get (current_program_space->exec_bfd ()); + bool build_id_mismatch = false; if (exec_file_build_id != nullptr) { /* Prepend the target prefix, to force gdb_bfd_open to open the @@ -334,6 +338,14 @@ exec_file_locate_attach (int pid, int defer_bp_reset, int from_tty) gdb::unique_xmalloc_ptr<char> exec_file_host = exec_file_find (exec_file_target, NULL); + if (exec_file_host == nullptr) + { + warning (_("No executable has been specified, and target executable " + "%ps could not be found. Try using the \"%ps\" command."), + styled_string (file_name_style.style (), exec_file_target), + styled_string (command_style.style (), "file")); + return; + } if (defer_bp_reset) add_flags |= SYMFILE_DEFER_BP_RESET; @@ -1065,9 +1077,7 @@ exec_target::find_memory_regions (find_memory_region_ftype func, void *data) return objfile_find_memory_regions (this, func, data); } -void _initialize_exec (); -void -_initialize_exec () +INIT_GDB_FILE (exec) { struct cmd_list_element *c; diff --git a/gdb/extension.c b/gdb/extension.c index 6d14a20..d34dbcd 100644 --- a/gdb/extension.c +++ b/gdb/extension.c @@ -1102,9 +1102,7 @@ ext_lang_before_prompt (const char *current_gdb_prompt) } } -void _initialize_extension (); -void -_initialize_extension () +INIT_GDB_FILE (extension) { gdb::observers::before_prompt.attach (ext_lang_before_prompt, "extension"); } diff --git a/gdb/extract-store-integer.c b/gdb/extract-store-integer.c index 4f20092..d73040b 100644 --- a/gdb/extract-store-integer.c +++ b/gdb/extract-store-integer.c @@ -337,9 +337,7 @@ extract_integer_test () #endif -void _initialize_extract_store_integer (); -void -_initialize_extract_store_integer () +INIT_GDB_FILE (extract_store_integer) { #if GDB_SELF_TEST selftests::register_test ("copy_integer_to_size", diff --git a/gdb/f-lang.c b/gdb/f-lang.c index 3e31dce..1aa9fc4 100644 --- a/gdb/f-lang.c +++ b/gdb/f-lang.c @@ -1836,9 +1836,7 @@ builtin_f_type (struct gdbarch *gdbarch) static struct cmd_list_element *set_fortran_list; static struct cmd_list_element *show_fortran_list; -void _initialize_f_language (); -void -_initialize_f_language () +INIT_GDB_FILE (f_language) { add_setshow_prefix_cmd ("fortran", no_class, diff --git a/gdb/f-valprint.c b/gdb/f-valprint.c index 0f3b2a6..a0812b3 100644 --- a/gdb/f-valprint.c +++ b/gdb/f-valprint.c @@ -719,9 +719,7 @@ info_common_command (const char *comname, int from_tty) } } -void _initialize_f_valprint (); -void -_initialize_f_valprint () +INIT_GDB_FILE (f_valprint) { add_info ("common", info_common_command, _("Print out the values contained in a Fortran COMMON block.")); diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c index 71a8386..935b947 100644 --- a/gdb/fbsd-nat.c +++ b/gdb/fbsd-nat.c @@ -2465,14 +2465,12 @@ fbsd_nat_get_siginfo (ptid_t ptid, siginfo_t *siginfo) if (ptrace (PT_LWPINFO, pid, (caddr_t) &pl, sizeof pl) == -1) return false; if (!(pl.pl_flags & PL_FLAG_SI)) - return false;; + return false; *siginfo = pl.pl_siginfo; return (true); } -void _initialize_fbsd_nat (); -void -_initialize_fbsd_nat () +INIT_GDB_FILE (fbsd_nat) { add_setshow_boolean_cmd ("fbsd-lwp", class_maintenance, &debug_fbsd_lwp, _("\ diff --git a/gdb/filesystem.c b/gdb/filesystem.c index 51e1c3f..a272c13 100644 --- a/gdb/filesystem.c +++ b/gdb/filesystem.c @@ -76,9 +76,7 @@ is \"%s\".\n"), value); } -void _initialize_filesystem (); -void -_initialize_filesystem () +INIT_GDB_FILE (filesystem) { add_setshow_enum_cmd ("target-file-system-kind", class_files, diff --git a/gdb/findcmd.c b/gdb/findcmd.c index 993c487..4e47877 100644 --- a/gdb/findcmd.c +++ b/gdb/findcmd.c @@ -279,9 +279,7 @@ find_command (const char *args, int from_tty) found_count > 1 ? "s" : ""); } -void _initialize_mem_search (); -void -_initialize_mem_search () +INIT_GDB_FILE (mem_search) { add_cmd ("find", class_vars, find_command, _("\ Search memory for a sequence of bytes.\n\ diff --git a/gdb/findvar.c b/gdb/findvar.c index 2938931..9da5c48 100644 --- a/gdb/findvar.c +++ b/gdb/findvar.c @@ -485,7 +485,8 @@ language_defn::read_var_value (struct symbol *var, /* Determine address of TLS variable. */ if (obj_section && (obj_section->the_bfd_section->flags & SEC_THREAD_LOCAL) != 0) - addr = target_translate_tls_address (obj_section->objfile, addr); + addr = target_translate_tls_address (obj_section->objfile, addr, + var->print_name ()); } break; diff --git a/gdb/fork-child.c b/gdb/fork-child.c index 8abfbda..fd7f1e0 100644 --- a/gdb/fork-child.c +++ b/gdb/fork-child.c @@ -153,9 +153,7 @@ show_startup_with_shell (struct ui_file *file, int from_tty, value); } -void _initialize_fork_child (); -void -_initialize_fork_child () +INIT_GDB_FILE (fork_child) { add_setshow_filename_cmd ("exec-wrapper", class_run, &exec_wrapper, _("\ Set a wrapper for running programs.\n\ diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c index 9ed2dee..95768fa 100644 --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -49,6 +49,7 @@ static constexpr std::initializer_list<const frame_unwind *> standard_unwinders = { &dummy_frame_unwind, +#if defined(DWARF_FORMAT_AVAILABLE) /* The DWARF tailcall sniffer must come before the inline sniffer. Otherwise, we can end up in a situation where a DWARF frame finds tailcall information, but then the inline sniffer claims a frame @@ -57,6 +58,7 @@ static constexpr std::initializer_list<const frame_unwind *> activated if the newer frame was created using the DWARF unwinder, and it also found tailcall information. */ &dwarf2_tailcall_frame_unwind, +#endif &inline_frame_unwind, }; @@ -612,9 +614,7 @@ maintenance_enable_frame_unwinders (const char *args, int from_tty) enable_disable_frame_unwinders (args, from_tty, true); } -void _initialize_frame_unwind (); -void -_initialize_frame_unwind () +INIT_GDB_FILE (frame_unwind) { /* Add "maint info frame-unwinders". */ add_cmd ("frame-unwinders", diff --git a/gdb/frame.c b/gdb/frame.c index fe5336f..fc4cbca 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -3424,9 +3424,7 @@ frame_info_ptr::reinflate () const return m_ptr; } -void _initialize_frame (); -void -_initialize_frame () +INIT_GDB_FILE (frame) { obstack_init (&frame_cache_obstack); diff --git a/gdb/frv-linux-tdep.c b/gdb/frv-linux-tdep.c index 067b983..2a0fe1b 100644 --- a/gdb/frv-linux-tdep.c +++ b/gdb/frv-linux-tdep.c @@ -482,9 +482,7 @@ frv_linux_elf_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_frv_linux_tdep (); -void -_initialize_frv_linux_tdep () +INIT_GDB_FILE (frv_linux_tdep) { gdbarch_register_osabi (bfd_arch_frv, 0, GDB_OSABI_LINUX, frv_linux_init_abi); diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c index 3608872..b2d6e1a 100644 --- a/gdb/frv-tdep.c +++ b/gdb/frv-tdep.c @@ -25,6 +25,7 @@ #include "frame.h" #include "frame-unwind.h" #include "frame-base.h" +#include "solib-frv.h" #include "trad-frame.h" #include "dis-asm.h" #include "sim-regno.h" @@ -1554,7 +1555,7 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_convert_from_func_ptr_addr (gdbarch, frv_convert_from_func_ptr_addr); - set_gdbarch_so_ops (gdbarch, &frv_so_ops); + set_gdbarch_make_solib_ops (gdbarch, make_frv_solib_ops); /* Hook in ABI-specific overrides, if they have been registered. */ gdbarch_init_osabi (info, gdbarch); @@ -1569,9 +1570,7 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_frv_tdep (); -void -_initialize_frv_tdep () +INIT_GDB_FILE (frv_tdep) { gdbarch_register (bfd_arch_frv, frv_gdbarch_init); } diff --git a/gdb/frv-tdep.h b/gdb/frv-tdep.h index 07982b4..7b51b42 100644 --- a/gdb/frv-tdep.h +++ b/gdb/frv-tdep.h @@ -118,7 +118,4 @@ CORE_ADDR frv_fdpic_find_canonical_descriptor (CORE_ADDR entry_point); needed for TLS support. */ CORE_ADDR frv_fetch_objfile_link_map (struct objfile *objfile); -struct solib_ops; -extern const solib_ops frv_so_ops; - #endif /* GDB_FRV_TDEP_H */ diff --git a/gdb/ft32-tdep.c b/gdb/ft32-tdep.c index f84e225..3384929 100644 --- a/gdb/ft32-tdep.c +++ b/gdb/ft32-tdep.c @@ -621,9 +621,7 @@ ft32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Register this machine's init routine. */ -void _initialize_ft32_tdep (); -void -_initialize_ft32_tdep () +INIT_GDB_FILE (ft32_tdep) { gdbarch_register (bfd_arch_ft32, ft32_gdbarch_init); } diff --git a/gdb/gcore-1.in b/gdb/gcore-1.in index 1220f4a..2f6eb02 100644 --- a/gdb/gcore-1.in +++ b/gdb/gcore-1.in @@ -34,11 +34,11 @@ data_directory_opt=() function print_usage() { prefix="Usage: $0" - paddin=$(printf '%*s' ${#prefix}) + padding=$(printf '%*s' ${#prefix}) echo "$prefix [-h|--help] [-v|--version]" - echo "$paddin [-a] [-o prefix] [-d data-directory]" - echo "$paddin pid1 [pid2...pidN]" + echo "$padding [-a] [-o prefix] [-d data-directory]" + echo "$padding pid1 [pid2...pidN]" } function print_try_help() { diff --git a/gdb/gcore.c b/gdb/gcore.c index fa15d06..d532652 100644 --- a/gdb/gcore.c +++ b/gdb/gcore.c @@ -861,9 +861,7 @@ gcore_find_signalled_thread () return nullptr; } -void _initialize_gcore (); -void -_initialize_gcore () +INIT_GDB_FILE (gcore) { cmd_list_element *generate_core_file_cmd = add_com ("generate-core-file", class_files, gcore_command, _("\ diff --git a/gdb/gdb-demangle.c b/gdb/gdb-demangle.c index 239306a..c285788 100644 --- a/gdb/gdb-demangle.c +++ b/gdb/gdb-demangle.c @@ -208,9 +208,7 @@ demangle_command (const char *args, int from_tty) error (_("Can't demangle \"%s\""), name); } -void _initialize_gdb_demangle (); -void -_initialize_gdb_demangle () +INIT_GDB_FILE (gdb_demangle) { int i, ndems; diff --git a/gdb/gdb-gdb.py.in b/gdb/gdb-gdb.py.in index 4d96235..7388c6f 100644 --- a/gdb/gdb-gdb.py.in +++ b/gdb/gdb-gdb.py.in @@ -122,7 +122,7 @@ class StructTypePrettyPrinter: class StructMainTypePrettyPrinter: - """Pretty-print an objet of type main_type""" + """Pretty-print an object of type main_type""" def __init__(self, val): self.val = val @@ -164,8 +164,10 @@ class StructMainTypePrettyPrinter: return "physaddr = 0x%x" % loc_val["physaddr"] elif loc_kind == "FIELD_LOC_KIND_PHYSNAME": return "physname = %s" % loc_val["physname"] - elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK": - return "dwarf_block = %s" % loc_val["dwarf_block"] + elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK_ADDR": + return "dwarf_block_addr = %s" % loc_val["dwarf_block"] + elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK_BITPOS": + return "dwarf_block_bitpos = %s" % loc_val["dwarf_block"] else: return "m_loc = ??? (unsupported m_loc_kind value)" diff --git a/gdb/gdb-stabs.h b/gdb/gdb-stabs.h index 2f3a0a5..9909129 100644 --- a/gdb/gdb-stabs.h +++ b/gdb/gdb-stabs.h @@ -20,9 +20,9 @@ #ifndef GDB_GDB_STABS_H #define GDB_GDB_STABS_H -/* During initial symbol readin, we need to have a structure to keep +/* During initial symbol reading, we need to have a structure to keep track of which psymtabs have which bincls in them. This structure - is used during readin to setup the list of dependencies within each + is used during reading to setup the list of dependencies within each partial symbol table. */ struct legacy_psymtab; diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c index 06d6f5c..2e477eb 100644 --- a/gdb/gdb_bfd.c +++ b/gdb/gdb_bfd.c @@ -1346,9 +1346,7 @@ gdb_bfd_init () error (_("fatal error: libbfd ABI mismatch")); } -void _initialize_gdb_bfd (); -void -_initialize_gdb_bfd () +INIT_GDB_FILE (gdb_bfd) { add_cmd ("bfds", class_maintenance, maintenance_info_bfds, _("\ List the BFDs that are currently open."), diff --git a/gdb/gdb_buildall.sh b/gdb/gdb_buildall.sh index 430aae4..2053333 100644 --- a/gdb/gdb_buildall.sh +++ b/gdb/gdb_buildall.sh @@ -261,7 +261,7 @@ echo "done." # Clean up build directory if necessary. if ${clean} then - echo "cleanning up $dir" + echo "cleaning up $dir" rm -rf ${dir} fi diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index 32d1659..fc570d3 100644 --- a/gdb/gdbarch-gen.c +++ b/gdb/gdbarch-gen.c @@ -157,7 +157,7 @@ struct gdbarch gdbarch_single_step_through_delay_ftype *single_step_through_delay = nullptr; gdbarch_print_insn_ftype *print_insn = default_print_insn; gdbarch_skip_trampoline_code_ftype *skip_trampoline_code = generic_skip_trampoline_code; - const solib_ops * so_ops = &solib_target_so_ops; + gdbarch_make_solib_ops_ftype *make_solib_ops = make_target_solib_ops; gdbarch_skip_solib_resolver_ftype *skip_solib_resolver = generic_skip_solib_resolver; gdbarch_in_solib_return_trampoline_ftype *in_solib_return_trampoline = generic_in_solib_return_trampoline; gdbarch_in_indirect_branch_thunk_ftype *in_indirect_branch_thunk = default_in_indirect_branch_thunk; @@ -425,7 +425,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of single_step_through_delay, has predicate. */ /* Skip verify of print_insn, invalid_p == 0. */ /* Skip verify of skip_trampoline_code, invalid_p == 0. */ - /* Skip verify of so_ops, invalid_p == 0. */ + /* Skip verify of make_solib_ops, invalid_p == 0. */ /* Skip verify of skip_solib_resolver, invalid_p == 0. */ /* Skip verify of in_solib_return_trampoline, invalid_p == 0. */ /* Skip verify of in_indirect_branch_thunk, invalid_p == 0. */ @@ -966,8 +966,8 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: skip_trampoline_code = <%s>\n", host_address_to_string (gdbarch->skip_trampoline_code)); gdb_printf (file, - "gdbarch_dump: so_ops = %s\n", - host_address_to_string (gdbarch->so_ops)); + "gdbarch_dump: make_solib_ops = <%s>\n", + host_address_to_string (gdbarch->make_solib_ops)); gdb_printf (file, "gdbarch_dump: skip_solib_resolver = <%s>\n", host_address_to_string (gdbarch->skip_solib_resolver)); @@ -3469,21 +3469,21 @@ set_gdbarch_skip_trampoline_code (struct gdbarch *gdbarch, gdbarch->skip_trampoline_code = skip_trampoline_code; } -const solib_ops * -gdbarch_so_ops (struct gdbarch *gdbarch) +solib_ops_up +gdbarch_make_solib_ops (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); - /* Skip verify of so_ops, invalid_p == 0. */ + gdb_assert (gdbarch->make_solib_ops != NULL); if (gdbarch_debug >= 2) - gdb_printf (gdb_stdlog, "gdbarch_so_ops called\n"); - return gdbarch->so_ops; + gdb_printf (gdb_stdlog, "gdbarch_make_solib_ops called\n"); + return gdbarch->make_solib_ops (); } void -set_gdbarch_so_ops (struct gdbarch *gdbarch, - const solib_ops * so_ops) +set_gdbarch_make_solib_ops (struct gdbarch *gdbarch, + gdbarch_make_solib_ops_ftype make_solib_ops) { - gdbarch->so_ops = so_ops; + gdbarch->make_solib_ops = make_solib_ops; } CORE_ADDR diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 313a8f1..281b97b 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -818,10 +818,11 @@ typedef CORE_ADDR (gdbarch_skip_trampoline_code_ftype) (const frame_info_ptr &fr extern CORE_ADDR gdbarch_skip_trampoline_code (struct gdbarch *gdbarch, const frame_info_ptr &frame, CORE_ADDR pc); extern void set_gdbarch_skip_trampoline_code (struct gdbarch *gdbarch, gdbarch_skip_trampoline_code_ftype *skip_trampoline_code); -/* Vtable of solib operations functions. */ +/* Return a newly-allocated solib_ops object capable of providing the solibs for this architecture. */ -extern const solib_ops * gdbarch_so_ops (struct gdbarch *gdbarch); -extern void set_gdbarch_so_ops (struct gdbarch *gdbarch, const solib_ops * so_ops); +typedef solib_ops_up (gdbarch_make_solib_ops_ftype) (); +extern solib_ops_up gdbarch_make_solib_ops (struct gdbarch *gdbarch); +extern void set_gdbarch_make_solib_ops (struct gdbarch *gdbarch, gdbarch_make_solib_ops_ftype *make_solib_ops); /* If in_solib_dynsym_resolve_code() returns true, and SKIP_SOLIB_RESOLVER evaluates non-zero, this is the address where the debugger will place diff --git a/gdb/gdbarch-selftests.c b/gdb/gdbarch-selftests.c index c370204..5c94f60 100644 --- a/gdb/gdbarch-selftests.c +++ b/gdb/gdbarch-selftests.c @@ -183,9 +183,7 @@ check_stack_growth (struct gdbarch *gdbarch) } /* namespace selftests */ -void _initialize_gdbarch_selftests (); -void -_initialize_gdbarch_selftests () +INIT_GDB_FILE (gdbarch_selftests) { selftests::register_test_foreach_arch ("register_to_value", selftests::register_to_value_test); diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 9feb2cc..6accbd2 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -30,6 +30,7 @@ #include "displaced-stepping.h" #include "gdbsupport/gdb-checked-static-cast.h" #include "registry.h" +#include "solib.h" struct floatformat; struct ui_file; diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py index ec09d95..91c867e 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -1431,12 +1431,12 @@ Function( invalid=False, ) -Value( - comment="Vtable of solib operations functions.", - type="const solib_ops *", - name="so_ops", - predefault="&solib_target_so_ops", - printer="host_address_to_string (gdbarch->so_ops)", +Function( + comment="Return a newly-allocated solib_ops object capable of providing the solibs for this architecture.", + type="solib_ops_up", + name="make_solib_ops", + params=[], + predefault="make_target_solib_ops", invalid=False, ) diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index 5713eac..14a903b 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 (); @@ -2121,7 +2121,7 @@ is_dynamic_type_internal (struct type *type, bool top_level) return true; /* If the field is at a fixed offset, then it is not dynamic. */ - if (type->field (i).loc_kind () != FIELD_LOC_KIND_DWARF_BLOCK) + if (!type->field (i).loc_is_dwarf_block ()) continue; /* Do not consider C++ virtual base types to be dynamic due to the field's offset being dynamic; these are @@ -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,118 @@ 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_is_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_or_bitpos; + if (dwarf2_evaluate_property (&prop, frame, addr_stack, + &addr_or_bitpos, vals)) + { + if (field.loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK_ADDR) + field.set_loc_bitpos (TARGET_CHAR_BIT + * (addr_or_bitpos - addr_stack->addr)); + else + field.set_loc_bitpos (addr_or_bitpos); + + 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 +2815,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 +2861,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) { @@ -4356,7 +4420,8 @@ check_types_equal (struct type *type1, struct type *type2, field2->loc_physname ())) return false; break; - case FIELD_LOC_KIND_DWARF_BLOCK: + case FIELD_LOC_KIND_DWARF_BLOCK_ADDR: + case FIELD_LOC_KIND_DWARF_BLOCK_BITPOS: { struct dwarf2_locexpr_baton *block1, *block2; @@ -5501,8 +5566,12 @@ copy_type_recursive (struct type *type, copied_types_hash_t &copied_types) new_type->field (i).set_loc_physname (xstrdup (type->field (i).loc_physname ())); break; - case FIELD_LOC_KIND_DWARF_BLOCK: - new_type->field (i).set_loc_dwarf_block + case FIELD_LOC_KIND_DWARF_BLOCK_ADDR: + new_type->field (i).set_loc_dwarf_block_addr + (type->field (i).loc_dwarf_block ()); + break; + case FIELD_LOC_KIND_DWARF_BLOCK_BITPOS: + new_type->field (i).set_loc_dwarf_block_bitpos (type->field (i).loc_dwarf_block ()); break; default: @@ -6115,17 +6184,25 @@ builtin_type (struct objfile *objfile) return builtin_type (objfile->arch ()); } -/* See gdbtypes.h. */ +/* See dwarf2/call-site.h. */ CORE_ADDR call_site::pc () const { + /* dwarf2_per_objfile is defined in dwarf/read.c, so if that is disabled + at configure time, we won't be able to use this relocate function. + This is dwarf-specific, and would ideally be in call-site.h, but + including dwarf2/read.h in dwarf2/call-site.h will lead to things being + included in the wrong order and many compilation errors will happen. + This is the next best thing. */ +#if defined(DWARF_FORMAT_AVAILABLE) return per_objfile->relocate (m_unrelocated_pc); +#else + gdb_assert_not_reached ("unexpected call_site object found"); +#endif } -void _initialize_gdbtypes (); -void -_initialize_gdbtypes () +INIT_GDB_FILE (gdbtypes) { add_setshow_zuinteger_cmd ("overload", no_class, &overload_debug, _("Set debugging of C++ overloading."), diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index 5ee9deb..9e2efe9 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; } @@ -480,7 +480,10 @@ enum field_loc_kind FIELD_LOC_KIND_ENUMVAL, /**< enumval */ FIELD_LOC_KIND_PHYSADDR, /**< physaddr */ FIELD_LOC_KIND_PHYSNAME, /**< physname */ - FIELD_LOC_KIND_DWARF_BLOCK /**< dwarf_block */ + /* A DWARF block that computes the address of the field. */ + FIELD_LOC_KIND_DWARF_BLOCK_ADDR, /**< dwarf_block */ + /* A DWARF block that computes the bit offset of the field. */ + FIELD_LOC_KIND_DWARF_BLOCK_BITPOS, }; /* * A discriminant to determine which field in the @@ -616,6 +619,13 @@ struct field return m_loc_kind; } + /* Return true if this location has either "DWARF block" kind. */ + bool loc_is_dwarf_block () const + { + return (m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK_ADDR + || m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK_BITPOS); + } + LONGEST loc_bitpos () const { gdb_assert (m_loc_kind == FIELD_LOC_KIND_BITPOS); @@ -666,13 +676,19 @@ struct field dwarf2_locexpr_baton *loc_dwarf_block () const { - gdb_assert (m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK); + gdb_assert (loc_is_dwarf_block ()); return m_loc.dwarf_block; } - void set_loc_dwarf_block (dwarf2_locexpr_baton *dwarf_block) + void set_loc_dwarf_block_addr (dwarf2_locexpr_baton *dwarf_block) + { + m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK_ADDR; + m_loc.dwarf_block = dwarf_block; + } + + void set_loc_dwarf_block_bitpos (dwarf2_locexpr_baton *dwarf_block) { - m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK; + m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK_BITPOS; m_loc.dwarf_block = dwarf_block; } @@ -2629,6 +2645,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/gmp-utils.c b/gdb/gmp-utils.c index b48e97c..5c5423a 100644 --- a/gdb/gmp-utils.c +++ b/gdb/gmp-utils.c @@ -250,10 +250,7 @@ xfree_for_gmp (void *ptr, size_t size) xfree (ptr); } -void _initialize_gmp_utils (); - -void -_initialize_gmp_utils () +INIT_GDB_FILE (gmp_utils) { /* Tell GMP to use GDB's memory management routines. */ mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp); diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c index ea07dd6..dd639fe 100644 --- a/gdb/gnu-nat.c +++ b/gdb/gnu-nat.c @@ -3434,9 +3434,7 @@ to the thread's initial suspend-count when gdb notices the threads."), &thread_cmd_list); } -void _initialize_gnu_nat (); -void -_initialize_gnu_nat () +INIT_GDB_FILE (gnu_nat) { proc_server = getproc (); diff --git a/gdb/gnu-v2-abi.c b/gdb/gnu-v2-abi.c index ab9f53b..9246788 100644 --- a/gdb/gnu-v2-abi.c +++ b/gdb/gnu-v2-abi.c @@ -265,15 +265,9 @@ gnuv2_value_rtti_type (struct value *v, int *full, LONGEST *top, int *using_enc) if (top && ((*top) >0)) { if (rtti_type->length () > known_type->length ()) - { - if (full) - *full=0; - } + *full = 0; else - { - if (full) - *full=1; - } + *full = 1; } } else @@ -411,9 +405,7 @@ init_gnuv2_ops (void) gnu_v2_abi_ops.baseclass_offset = gnuv2_baseclass_offset; } -void _initialize_gnu_v2_abi (); -void -_initialize_gnu_v2_abi () +INIT_GDB_FILE (gnu_v2_abi) { init_gnuv2_ops (); register_cp_abi (&gnu_v2_abi_ops); diff --git a/gdb/gnu-v3-abi.c b/gdb/gnu-v3-abi.c index 4f53e9f..e3818d9 100644 --- a/gdb/gnu-v3-abi.c +++ b/gdb/gnu-v3-abi.c @@ -476,7 +476,7 @@ gnuv3_baseclass_offset (struct type *type, int index, return TYPE_BASECLASS_BITPOS (type, index) / 8; /* If we have a DWARF expression for the offset, evaluate it. */ - if (type->field (index).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK) + if (type->field (index).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK_ADDR) { struct dwarf2_property_baton baton; baton.property_type @@ -1570,9 +1570,7 @@ init_gnuv3_ops (void) gnu_v3_abi_ops.pass_by_reference = gnuv3_pass_by_reference; } -void _initialize_gnu_v3_abi (); -void -_initialize_gnu_v3_abi () +INIT_GDB_FILE (gnu_v3_abi) { init_gnuv3_ops (); diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c index 8dcb22d..4d8e6c9 100644 --- a/gdb/go32-nat.c +++ b/gdb/go32-nat.c @@ -2057,9 +2057,7 @@ go32_pte_for_address (const char *arg, int from_tty) static struct cmd_list_element *info_dos_cmdlist = NULL; -void _initialize_go32_nat (); -void -_initialize_go32_nat () +INIT_GDB_FILE (go32_nat) { x86_dr_low.set_control = go32_set_dr7; x86_dr_low.set_addr = go32_set_dr; diff --git a/gdb/guile/guile-internal.h b/gdb/guile/guile-internal.h index 3101882..60a8bf3 100644 --- a/gdb/guile/guile-internal.h +++ b/gdb/guile/guile-internal.h @@ -449,10 +449,11 @@ extern const struct block *bkscm_scm_to_block /* scm-cmd.c */ -extern char *gdbscm_parse_command_name (const char *name, - const char *func_name, int arg_pos, - struct cmd_list_element ***base_list, - struct cmd_list_element **start_list); +extern char *gdbscm_parse_command_name + (const char *name, const char *func_name, int arg_pos, + struct cmd_list_element ***base_list, + struct cmd_list_element **start_list, + struct cmd_list_element **prefix_cmd = nullptr); extern int gdbscm_valid_command_class_p (int command_class); diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c index 1803df0..66a74b3 100644 --- a/gdb/guile/guile.c +++ b/gdb/guile/guile.c @@ -827,9 +827,7 @@ message == an error message without a stack will be printed."), &set_guile_list, &show_guile_list); } -void _initialize_guile (); -void -_initialize_guile () +INIT_GDB_FILE (guile) { install_gdb_commands (); } diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c index 4757872..19fb742 100644 --- a/gdb/guile/scm-cmd.c +++ b/gdb/guile/scm-cmd.c @@ -457,11 +457,13 @@ cmdscm_completer (struct cmd_list_element *command, name of the new command. All earlier words must be existing prefix commands. - *BASE_LIST is set to the final prefix command's list of - *sub-commands. + *BASE_LIST is set to the final prefix command's list of sub-commands. START_LIST is the list in which the search starts. + When PREFIX_CMD is not NULL then *PREFIX_CMD is set to the prefix + command itself, or NULL, if there is no prefix command. + This function returns the xmalloc()d name of the new command. On error a Scheme exception is thrown. */ @@ -469,13 +471,17 @@ char * gdbscm_parse_command_name (const char *name, const char *func_name, int arg_pos, struct cmd_list_element ***base_list, - struct cmd_list_element **start_list) + struct cmd_list_element **start_list, + struct cmd_list_element **prefix_cmd) { struct cmd_list_element *elt; int len = strlen (name); int i, lastchar; char *msg; + if (prefix_cmd != nullptr) + *prefix_cmd = nullptr; + /* Skip trailing whitespace. */ for (i = len - 1; i >= 0 && (name[i] == ' ' || name[i] == '\t'); --i) ; @@ -490,9 +496,9 @@ gdbscm_parse_command_name (const char *name, /* Find first character of the final word. */ for (; i > 0 && valid_cmd_char_p (name[i - 1]); --i) ; - gdb::unique_xmalloc_ptr<char> result ((char *) xmalloc (lastchar - i + 2)); - memcpy (result.get (), &name[i], lastchar - i + 1); - result.get ()[lastchar - i + 1] = '\0'; + + gdb::unique_xmalloc_ptr<char> result + = make_unique_xstrndup (&name[i], lastchar - i + 1); /* Skip whitespace again. */ for (--i; i >= 0 && (name[i] == ' ' || name[i] == '\t'); --i) @@ -503,18 +509,21 @@ gdbscm_parse_command_name (const char *name, return result.release (); } - gdb::unique_xmalloc_ptr<char> prefix_text ((char *) xmalloc (i + 2)); - memcpy (prefix_text.get (), name, i + 1); - prefix_text.get ()[i + 1] = '\0'; + gdb::unique_xmalloc_ptr<char> prefix_text + = make_unique_xstrndup (name, i + 1); const char *prefix_text2 = prefix_text.get (); elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1); - if (elt == NULL || elt == CMD_LIST_AMBIGUOUS) + if (elt == nullptr || elt == CMD_LIST_AMBIGUOUS || *prefix_text2 != '\0') { msg = xstrprintf (_("could not find command prefix '%s'"), prefix_text.get ()).release (); scm_dynwind_begin ((scm_t_dynwind_flags) 0); gdbscm_dynwind_xfree (msg); + /* Release memory now as the destructors will not be run when the + guile exception is thrown. */ + result = nullptr; + prefix_text = nullptr; gdbscm_out_of_range_error (func_name, arg_pos, gdbscm_scm_from_c_string (name), msg); } @@ -522,6 +531,8 @@ gdbscm_parse_command_name (const char *name, if (elt->is_prefix ()) { *base_list = elt->subcommands; + if (prefix_cmd != nullptr) + *prefix_cmd = elt; return result.release (); } @@ -529,6 +540,10 @@ gdbscm_parse_command_name (const char *name, prefix_text.get ()).release (); scm_dynwind_begin ((scm_t_dynwind_flags) 0); gdbscm_dynwind_xfree (msg); + /* Release memory now as the destructors will not be run when the guile + exception is thrown. */ + result = nullptr; + prefix_text = nullptr; gdbscm_out_of_range_error (func_name, arg_pos, gdbscm_scm_from_c_string (name), msg); /* NOTREACHED */ @@ -743,8 +758,9 @@ gdbscm_register_command_x (SCM self) if (cmdscm_is_valid (c_smob)) scm_misc_error (FUNC_NAME, _("command is already registered"), SCM_EOL); + struct cmd_list_element *prefix_cmd = nullptr; cmd_name = gdbscm_parse_command_name (c_smob->name, FUNC_NAME, SCM_ARG1, - &cmd_list, &cmdlist); + &cmd_list, &cmdlist, &prefix_cmd); c_smob->cmd_name = gdbscm_gc_xstrdup (cmd_name); xfree (cmd_name); @@ -753,18 +769,50 @@ gdbscm_register_command_x (SCM self) { if (c_smob->is_prefix) { - /* If we have our own "invoke" method, then allow unknown - sub-commands. */ - int allow_unknown = gdbscm_is_true (c_smob->invoke); + bool has_invoke = gdbscm_is_true (c_smob->invoke) == 1; - cmd = add_prefix_cmd (c_smob->cmd_name, c_smob->cmd_class, - NULL, c_smob->doc, &c_smob->sub_list, - allow_unknown, cmd_list); + if (has_invoke) + { + cmd = add_prefix_cmd (c_smob->cmd_name, c_smob->cmd_class, + NULL, c_smob->doc, &c_smob->sub_list, + 1 /* allow_unknown */, cmd_list); + cmd->func = cmdscm_function; + } + else + { + /* If there is no 'invoke' method, then create the prefix + using the standard prefix callbacks. This means that for + 'set prefix' the user will get the help text listing all + of the sub-commands, and for 'show prefix', the user will + see all of the sub-command values. */ + if (prefix_cmd != nullptr) + { + while (prefix_cmd->prefix != nullptr) + prefix_cmd = prefix_cmd->prefix; + } + + bool is_show = (prefix_cmd != nullptr + && prefix_cmd->subcommands == &showlist); + + if (is_show) + cmd = add_show_prefix_cmd (c_smob->cmd_name, + c_smob->cmd_class, + c_smob->doc, + &c_smob->sub_list, + 0 /* allow_unknown */, cmd_list); + else + cmd = add_basic_prefix_cmd (c_smob->cmd_name, + c_smob->cmd_class, + c_smob->doc, + &c_smob->sub_list, + 0 /* allow_unknown */, cmd_list); + } } else { cmd = add_cmd (c_smob->cmd_name, c_smob->cmd_class, c_smob->doc, cmd_list); + cmd->func = cmdscm_function; } } catch (const gdb_exception &except) @@ -777,7 +825,6 @@ gdbscm_register_command_x (SCM self) So no more errors after this point. */ /* There appears to be no API to set this. */ - cmd->func = cmdscm_function; cmd->destroyer = cmdscm_destroyer; c_smob->command = cmd; 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-param.c b/gdb/guile/scm-param.c index 65226ec..5137847 100644 --- a/gdb/guile/scm-param.c +++ b/gdb/guile/scm-param.c @@ -308,13 +308,37 @@ pascm_is_valid (param_smob *p_smob) return p_smob->commands.set != nullptr; } -/* A helper function which return the default documentation string for - a parameter (which is to say that it's undocumented). */ + +/* The different types of documentation string. */ + +enum doc_string_type +{ + doc_string_set, + doc_string_show, + doc_string_description +}; + +/* A helper function which returns the default documentation string for + a parameter CMD_NAME. The DOC_TYPE indicates which type of + documentation string is needed. The returned string is dynamically + allocated. */ static char * -get_doc_string (void) +get_doc_string (doc_string_type doc_type, const char *cmd_name) { - return xstrdup (_("This command is not documented.")); + if (doc_type == doc_string_description) + return xstrdup (_("This command is not documented.")); + else + { + gdb_assert (cmd_name != nullptr); + + if (doc_type == doc_string_show) + return xstrprintf (_("Show the current value of '%s'."), + cmd_name).release (); + else + return xstrprintf (_("Set the current value of '%s'."), + cmd_name).release (); + } } /* Subroutine of pascm_set_func, pascm_show_func to simplify them. @@ -990,11 +1014,14 @@ gdbscm_make_parameter (SCM name_scm, SCM rest) &show_doc_arg_pos, &show_doc, &initial_value_arg_pos, &initial_value_scm); - /* If doc is NULL, leave it NULL. See add_setshow_cmd_full. */ + if (doc == nullptr) + doc = get_doc_string (doc_string_description, nullptr); + else if (*doc == '\0') + doc = nullptr; if (set_doc == NULL) - set_doc = get_doc_string (); + set_doc = get_doc_string (doc_string_set, name); if (show_doc == NULL) - show_doc = get_doc_string (); + show_doc = get_doc_string (doc_string_show, name); s = name; name = gdbscm_canonicalize_command_name (s, 0); 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/h8300-tdep.c b/gdb/h8300-tdep.c index db54438..15b0002 100644 --- a/gdb/h8300-tdep.c +++ b/gdb/h8300-tdep.c @@ -1374,9 +1374,7 @@ h8300_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) } -void _initialize_h8300_tdep (); -void -_initialize_h8300_tdep () +INIT_GDB_FILE (h8300_tdep) { gdbarch_register (bfd_arch_h8300, h8300_gdbarch_init); } diff --git a/gdb/hppa-bsd-tdep.c b/gdb/hppa-bsd-tdep.c index db6c92f..715e794 100644 --- a/gdb/hppa-bsd-tdep.c +++ b/gdb/hppa-bsd-tdep.c @@ -128,8 +128,7 @@ hppabsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_skip_trampoline_code (gdbarch, hppa_skip_trampoline_code); /* OpenBSD and NetBSD use SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); /* Hook in the DWARF CFI frame unwinder. */ dwarf2_frame_set_init_reg (gdbarch, hppabsd_dwarf2_frame_init_reg); diff --git a/gdb/hppa-linux-nat.c b/gdb/hppa-linux-nat.c index 62b3294..5e63e7f 100644 --- a/gdb/hppa-linux-nat.c +++ b/gdb/hppa-linux-nat.c @@ -383,9 +383,7 @@ fill_fpregset (const struct regcache *regcache, } } -void _initialize_hppa_linux_nat (); -void -_initialize_hppa_linux_nat () +INIT_GDB_FILE (hppa_linux_nat) { /* Register the target. */ linux_target = &the_hppa_linux_nat_target; diff --git a/gdb/hppa-linux-tdep.c b/gdb/hppa-linux-tdep.c index 16cdd45..2eb8d46 100644 --- a/gdb/hppa-linux-tdep.c +++ b/gdb/hppa-linux-tdep.c @@ -32,6 +32,7 @@ #include "regcache.h" #include "hppa-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "elf/common.h" /* Map DWARF DBX register numbers to GDB register numbers. */ @@ -499,8 +500,7 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) frame_unwind_append_unwinder (gdbarch, &hppa_linux_sigtramp_frame_unwind); /* GNU/Linux uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); tdep->in_solib_call_trampoline = hppa_in_solib_call_trampoline; set_gdbarch_skip_trampoline_code (gdbarch, hppa_skip_trampoline_code); @@ -524,9 +524,7 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) svr4_fetch_objfile_link_map); } -void _initialize_hppa_linux_tdep (); -void -_initialize_hppa_linux_tdep () +INIT_GDB_FILE (hppa_linux_tdep) { gdbarch_register_osabi (bfd_arch_hppa, 0, GDB_OSABI_LINUX, hppa_linux_init_abi); diff --git a/gdb/hppa-netbsd-nat.c b/gdb/hppa-netbsd-nat.c index f57d650..1ebf205 100644 --- a/gdb/hppa-netbsd-nat.c +++ b/gdb/hppa-netbsd-nat.c @@ -229,9 +229,7 @@ hppa_nbsd_nat_target::store_registers (struct regcache *regcache, int regnum) } } -void _initialize_hppanbsd_nat (); -void -_initialize_hppanbsd_nat () +INIT_GDB_FILE (hppanbsd_nat) { add_inf_child_target (&the_hppa_nbsd_nat_target); } diff --git a/gdb/hppa-netbsd-tdep.c b/gdb/hppa-netbsd-tdep.c index e52e181..90d8eb8 100644 --- a/gdb/hppa-netbsd-tdep.c +++ b/gdb/hppa-netbsd-tdep.c @@ -210,9 +210,7 @@ hppanbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tramp_frame_prepend_unwinder (gdbarch, &hppanbsd_sigtramp_si4); } -void _initialize_hppanbsd_tdep (); -void -_initialize_hppanbsd_tdep () +INIT_GDB_FILE (hppanbsd_tdep) { gdbarch_register_osabi (bfd_arch_hppa, 0, GDB_OSABI_NETBSD, hppanbsd_init_abi); diff --git a/gdb/hppa-obsd-nat.c b/gdb/hppa-obsd-nat.c index d879835..322b6f9 100644 --- a/gdb/hppa-obsd-nat.c +++ b/gdb/hppa-obsd-nat.c @@ -250,9 +250,7 @@ hppa_obsd_nat_target::store_registers (struct regcache *regcache, int regnum) } } -void _initialize_hppaobsd_nat (); -void -_initialize_hppaobsd_nat () +INIT_GDB_FILE (hppaobsd_nat) { add_inf_child_target (&the_hppa_obsd_nat_target); } diff --git a/gdb/hppa-obsd-tdep.c b/gdb/hppa-obsd-tdep.c index d2144f9..a511fa8 100644 --- a/gdb/hppa-obsd-tdep.c +++ b/gdb/hppa-obsd-tdep.c @@ -166,9 +166,7 @@ hppaobsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) (gdbarch, hppaobsd_iterate_over_regset_sections); } -void _initialize_hppabsd_tdep (); -void -_initialize_hppabsd_tdep () +INIT_GDB_FILE (hppabsd_tdep) { gdbarch_register_osabi (bfd_arch_hppa, 0, GDB_OSABI_OPENBSD, hppaobsd_init_abi); diff --git a/gdb/hppa-tdep.c b/gdb/hppa-tdep.c index 3c9dde2..61e6cb2 100644 --- a/gdb/hppa-tdep.c +++ b/gdb/hppa-tdep.c @@ -3119,9 +3119,7 @@ hppa_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "elf = %s\n", tdep->is_elf ? "yes" : "no"); } -void _initialize_hppa_tdep (); -void -_initialize_hppa_tdep () +INIT_GDB_FILE (hppa_tdep) { gdbarch_register (bfd_arch_hppa, hppa_gdbarch_init, hppa_dump_tdep); diff --git a/gdb/i386-bsd-nat.c b/gdb/i386-bsd-nat.c index ae9e090..c5eda86 100644 --- a/gdb/i386-bsd-nat.c +++ b/gdb/i386-bsd-nat.c @@ -237,9 +237,7 @@ i386bsd_store_inferior_registers (struct regcache *regcache, int regnum) } } -void _initialize_i386bsd_nat (); -void -_initialize_i386bsd_nat () +INIT_GDB_FILE (i386bsd_nat) { /* To support the recognition of signal handlers, i386-bsd-tdep.c hardcodes some constants. Inclusion of this file means that we diff --git a/gdb/i386-darwin-nat.c b/gdb/i386-darwin-nat.c index 08d3a71..09d6ba6 100644 --- a/gdb/i386-darwin-nat.c +++ b/gdb/i386-darwin-nat.c @@ -631,9 +631,7 @@ darwin_set_sstep (thread_t thread, int enable) } } -void _initialize_i386_darwin_nat (); -void -_initialize_i386_darwin_nat () +INIT_GDB_FILE (i386_darwin_nat) { #ifdef BFD64 amd64_native_gregset64_reg_offset = amd64_darwin_thread_state_reg_offset; diff --git a/gdb/i386-darwin-tdep.c b/gdb/i386-darwin-tdep.c index 6180e02..a91afa2 100644 --- a/gdb/i386-darwin-tdep.c +++ b/gdb/i386-darwin-tdep.c @@ -271,7 +271,7 @@ i386_darwin_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) alignment. */ set_gdbarch_long_double_bit (gdbarch, 128); - set_gdbarch_so_ops (gdbarch, &darwin_so_ops); + set_gdbarch_make_solib_ops (gdbarch, make_darwin_solib_ops); } static enum gdb_osabi @@ -286,9 +286,7 @@ i386_mach_o_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_i386_darwin_tdep (); -void -_initialize_i386_darwin_tdep () +INIT_GDB_FILE (i386_darwin_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_unknown, bfd_target_mach_o_flavour, i386_mach_o_osabi_sniffer); diff --git a/gdb/i386-dicos-tdep.c b/gdb/i386-dicos-tdep.c index d3f19e2..50570c9 100644 --- a/gdb/i386-dicos-tdep.c +++ b/gdb/i386-dicos-tdep.c @@ -40,9 +40,7 @@ i386_dicos_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_i386_dicos_tdep (); -void -_initialize_i386_dicos_tdep () +INIT_GDB_FILE (i386_dicos_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour, i386_dicos_osabi_sniffer); diff --git a/gdb/i386-fbsd-nat.c b/gdb/i386-fbsd-nat.c index 77f0edd..f4088a3 100644 --- a/gdb/i386-fbsd-nat.c +++ b/gdb/i386-fbsd-nat.c @@ -330,9 +330,7 @@ i386_fbsd_nat_target::read_description () return i386_target_description (X86_XSTATE_X87_MASK, true); } -void _initialize_i386fbsd_nat (); -void -_initialize_i386fbsd_nat () +INIT_GDB_FILE (i386fbsd_nat) { add_inf_child_target (&the_i386_fbsd_nat_target); diff --git a/gdb/i386-fbsd-tdep.c b/gdb/i386-fbsd-tdep.c index 72237d8..827d53c 100644 --- a/gdb/i386-fbsd-tdep.c +++ b/gdb/i386-fbsd-tdep.c @@ -402,8 +402,7 @@ i386fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) i386fbsd_core_read_description); /* FreeBSD uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); @@ -411,9 +410,7 @@ i386fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) i386fbsd_get_thread_local_address); } -void _initialize_i386fbsd_tdep (); -void -_initialize_i386fbsd_tdep () +INIT_GDB_FILE (i386fbsd_tdep) { gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_FREEBSD, i386fbsd_init_abi); diff --git a/gdb/i386-gnu-tdep.c b/gdb/i386-gnu-tdep.c index 97fbc69..f944fa5 100644 --- a/gdb/i386-gnu-tdep.c +++ b/gdb/i386-gnu-tdep.c @@ -180,8 +180,7 @@ i386gnu_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Hurd uses SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); /* Hurd uses the dynamic linker included in the GNU C Library. */ set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); @@ -202,9 +201,7 @@ i386gnu_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->sc_num_regs = ARRAY_SIZE (i386_gnu_sc_reg_offset); } -void _initialize_i386gnu_tdep (); -void -_initialize_i386gnu_tdep () +INIT_GDB_FILE (i386gnu_tdep) { gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_HURD, i386gnu_init_abi); } diff --git a/gdb/i386-go32-tdep.c b/gdb/i386-go32-tdep.c index 4db09b3..e6a1721 100644 --- a/gdb/i386-go32-tdep.c +++ b/gdb/i386-go32-tdep.c @@ -61,9 +61,7 @@ i386_coff_osabi_sniffer (bfd *abfd) } -void _initialize_i386_go32_tdep (); -void -_initialize_i386_go32_tdep () +INIT_GDB_FILE (i386_go32_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_coff_flavour, i386_coff_osabi_sniffer); diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c index a5d5582..f5112d2 100644 --- a/gdb/i386-linux-nat.c +++ b/gdb/i386-linux-nat.c @@ -696,9 +696,7 @@ i386_linux_nat_target::low_resume (ptid_t ptid, int step, enum gdb_signal signal perror_with_name (("ptrace")); } -void _initialize_i386_linux_nat (); -void -_initialize_i386_linux_nat () +INIT_GDB_FILE (i386_linux_nat) { linux_target = &the_i386_linux_nat_target; diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index 4b05cc6..efbde6a 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -30,6 +30,7 @@ #include "i386-tdep.h" #include "i386-linux-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "utils.h" #include "glibc-tdep.h" #include "solib-svr4.h" @@ -1461,8 +1462,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* GNU/Linux uses SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); /* GNU/Linux uses the dynamic linker included in the GNU C Library. */ set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); @@ -1490,9 +1490,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) i386_linux_get_syscall_number); } -void _initialize_i386_linux_tdep (); -void -_initialize_i386_linux_tdep () +INIT_GDB_FILE (i386_linux_tdep) { gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_LINUX, i386_linux_init_abi); diff --git a/gdb/i386-netbsd-nat.c b/gdb/i386-netbsd-nat.c index 0f39fe1..efbf361 100644 --- a/gdb/i386-netbsd-nat.c +++ b/gdb/i386-netbsd-nat.c @@ -72,9 +72,7 @@ i386nbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) static i386_bsd_nat_target<nbsd_nat_target> the_i386_nbsd_nat_target; -void _initialize_i386nbsd_nat (); -void -_initialize_i386nbsd_nat () +INIT_GDB_FILE (i386nbsd_nat) { add_inf_child_target (&the_i386_nbsd_nat_target); diff --git a/gdb/i386-netbsd-tdep.c b/gdb/i386-netbsd-tdep.c index 701ce04..a1124d0 100644 --- a/gdb/i386-netbsd-tdep.c +++ b/gdb/i386-netbsd-tdep.c @@ -415,16 +415,13 @@ i386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) i386_elf_init_abi (info, gdbarch); /* NetBSD ELF uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); /* NetBSD ELF uses -fpcc-struct-return by default. */ tdep->struct_return = pcc_struct_return; } -void _initialize_i386nbsd_tdep (); -void -_initialize_i386nbsd_tdep () +INIT_GDB_FILE (i386nbsd_tdep) { gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD, i386nbsdelf_init_abi); diff --git a/gdb/i386-obsd-nat.c b/gdb/i386-obsd-nat.c index 323acdb..f478e73 100644 --- a/gdb/i386-obsd-nat.c +++ b/gdb/i386-obsd-nat.c @@ -89,9 +89,7 @@ i386obsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) static i386_bsd_nat_target<obsd_nat_target> the_i386_obsd_nat_target; -void _initialize_i386obsd_nat (); -void -_initialize_i386obsd_nat () +INIT_GDB_FILE (i386obsd_nat) { add_inf_child_target (&i386_obsd_nat_target); diff --git a/gdb/i386-obsd-tdep.c b/gdb/i386-obsd-tdep.c index be65668..1126597 100644 --- a/gdb/i386-obsd-tdep.c +++ b/gdb/i386-obsd-tdep.c @@ -441,13 +441,10 @@ i386obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) frame_unwind_prepend_unwinder (gdbarch, &i386obsd_trapframe_unwind); /* OpenBSD ELF uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); } -void _initialize_i386obsd_tdep (); -void -_initialize_i386obsd_tdep () +INIT_GDB_FILE (i386obsd_tdep) { gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_OPENBSD, i386obsd_init_abi); diff --git a/gdb/i386-sol2-nat.c b/gdb/i386-sol2-nat.c index d0fb836..1e87007 100644 --- a/gdb/i386-sol2-nat.c +++ b/gdb/i386-sol2-nat.c @@ -252,9 +252,7 @@ fill_fpregset (const struct regcache *regcache, #endif -void _initialize_amd64_sol2_nat (); -void -_initialize_amd64_sol2_nat () +INIT_GDB_FILE (amd64_sol2_nat) { #if PR_MODEL_NATIVE == PR_MODEL_LP64 amd64_native_gregset32_reg_offset = amd64_sol2_gregset32_reg_offset; diff --git a/gdb/i386-sol2-tdep.c b/gdb/i386-sol2-tdep.c index e842236..4ff08d2 100644 --- a/gdb/i386-sol2-tdep.c +++ b/gdb/i386-sol2-tdep.c @@ -86,8 +86,7 @@ i386_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->sc_num_regs = tdep->gregset_num_regs; /* Solaris has SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); } @@ -102,9 +101,7 @@ i386_sol2_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_i386_sol2_tdep (); -void -_initialize_i386_sol2_tdep () +INIT_GDB_FILE (i386_sol2_tdep) { /* Register an ELF OS ABI sniffer for Solaris 2 binaries. */ gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour, diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index 23788b4..9be4748 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -8970,9 +8970,7 @@ i386_target_description (uint64_t xcr0, bool segments) return *tdesc; } -void _initialize_i386_tdep (); -void -_initialize_i386_tdep () +INIT_GDB_FILE (i386_tdep) { gdbarch_register (bfd_arch_i386, i386_gdbarch_init); diff --git a/gdb/i386-windows-nat.c b/gdb/i386-windows-nat.c index 6c1369a..f878727 100644 --- a/gdb/i386-windows-nat.c +++ b/gdb/i386-windows-nat.c @@ -82,9 +82,7 @@ i386_windows_segment_register_p (int regnum) return regnum >= I386_CS_REGNUM && regnum <= I386_GS_REGNUM; } -void _initialize_i386_windows_nat (); -void -_initialize_i386_windows_nat () +INIT_GDB_FILE (i386_windows_nat) { #ifndef __x86_64__ x86_set_debug_register_length (4); diff --git a/gdb/i386-windows-tdep.c b/gdb/i386-windows-tdep.c index 43ae5e4..892ecd4 100644 --- a/gdb/i386-windows-tdep.c +++ b/gdb/i386-windows-tdep.c @@ -229,9 +229,7 @@ i386_cygwin_core_osabi_sniffer (bfd *abfd) return GDB_OSABI_UNKNOWN; } -void _initialize_i386_windows_tdep (); -void -_initialize_i386_windows_tdep () +INIT_GDB_FILE (i386_windows_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_coff_flavour, i386_windows_osabi_sniffer); diff --git a/gdb/ia64-libunwind-tdep.c b/gdb/ia64-libunwind-tdep.c index 3ba9cb6..6d3b265 100644 --- a/gdb/ia64-libunwind-tdep.c +++ b/gdb/ia64-libunwind-tdep.c @@ -574,9 +574,7 @@ libunwind_is_initialized (void) return libunwind_initialized; } -void _initialize_libunwind_frame (); -void -_initialize_libunwind_frame () +INIT_GDB_FILE (libunwind_frame) { libunwind_initialized = libunwind_load (); } diff --git a/gdb/ia64-linux-nat.c b/gdb/ia64-linux-nat.c index 964b175..e833f9d 100644 --- a/gdb/ia64-linux-nat.c +++ b/gdb/ia64-linux-nat.c @@ -914,9 +914,7 @@ ia64_linux_nat_target::low_status_is_event (int status) || WSTOPSIG (status) == SIGILL); } -void _initialize_ia64_linux_nat (); -void -_initialize_ia64_linux_nat () +INIT_GDB_FILE (ia64_linux_nat) { /* Register the target. */ linux_target = &the_ia64_linux_nat_target; diff --git a/gdb/ia64-linux-tdep.c b/gdb/ia64-linux-tdep.c index c45b2bb..6afffee 100644 --- a/gdb/ia64-linux-tdep.c +++ b/gdb/ia64-linux-tdep.c @@ -26,6 +26,7 @@ #include "solib-svr4.h" #include "symtab.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "regset.h" #include <ctype.h> @@ -235,8 +236,7 @@ ia64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops); /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, @@ -257,9 +257,7 @@ ia64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) ia64_linux_stap_is_single_operand); } -void _initialize_ia64_linux_tdep (); -void -_initialize_ia64_linux_tdep () +INIT_GDB_FILE (ia64_linux_tdep) { gdbarch_register_osabi (bfd_arch_ia64, 0, GDB_OSABI_LINUX, ia64_linux_init_abi); diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c index 395b5f7..7c6526a 100644 --- a/gdb/ia64-tdep.c +++ b/gdb/ia64-tdep.c @@ -4014,9 +4014,7 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_ia64_tdep (); -void -_initialize_ia64_tdep () +INIT_GDB_FILE (ia64_tdep) { gdbarch_register (bfd_arch_ia64, ia64_gdbarch_init, NULL); } diff --git a/gdb/ia64-vms-tdep.c b/gdb/ia64-vms-tdep.c index dcd5598..32fe5d4 100644 --- a/gdb/ia64-vms-tdep.c +++ b/gdb/ia64-vms-tdep.c @@ -153,9 +153,7 @@ ia64_openvms_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) #endif } -void _initialize_ia64_vms_tdep (); -void -_initialize_ia64_vms_tdep () +INIT_GDB_FILE (ia64_vms_tdep) { gdbarch_register_osabi (bfd_arch_ia64, 0, GDB_OSABI_OPENVMS, ia64_openvms_init_abi); diff --git a/gdb/inf-child.c b/gdb/inf-child.c index 016f30a..57ad0b4 100644 --- a/gdb/inf-child.c +++ b/gdb/inf-child.c @@ -322,11 +322,11 @@ inf_child_target::fileio_fstat (int fd, struct stat *sb, fileio_error *target_er return ret; } -/* Implementation of to_fileio_stat. */ +/* Implementation of to_fileio_lstat. */ int -inf_child_target::fileio_stat (struct inferior *inf, const char *filename, - struct stat *sb, fileio_error *target_errno) +inf_child_target::fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) { int ret; diff --git a/gdb/inf-child.h b/gdb/inf-child.h index 79b5157..70de393 100644 --- a/gdb/inf-child.h +++ b/gdb/inf-child.h @@ -81,8 +81,8 @@ public: int fileio_pread (int fd, gdb_byte *read_buf, int len, ULONGEST offset, fileio_error *target_errno) override; int fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno) override; - int fileio_stat (struct inferior *inf, const char *filename, - struct stat *sb, fileio_error *target_errno) override; + int fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) override; int fileio_close (int fd, fileio_error *target_errno) override; int fileio_unlink (struct inferior *inf, const char *filename, diff --git a/gdb/infcall.c b/gdb/infcall.c index 098072d..2b5936d 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -1870,9 +1870,7 @@ When the function is done executing, GDB will silently stop."), gdb_assert_not_reached ("... should not be here"); } -void _initialize_infcall (); -void -_initialize_infcall () +INIT_GDB_FILE (infcall) { add_setshow_boolean_cmd ("may-call-functions", no_class, &may_call_functions_p, _("\ diff --git a/gdb/infcmd.c b/gdb/infcmd.c index e9b58ce..8978c8a 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -224,14 +224,11 @@ strip_bg_char (const char *args, int *bg_char_p) return make_unique_xstrdup (args); } -/* Common actions to take after creating any sort of inferior, by any - means (running, attaching, connecting, et cetera). The target - should be stopped. */ +/* See inferior.h. */ void -post_create_inferior (int from_tty) +post_create_inferior (int from_tty, bool set_pspace_solib_ops) { - /* Be sure we own the terminal in case write operations are performed. */ target_terminal::ours_for_output (); @@ -261,6 +258,10 @@ post_create_inferior (int from_tty) throw; } + if (set_pspace_solib_ops) + current_program_space->set_solib_ops + (gdbarch_make_solib_ops (current_inferior ()->arch ())); + if (current_program_space->exec_bfd ()) { const unsigned solib_add_generation @@ -482,7 +483,7 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how) /* Pass zero for FROM_TTY, because at this point the "run" command has done its thing; now we are setting up the running program. */ - post_create_inferior (0); + post_create_inferior (0, true); /* Queue a pending event so that the program stops immediately. */ if (run_how == RUN_STOP_AT_FIRST_INSN) @@ -2506,7 +2507,7 @@ setup_inferior (int from_tty) /* Take any necessary post-attaching actions for this platform. */ target_post_attach (inferior_ptid.pid ()); - post_create_inferior (from_tty); + post_create_inferior (from_tty, true); } /* What to do after the first program stops after attaching. */ @@ -3080,9 +3081,7 @@ use \"set args\" without arguments.\n\ \n\ To start the inferior without using a shell, use \"set startup-with-shell off\"." -void _initialize_infcmd (); -void -_initialize_infcmd () +INIT_GDB_FILE (infcmd) { static struct cmd_list_element *info_proc_cmdlist; struct cmd_list_element *c = nullptr; diff --git a/gdb/inferior.h b/gdb/inferior.h index 54f5229..fe94e28 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -213,7 +213,14 @@ extern ptid_t gdb_startup_inferior (pid_t pid, int num_traps); extern void setup_inferior (int from_tty); -extern void post_create_inferior (int from_tty); +/* Common actions to take after creating any sort of inferior, by any + means (running, attaching, connecting, et cetera). The target + should be stopped. + + If SET_PSPACE_SOLIB_OPS is true, initialize the program space's solib + provider using the current inferior's architecture. */ + +extern void post_create_inferior (int from_tty, bool set_pspace_solib_ops); extern void attach_command (const char *, int); diff --git a/gdb/inflow.c b/gdb/inflow.c index f5ae6cd..31e1565 100644 --- a/gdb/inflow.c +++ b/gdb/inflow.c @@ -944,9 +944,7 @@ initialize_stdin_serial (void) stdin_serial = serial_fdopen (0); } -void _initialize_inflow (); -void -_initialize_inflow () +INIT_GDB_FILE (inflow) { add_info ("terminal", info_terminal_command, _("Print inferior's saved terminal status.")); diff --git a/gdb/infrun.c b/gdb/infrun.c index 0b87287..05bf6ab 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -58,7 +58,6 @@ #include "target-descriptions.h" #include "target-dcache.h" #include "terminal.h" -#include "solist.h" #include "gdbsupport/event-loop.h" #include "thread-fsm.h" #include "gdbsupport/enum-flags.h" @@ -473,6 +472,7 @@ holding the child stopped. Try \"set %ps\" or \"%ps\".\n"), inferior *parent_inf = current_inferior (); inferior *child_inf = nullptr; + bool child_has_new_pspace = false; gdb_assert (parent_inf->thread_waiting_for_vfork_done == nullptr); @@ -537,6 +537,7 @@ holding the child stopped. Try \"set %ps\" or \"%ps\".\n"), else { child_inf->pspace = new program_space (new_address_space ()); + child_has_new_pspace = true; child_inf->aspace = child_inf->pspace->aspace; child_inf->removable = true; clone_program_space (child_inf->pspace, parent_inf->pspace); @@ -626,6 +627,7 @@ holding the child stopped. Try \"set %ps\" or \"%ps\".\n"), else { child_inf->pspace = new program_space (new_address_space ()); + child_has_new_pspace = true; child_inf->aspace = child_inf->pspace->aspace; child_inf->removable = true; child_inf->symfile_flags = SYMFILE_NO_READ; @@ -724,7 +726,8 @@ holding the child stopped. Try \"set %ps\" or \"%ps\".\n"), maybe_restore.emplace (); switch_to_thread (*child_inf->threads ().begin ()); - post_create_inferior (0); + + post_create_inferior (0, child_has_new_pspace); } return false; @@ -1322,6 +1325,7 @@ follow_exec (ptid_t ptid, const char *exec_file_target) we don't want those to be satisfied by the libraries of the previous incarnation of this process. */ no_shared_libraries (current_program_space); + current_program_space->unset_solib_ops (); inferior *execing_inferior = current_inferior (); inferior *following_inferior; @@ -1378,6 +1382,8 @@ follow_exec (ptid_t ptid, const char *exec_file_target) registers. */ target_find_description (); + current_program_space->set_solib_ops + (gdbarch_make_solib_ops (following_inferior->arch ())); gdb::observers::inferior_execd.notify (execing_inferior, following_inferior); breakpoint_re_set (); @@ -3819,7 +3825,7 @@ start_remote (int from_tty) /* Now that the inferior has stopped, do any bookkeeping like loading shared libraries. We want to do this before normal_stop, so that the displayed frame is up to date. */ - post_create_inferior (from_tty); + post_create_inferior (from_tty, true); normal_stop (); } @@ -10532,9 +10538,7 @@ infrun_thread_ptid_changed () #endif /* GDB_SELF_TEST */ -void _initialize_infrun (); -void -_initialize_infrun () +INIT_GDB_FILE (infrun) { struct cmd_list_element *c; diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c index d724c28..2191282 100644 --- a/gdb/inline-frame.c +++ b/gdb/inline-frame.c @@ -615,9 +615,7 @@ maintenance_info_inline_frames (const char *arg, int from_tty) -void _initialize_inline_frame (); -void -_initialize_inline_frame () +INIT_GDB_FILE (inline_frame) { add_cmd ("inline-frames", class_maintenance, maintenance_info_inline_frames, _("\ diff --git a/gdb/interps.c b/gdb/interps.c index 00796bc..b4de3c7 100644 --- a/gdb/interps.c +++ b/gdb/interps.c @@ -575,9 +575,7 @@ interps_notify_memory_changed (inferior *inf, CORE_ADDR addr, ssize_t len, } /* This just adds the "interpreter-exec" command. */ -void _initialize_interpreter (); -void -_initialize_interpreter () +INIT_GDB_FILE (interpreter) { struct cmd_list_element *c; diff --git a/gdb/interps.h b/gdb/interps.h index 8d35309..c74495b 100644 --- a/gdb/interps.h +++ b/gdb/interps.h @@ -23,6 +23,7 @@ #define GDB_INTERPS_H #include "gdbsupport/intrusive_list.h" +#include "gdbsupport/event-loop.h" struct bpstat; struct ui_out; @@ -78,6 +79,13 @@ public: virtual void pre_command_loop () {} + /* Service one event. + This gives the interpreter a chance to handle its own private + events that cannot be fed into the gdb event mechanism. + In all cases, this should call gdb_do_one_event at some point. */ + virtual int do_one_event (int mstimeout = -1) + { return gdb_do_one_event (mstimeout); } + /* Returns true if this interpreter supports using the readline library; false if it uses GDB's own simplified readline emulation. */ diff --git a/gdb/iq2000-tdep.c b/gdb/iq2000-tdep.c index 49ae744..58a4dfb 100644 --- a/gdb/iq2000-tdep.c +++ b/gdb/iq2000-tdep.c @@ -840,9 +840,7 @@ iq2000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) Initializer function for the iq2000 module. Called by gdb at start-up. */ -void _initialize_iq2000_tdep (); -void -_initialize_iq2000_tdep () +INIT_GDB_FILE (iq2000_tdep) { gdbarch_register (bfd_arch_iq2000, iq2000_gdbarch_init); } @@ -1313,9 +1313,7 @@ show_jit_reader_directory (const char *args, int from_tty) jit_reader_dir.c_str ())); } -void _initialize_jit (); -void -_initialize_jit () +INIT_GDB_FILE (jit) { jit_reader_dir = relocate_gdb_directory (JIT_READER_DIR, JIT_READER_DIR_RELOCATABLE); diff --git a/gdb/language.c b/gdb/language.c index 18a7c9c..80f9b01 100644 --- a/gdb/language.c +++ b/gdb/language.c @@ -1095,9 +1095,7 @@ language_lookup_primitive_type_as_symbol (const struct language_defn *la, /* Initialize the language routines. */ -void _initialize_language (); -void -_initialize_language () +INIT_GDB_FILE (language) { static const char *const type_or_range_names[] = { "on", "off", "warn", "auto", NULL }; diff --git a/gdb/linespec.c b/gdb/linespec.c index d42420c..b59c055 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -1016,7 +1016,7 @@ linespec_lexer_consume_token (linespec_parser *parser) if (*parser->lexer.stream != '\0') { parser->completion_quote_char = '\0'; - parser->completion_quote_end = NULL;; + parser->completion_quote_end = NULL; } } @@ -1074,6 +1074,12 @@ add_sal_to_sals (struct linespec_state *self, struct symtab_and_line *sal, const char *symname, bool literal_canonical) { + /* We don't want two SALs with the same PC from the + same program space. */ + for (const auto &s : *sals) + if (sal->pc == s.pc && sal->pspace == s.pspace) + return; + sals->push_back (*sal); if (self->canonical) @@ -3407,30 +3413,52 @@ lookup_prefix_sym (struct linespec_state *state, return collector.release_symbols (); } -/* A std::sort comparison function for symbols. The resulting order does - not actually matter; we just need to be able to sort them so that - symbols with the same program space end up next to each other. */ +/* Compare pspace A and B based on program space ID. Return 0 if equal, + 1 if A->num > B->num, -1 otherwise (modeled on strcmp). */ -static bool -compare_symbols (const block_symbol &a, const block_symbol &b) +static int +compare_pspace (const struct program_space *a, const struct program_space *b) { - uintptr_t uia, uib; + if (a->num > b->num) + return 1; - uia = (uintptr_t) a.symbol->symtab ()->compunit ()->objfile ()->pspace (); - uib = (uintptr_t) b.symbol->symtab ()->compunit ()->objfile ()->pspace (); + if (a->num < b->num) + return -1; - if (uia < uib) - return true; - if (uia > uib) - return false; + return 0; +} + +/* An std::sort comparison function for pointers. Don't use this if stable + sorting results are required. */ + +static bool +compare_pointers (void *a, void *b) +{ + return (uintptr_t)a < (uintptr_t)b; +} - uia = (uintptr_t) a.symbol; - uib = (uintptr_t) b.symbol; +/* An std::sort comparison function for symbols. The requirement is that + symbols with the same program space end up next to each other. This is for + the purpose of iterating over the symbols and doing something once for each + program space. */ - if (uia < uib) +static bool +compare_symbols (const block_symbol &a, const block_symbol &b) +{ + /* To check for same program space, we could just use a pointer comparison, + which gives unstable sorting results. While the assumption is that this + doesn't matter, play it safe and compare program space IDs instead. */ + int cmp + = compare_pspace (a.symbol->symtab ()->compunit ()->objfile ()->pspace (), + b.symbol->symtab ()->compunit ()->objfile ()->pspace ()); + if (cmp == -1) return true; + if (cmp == 1) + return false; - return false; + /* This gives unstable sorting results. We assume that this doesn't + matter. */ + return compare_pointers (a.symbol, b.symbol); } /* Like compare_symbols but for minimal symbols. */ @@ -3438,23 +3466,16 @@ compare_symbols (const block_symbol &a, const block_symbol &b) static bool compare_msymbols (const bound_minimal_symbol &a, const bound_minimal_symbol &b) { - uintptr_t uia, uib; - - uia = (uintptr_t) a.objfile->pspace (); - uib = (uintptr_t) a.objfile->pspace (); - - if (uia < uib) + /* See comment in compare_symbols for use of compare_pspace. */ + int cmp = compare_pspace (a.objfile->pspace (), a.objfile->pspace ()); + if (cmp == -1) return true; - if (uia > uib) + if (cmp == 1) return false; - uia = (uintptr_t) a.minsym; - uib = (uintptr_t) b.minsym; - - if (uia < uib) - return true; - - return false; + /* This gives unstable sorting results. We assume that this doesn't + matter. */ + return compare_pointers (a.minsym, b.minsym); } /* Look for all the matching instances of each symbol in NAMES. Only diff --git a/gdb/linux-fork.c b/gdb/linux-fork.c index 338ba03..9e986d8 100644 --- a/gdb/linux-fork.c +++ b/gdb/linux-fork.c @@ -1115,9 +1115,7 @@ restart_command (const char *args, int from_tty) linux_fork_context (fp, from_tty, inf); } -void _initialize_linux_fork (); -void -_initialize_linux_fork () +INIT_GDB_FILE (linux_fork) { /* Checkpoint command: create a fork of the inferior process and set it aside for later debugging. */ diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 2f98060..70f77fd 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -2129,7 +2129,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status) open_proc_mem_file (lp->ptid); ourstatus->set_execd - (make_unique_xstrdup (linux_proc_pid_to_exec_file (pid))); + (make_unique_xstrdup (linux_target->pid_to_exec_file (pid))); /* The thread that execed must have been resumed, but, when a thread execs, it changes its tid to the tgid, and the old @@ -4000,7 +4000,14 @@ linux_nat_target::thread_name (struct thread_info *thr) const char * linux_nat_target::pid_to_exec_file (int pid) { - return linux_proc_pid_to_exec_file (pid); + /* If there's no sysroot. Or the sysroot is just 'target:' and the + inferior is in the same mount namespce, then we can consider the + filesystem local. */ + bool local_fs = (gdb_sysroot.empty () + || (gdb_sysroot == TARGET_SYSROOT_PREFIX + && linux_ns_same (pid, LINUX_NS_MNT))); + + return linux_proc_pid_to_exec_file (pid, local_fs); } /* Object representing an /proc/PID/mem open file. We keep one such @@ -4585,6 +4592,20 @@ linux_nat_target::fileio_open (struct inferior *inf, const char *filename, return fd; } +/* Implementation of to_fileio_lstat. */ + +int +linux_nat_target::fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) +{ + int r = linux_mntns_lstat (linux_nat_fileio_pid_of (inf), filename, sb); + + if (r == -1) + *target_errno = host_to_fileio_error (errno); + + return r; +} + /* Implementation of to_fileio_readlink. */ std::optional<std::string> @@ -4707,9 +4728,7 @@ maintenance_info_lwps (const char *arg, int from_tty) } } -void _initialize_linux_nat (); -void -_initialize_linux_nat () +INIT_GDB_FILE (linux_nat) { add_setshow_boolean_cmd ("linux-nat", class_maintenance, &debug_linux_nat, _("\ diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index 6e53991..21ec309 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -108,6 +108,9 @@ public: const char *filename, fileio_error *target_errno) override; + int fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) override; + int fileio_unlink (struct inferior *inf, const char *filename, fileio_error *target_errno) override; diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 141c119..5c4bbf6 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,24 @@ 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) { - /* 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; + /* No NT_FILE entry for mappings with no filename. */ + if (map.filename.length () == 0) + return false; + + /* Special kernel mappings, those with names like '[vdso]' and + '[vsyscall]' will be placed in the core file, but shouldn't get an + NT_FILE entry. These special mappings all have a zero inode. */ + if (map.inode == 0 + && map.filename.front () == '[' + && map.filename.back () == ']') + 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 +1322,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 +1592,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 +1626,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 +1675,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 +1683,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 +1703,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; } @@ -3102,9 +3081,7 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch, linux_corefile_parse_exec_context); } -void _initialize_linux_tdep (); -void -_initialize_linux_tdep () +INIT_GDB_FILE (linux_tdep) { /* Observers used to invalidate the cache when needed. */ gdb::observers::inferior_exit.attach (invalidate_linux_cache_inf, @@ -3137,65 +3114,5 @@ VM_DONTDUMP flag (\"dd\" in /proc/PID/smaps) when generating the corefile. For\ more information about this file, refer to the manpage of proc(5) and core(5)."), NULL, show_dump_excluded_mappings, &setlist, &showlist); -} - -/* Fetch (and possibly build) an appropriate `link_map_offsets' for - ILP32/LP64 Linux systems which don't have the r_ldsomap field. */ - -link_map_offsets * -linux_ilp32_fetch_link_map_offsets () -{ - static link_map_offsets lmo; - static link_map_offsets *lmp = nullptr; - - if (lmp == nullptr) - { - lmp = &lmo; - - lmo.r_version_offset = 0; - lmo.r_version_size = 4; - lmo.r_map_offset = 4; - lmo.r_brk_offset = 8; - lmo.r_ldsomap_offset = -1; - lmo.r_next_offset = 20; - - /* Everything we need is in the first 20 bytes. */ - lmo.link_map_size = 20; - lmo.l_addr_offset = 0; - lmo.l_name_offset = 4; - lmo.l_ld_offset = 8; - lmo.l_next_offset = 12; - lmo.l_prev_offset = 16; - } - - return lmp; -} - -link_map_offsets * -linux_lp64_fetch_link_map_offsets () -{ - static link_map_offsets lmo; - static link_map_offsets *lmp = nullptr; - - if (lmp == nullptr) - { - lmp = &lmo; - - lmo.r_version_offset = 0; - lmo.r_version_size = 4; - lmo.r_map_offset = 8; - lmo.r_brk_offset = 16; - lmo.r_ldsomap_offset = -1; - lmo.r_next_offset = 40; - - /* Everything we need is in the first 40 bytes. */ - lmo.link_map_size = 40; - lmo.l_addr_offset = 0; - lmo.l_name_offset = 8; - lmo.l_ld_offset = 16; - lmo.l_next_offset = 24; - lmo.l_prev_offset = 32; - } - return lmp; } diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h index 7485fc1..3d82ea5 100644 --- a/gdb/linux-tdep.h +++ b/gdb/linux-tdep.h @@ -22,6 +22,7 @@ #include "bfd.h" #include "displaced-stepping.h" +#include "solib.h" struct inferior; struct regcache; @@ -112,9 +113,4 @@ extern CORE_ADDR linux_get_hwcap2 (const std::optional<gdb::byte_vector> &auxv, extern CORE_ADDR linux_get_hwcap2 (); -/* Fetch (and possibly build) an appropriate `struct link_map_offsets' - for ILP32 and LP64 Linux systems. */ -extern struct link_map_offsets *linux_ilp32_fetch_link_map_offsets (); -extern struct link_map_offsets *linux_lp64_fetch_link_map_offsets (); - #endif /* GDB_LINUX_TDEP_H */ diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index f60116a..8d49508 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -1981,9 +1981,7 @@ maintenance_check_libthread_db (const char *args, int from_tty) check_thread_db (info, true); } -void _initialize_thread_db (); -void -_initialize_thread_db () +INIT_GDB_FILE (thread_db) { /* Defer loading of libthread_db.so until inferior is running. This allows gdb to load correct libthread_db for a given diff --git a/gdb/lm32-tdep.c b/gdb/lm32-tdep.c index ef956b8..cee2e51 100644 --- a/gdb/lm32-tdep.c +++ b/gdb/lm32-tdep.c @@ -536,9 +536,7 @@ lm32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_lm32_tdep (); -void -_initialize_lm32_tdep () +INIT_GDB_FILE (lm32_tdep) { gdbarch_register (bfd_arch_lm32, lm32_gdbarch_init); } diff --git a/gdb/loongarch-linux-nat.c b/gdb/loongarch-linux-nat.c index 3efaa5f..2b59b36 100644 --- a/gdb/loongarch-linux-nat.c +++ b/gdb/loongarch-linux-nat.c @@ -751,9 +751,7 @@ loongarch_linux_nat_target::low_forget_process (pid_t pid) /* Initialize LoongArch Linux native support. */ -void _initialize_loongarch_linux_nat (); -void -_initialize_loongarch_linux_nat () +INIT_GDB_FILE (loongarch_linux_nat) { linux_target = &the_loongarch_linux_nat_target; add_inf_child_target (&the_loongarch_linux_nat_target); diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c index 38485e0..3766338 100644 --- a/gdb/loongarch-linux-tdep.c +++ b/gdb/loongarch-linux-tdep.c @@ -25,6 +25,7 @@ #include "inferior.h" #include "linux-record.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "loongarch-tdep.h" #include "record-full.h" #include "regset.h" @@ -1145,10 +1146,9 @@ loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) linux_init_abi (info, gdbarch, 0); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - info.bfd_arch_info->bits_per_address == 32 - ? linux_ilp32_fetch_link_map_offsets - : linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, (info.bfd_arch_info->bits_per_address == 32 + ? make_linux_ilp32_svr4_solib_ops + : make_linux_lp64_svr4_solib_ops)); /* GNU/Linux uses SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); @@ -1183,9 +1183,7 @@ loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Initialize LoongArch Linux target support. */ -void _initialize_loongarch_linux_tdep (); -void -_initialize_loongarch_linux_tdep () +INIT_GDB_FILE (loongarch_linux_tdep) { gdbarch_register_osabi (bfd_arch_loongarch, bfd_mach_loongarch32, GDB_OSABI_LINUX, loongarch_linux_init_abi); diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c index 092127dd..cc75cd4 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) @@ -2405,9 +2421,7 @@ loongarch_process_record (struct gdbarch *gdbarch, struct regcache *regcache, return ret; } -void _initialize_loongarch_tdep (); -void -_initialize_loongarch_tdep () +INIT_GDB_FILE (loongarch_tdep) { gdbarch_register (bfd_arch_loongarch, loongarch_gdbarch_init, nullptr); } diff --git a/gdb/m32c-tdep.c b/gdb/m32c-tdep.c index f8937a2..8d99839 100644 --- a/gdb/m32c-tdep.c +++ b/gdb/m32c-tdep.c @@ -2653,9 +2653,7 @@ m32c_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_m32c_tdep (); -void -_initialize_m32c_tdep () +INIT_GDB_FILE (m32c_tdep) { gdbarch_register (bfd_arch_m32c, m32c_gdbarch_init); diff --git a/gdb/m32r-linux-nat.c b/gdb/m32r-linux-nat.c index 7607bd2..c186693 100644 --- a/gdb/m32r-linux-nat.c +++ b/gdb/m32r-linux-nat.c @@ -234,9 +234,7 @@ m32r_linux_nat_target::store_registers (struct regcache *regcache, int regno) internal_error (_("Got request to store bad register number %d."), regno); } -void _initialize_m32r_linux_nat (); -void -_initialize_m32r_linux_nat () +INIT_GDB_FILE (m32r_linux_nat) { /* Register the target. */ linux_target = &the_m32r_linux_nat_target; diff --git a/gdb/m32r-linux-tdep.c b/gdb/m32r-linux-tdep.c index 4fbe7d9..03000cc 100644 --- a/gdb/m32r-linux-tdep.c +++ b/gdb/m32r-linux-tdep.c @@ -36,6 +36,7 @@ #include "m32r-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "gdbarch.h" @@ -461,8 +462,7 @@ m32r_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* GNU/Linux uses SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); /* Core file support. */ set_gdbarch_iterate_over_regset_sections @@ -473,9 +473,7 @@ m32r_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) svr4_fetch_objfile_link_map); } -void _initialize_m32r_linux_tdep (); -void -_initialize_m32r_linux_tdep () +INIT_GDB_FILE (m32r_linux_tdep) { gdbarch_register_osabi (bfd_arch_m32r, 0, GDB_OSABI_LINUX, m32r_linux_init_abi); diff --git a/gdb/m32r-tdep.c b/gdb/m32r-tdep.c index 6b6ad03..4ac08fd 100644 --- a/gdb/m32r-tdep.c +++ b/gdb/m32r-tdep.c @@ -908,9 +908,7 @@ m32r_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_m32r_tdep (); -void -_initialize_m32r_tdep () +INIT_GDB_FILE (m32r_tdep) { gdbarch_register (bfd_arch_m32r, m32r_gdbarch_init); } diff --git a/gdb/m68hc11-tdep.c b/gdb/m68hc11-tdep.c index dd8cbda..5389fee 100644 --- a/gdb/m68hc11-tdep.c +++ b/gdb/m68hc11-tdep.c @@ -1527,9 +1527,7 @@ m68hc11_gdbarch_init (struct gdbarch_info info, return gdbarch; } -void _initialize_m68hc11_tdep (); -void -_initialize_m68hc11_tdep () +INIT_GDB_FILE (m68hc11_tdep) { gdbarch_register (bfd_arch_m68hc11, m68hc11_gdbarch_init); gdbarch_register (bfd_arch_m68hc12, m68hc11_gdbarch_init); diff --git a/gdb/m68k-bsd-nat.c b/gdb/m68k-bsd-nat.c index 04542fd..ae8fa4d 100644 --- a/gdb/m68k-bsd-nat.c +++ b/gdb/m68k-bsd-nat.c @@ -225,9 +225,7 @@ m68kbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) return 1; } -void _initialize_m68kbsd_nat (); -void -_initialize_m68kbsd_nat () +INIT_GDB_FILE (m68kbsd_nat) { add_inf_child_target (&the_m68k_bsd_nat_target); diff --git a/gdb/m68k-bsd-tdep.c b/gdb/m68k-bsd-tdep.c index 09c57c6..37c1464 100644 --- a/gdb/m68k-bsd-tdep.c +++ b/gdb/m68k-bsd-tdep.c @@ -147,13 +147,10 @@ m68kbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->struct_return = pcc_struct_return; /* NetBSD ELF uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); } -void _initialize_m68kbsd_tdep (); -void -_initialize_m68kbsd_tdep () +INIT_GDB_FILE (m68kbsd_tdep) { gdbarch_register_osabi (bfd_arch_m68k, 0, GDB_OSABI_NETBSD, m68kbsd_init_abi); diff --git a/gdb/m68k-linux-nat.c b/gdb/m68k-linux-nat.c index d43ed68..02286ff 100644 --- a/gdb/m68k-linux-nat.c +++ b/gdb/m68k-linux-nat.c @@ -508,9 +508,7 @@ ps_get_thread_area (struct ps_prochandle *ph, return PS_OK; } -void _initialize_m68k_linux_nat (); -void -_initialize_m68k_linux_nat () +INIT_GDB_FILE (m68k_linux_nat) { /* Register the target. */ linux_target = &the_m68k_linux_nat_target; diff --git a/gdb/m68k-linux-tdep.c b/gdb/m68k-linux-tdep.c index cfc37ab..bd2a14a 100644 --- a/gdb/m68k-linux-tdep.c +++ b/gdb/m68k-linux-tdep.c @@ -35,6 +35,7 @@ #include "observable.h" #include "elf/common.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "regset.h" /* Offsets (in target ints) into jmp_buf. */ @@ -407,8 +408,7 @@ m68k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Shared library handling. */ /* GNU/Linux uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); /* GNU/Linux uses the dynamic linker included in the GNU C Library. */ set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); @@ -424,9 +424,7 @@ m68k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) svr4_fetch_objfile_link_map); } -void _initialize_m68k_linux_tdep (); -void -_initialize_m68k_linux_tdep () +INIT_GDB_FILE (m68k_linux_tdep) { gdbarch_register_osabi (bfd_arch_m68k, 0, GDB_OSABI_LINUX, m68k_linux_init_abi); diff --git a/gdb/m68k-tdep.c b/gdb/m68k-tdep.c index 4e25518..94bfddb 100644 --- a/gdb/m68k-tdep.c +++ b/gdb/m68k-tdep.c @@ -1365,9 +1365,7 @@ m68k_osabi_sniffer (bfd *abfd) return osabi; } -void _initialize_m68k_tdep (); -void -_initialize_m68k_tdep () +INIT_GDB_FILE (m68k_tdep) { gdbarch_register (bfd_arch_m68k, m68k_gdbarch_init, m68k_dump_tdep); diff --git a/gdb/machoread.c b/gdb/machoread.c index cc8aca3..ffee181 100644 --- a/gdb/machoread.c +++ b/gdb/machoread.c @@ -940,9 +940,7 @@ static const struct sym_fns macho_sym_fns = { NULL, /* sym_get_probes */ }; -void _initialize_machoread (); -void -_initialize_machoread () +INIT_GDB_FILE (machoread) { add_symtab_fns (bfd_target_mach_o_flavour, &macho_sym_fns); diff --git a/gdb/macrocmd.c b/gdb/macrocmd.c index 0192a00..d805f3d 100644 --- a/gdb/macrocmd.c +++ b/gdb/macrocmd.c @@ -57,11 +57,11 @@ macro_expand_command (const char *exp, int from_tty) " expression you\n" "want to expand.")); - gdb::unique_xmalloc_ptr<macro_scope> ms = default_macro_scope (); + macro_scope ms = default_macro_scope (); - if (ms != nullptr) + if (ms.is_valid ()) { - gdb::unique_xmalloc_ptr<char> expanded = macro_expand (exp, *ms); + gdb::unique_xmalloc_ptr<char> expanded = macro_expand (exp, ms); gdb_puts ("expands to: "); gdb_puts (expanded.get ()); @@ -85,11 +85,11 @@ macro_expand_once_command (const char *exp, int from_tty) " the expression\n" "you want to expand.")); - gdb::unique_xmalloc_ptr<macro_scope> ms = default_macro_scope (); + macro_scope ms = default_macro_scope (); - if (ms != nullptr) + if (ms.is_valid ()) { - gdb::unique_xmalloc_ptr<char> expanded = macro_expand_once (exp, *ms); + gdb::unique_xmalloc_ptr<char> expanded = macro_expand_once (exp, ms); gdb_puts ("expands to: "); gdb_puts (expanded.get ()); @@ -169,7 +169,6 @@ print_macro_definition (const char *name, static void info_macro_command (const char *args, int from_tty) { - gdb::unique_xmalloc_ptr<struct macro_scope> ms; const char *name; int show_all_macros_named = 0; const char *arg_start = args; @@ -201,15 +200,15 @@ info_macro_command (const char *args, int from_tty) " of the macro\n" "whose definition you want to see.")); - ms = default_macro_scope (); + macro_scope ms = default_macro_scope (); - if (! ms) + if (!ms.is_valid ()) macro_inform_no_debuginfo (); else if (show_all_macros_named) - macro_for_each (ms->file->table, [&] (const char *macro_name, - const macro_definition *macro, - macro_source_file *source, - int line) + macro_for_each (ms.file->table, [&] (const char *macro_name, + const macro_definition *macro, + macro_source_file *source, + int line) { if (strcmp (name, macro_name) == 0) print_macro_definition (name, macro, source, line); @@ -218,12 +217,12 @@ info_macro_command (const char *args, int from_tty) { struct macro_definition *d; - d = macro_lookup_definition (ms->file, ms->line, name); + d = macro_lookup_definition (ms.file, ms.line, name); if (d) { int line; struct macro_source_file *file - = macro_definition_location (ms->file, ms->line, name, &line); + = macro_definition_location (ms.file, ms.line, name, &line); print_macro_definition (name, d, file, line); } @@ -232,7 +231,7 @@ info_macro_command (const char *args, int from_tty) gdb_printf ("The symbol `%s' has no definition as a C/C++" " preprocessor macro\n" "at ", name); - show_pp_source_pos (gdb_stdout, ms->file, ms->line); + show_pp_source_pos (gdb_stdout, ms.file, ms.line); } } } @@ -241,7 +240,7 @@ info_macro_command (const char *args, int from_tty) static void info_macros_command (const char *args, int from_tty) { - gdb::unique_xmalloc_ptr<struct macro_scope> ms; + macro_scope ms; if (args == NULL) ms = default_macro_scope (); @@ -254,10 +253,10 @@ info_macros_command (const char *args, int from_tty) ms = sal_macro_scope (sals[0]); } - if (! ms || ! ms->file || ! ms->file->table) + if (!ms.is_valid () || ms.file->table == nullptr) macro_inform_no_debuginfo (); else - macro_for_each_in_scope (ms->file, ms->line, print_macro_definition); + macro_for_each_in_scope (ms.file, ms.line, print_macro_definition); } @@ -409,9 +408,7 @@ macro_list_command (const char *exp, int from_tty) /* Initializing the `macrocmd' module. */ -void _initialize_macrocmd (); -void -_initialize_macrocmd () +INIT_GDB_FILE (macrocmd) { /* We introduce a new command prefix, `macro', under which we'll put the various commands for working with preprocessor macros. */ diff --git a/gdb/macroscope.c b/gdb/macroscope.c index 97b41b6..7aa0784 100644 --- a/gdb/macroscope.c +++ b/gdb/macroscope.c @@ -34,28 +34,29 @@ struct macro_table *macro_user_macros; -gdb::unique_xmalloc_ptr<struct macro_scope> +macro_scope sal_macro_scope (struct symtab_and_line sal) { + macro_scope result; struct macro_source_file *main_file, *inclusion; struct compunit_symtab *cust; if (sal.symtab == NULL) - return NULL; + return result; cust = sal.symtab->compunit (); if (cust->macro_table () == NULL) - return NULL; + return result; - gdb::unique_xmalloc_ptr<struct macro_scope> ms (XNEW (struct macro_scope)); + macro_scope ms; main_file = macro_main (cust->macro_table ()); inclusion = macro_lookup_inclusion (main_file, sal.symtab->filename_for_id); if (inclusion) { - ms->file = inclusion; - ms->line = sal.line; + ms.file = inclusion; + ms.line = sal.line; } else { @@ -73,8 +74,8 @@ sal_macro_scope (struct symtab_and_line sal) For the time being, though, we'll just treat these as occurring at the end of the main source file. */ - ms->file = main_file; - ms->line = -1; + ms.file = main_file; + ms.line = -1; complaint (_("symtab found for `%s', but that file\n" "is not covered in the compilation unit's macro information"), @@ -85,20 +86,16 @@ sal_macro_scope (struct symtab_and_line sal) } -gdb::unique_xmalloc_ptr<struct macro_scope> -user_macro_scope (void) +macro_scope +user_macro_scope () { - gdb::unique_xmalloc_ptr<struct macro_scope> ms (XNEW (struct macro_scope)); - ms->file = macro_main (macro_user_macros); - ms->line = -1; - return ms; + return { macro_main (macro_user_macros), -1 }; } -gdb::unique_xmalloc_ptr<struct macro_scope> -default_macro_scope (void) +macro_scope +default_macro_scope () { struct symtab_and_line sal; - gdb::unique_xmalloc_ptr<struct macro_scope> ms; frame_info_ptr frame; CORE_ADDR pc; @@ -128,8 +125,8 @@ default_macro_scope (void) sal.line = cursal.line; } - ms = sal_macro_scope (sal); - if (! ms) + macro_scope ms = sal_macro_scope (sal); + if (!ms.is_valid ()) ms = user_macro_scope (); return ms; @@ -152,9 +149,7 @@ standard_macro_lookup (const char *name, const macro_scope &ms) return result; } -void _initialize_macroscope (); -void -_initialize_macroscope () +INIT_GDB_FILE (macroscope) { macro_user_macros = new_macro_table (NULL, NULL, NULL); macro_set_main (macro_user_macros, "<user-defined>"); diff --git a/gdb/macroscope.h b/gdb/macroscope.h index de3186d..2cb6a0f 100644 --- a/gdb/macroscope.h +++ b/gdb/macroscope.h @@ -31,21 +31,25 @@ extern struct macro_table *macro_user_macros; in scope: a source file (either a main source file or an #inclusion), and a line number in that file. */ struct macro_scope { - struct macro_source_file *file; - int line; + struct macro_source_file *file = nullptr; + int line = 0; + + /* Return true if this scope is valid. */ + bool is_valid () const + { + return file != nullptr; + } }; /* Return a `struct macro_scope' object corresponding to the symtab and line given in SAL. If we have no macro information for that - location, or if SAL's pc is zero, return zero. */ -gdb::unique_xmalloc_ptr<struct macro_scope> sal_macro_scope - (struct symtab_and_line sal); - + location, or if SAL's pc is zero, return an invalid scope. */ +macro_scope sal_macro_scope (struct symtab_and_line sal); /* Return a `struct macro_scope' object representing just the user-defined macros. */ -gdb::unique_xmalloc_ptr<struct macro_scope> user_macro_scope (void); +macro_scope user_macro_scope (); /* Return a `struct macro_scope' object describing the scope the `macro expand' and `macro expand-once' commands should use for looking up @@ -54,7 +58,7 @@ gdb::unique_xmalloc_ptr<struct macro_scope> user_macro_scope (void); If we have no macro information for the current location, return the user macro scope. */ -gdb::unique_xmalloc_ptr<struct macro_scope> default_macro_scope (void); +macro_scope default_macro_scope (); /* Look up the definition of the macro named NAME in scope at the source location given by MS. */ @@ -399,7 +399,7 @@ start_event_loop () try { - result = gdb_do_one_event (); + result = current_interpreter ()->do_one_event (); } catch (const gdb_exception_forced_quit &ex) { @@ -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-test-options.c b/gdb/maint-test-options.c index 25f609b..97236c9 100644 --- a/gdb/maint-test-options.c +++ b/gdb/maint-test-options.c @@ -429,9 +429,7 @@ maintenance_test_options_unknown_is_operand_command_completer static cmd_list_element *maintenance_test_options_list; -void _initialize_maint_test_options (); -void -_initialize_maint_test_options () +INIT_GDB_FILE (maint_test_options) { cmd_list_element *cmd; diff --git a/gdb/maint-test-settings.c b/gdb/maint-test-settings.c index 663190a..c7b12c6 100644 --- a/gdb/maint-test-settings.c +++ b/gdb/maint-test-settings.c @@ -78,9 +78,7 @@ maintenance_show_test_settings_value_cmd } -void _initialize_maint_test_settings (); -void -_initialize_maint_test_settings () +INIT_GDB_FILE (maint_test_settings) { maintenance_test_settings_filename = "/foo/bar"; diff --git a/gdb/maint.c b/gdb/maint.c index f5977ec..78dea22 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) { @@ -907,9 +920,9 @@ maintenance_show_worker_threads (struct ui_file *file, int from_tty, } -/* If true, display time usage both at startup and for each command. */ +/* See maint.h. */ -static bool per_command_time; +bool per_command_time; /* If true, display space usage both at startup and for each command. */ @@ -1138,6 +1151,72 @@ set_per_command_cmd (const char *args, int from_tty) } } +/* Handle "mt set per-command time". Warn if per-thread run time + information is not possible. */ + +static void +maintenance_set_command_time_cmd (const char *args, int from_tty, + cmd_list_element *c) +{ + /* No point warning if this platform can't use multiple threads at + all. */ +#if CXX_STD_THREAD + static bool already_warned = false; + if (per_command_time + && !get_run_time_thread_scope_available () + && !already_warned) + { + warning (_("\ +per-thread run time information not available on this platform")); + already_warned = true; + } +#endif +} + +/* See maint.h. */ + +scoped_time_it::scoped_time_it (const char *what, bool enabled) + : m_enabled (enabled), + 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 @@ -1222,9 +1301,7 @@ Selftests have been disabled for this build.\n")); } -void _initialize_maint_cmds (); -void -_initialize_maint_cmds () +INIT_GDB_FILE (maint_cmds) { struct cmd_list_element *cmd; @@ -1343,6 +1420,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, @@ -1360,7 +1443,7 @@ Show whether to display per-command execution time."), _("\ If enabled, the execution time for each command will be\n\ displayed following the command's output."), - NULL, NULL, + maintenance_set_command_time_cmd, NULL, &per_command_setlist, &per_command_showlist); add_setshow_boolean_cmd ("space", class_maintenance, diff --git a/gdb/maint.h b/gdb/maint.h index 0ddc62b..6930018 100644 --- a/gdb/maint.h +++ b/gdb/maint.h @@ -61,7 +61,9 @@ class scoped_command_stats bool m_symtab_enabled : 1; run_time_clock::time_point m_start_cpu_time; std::chrono::steady_clock::time_point m_start_wall_time; +#ifdef HAVE_USEFUL_SBRK long m_start_space; +#endif /* Total number of symtabs (over all objfiles). */ int m_start_nr_symtabs; /* A count of the compunits. */ @@ -70,6 +72,37 @@ class scoped_command_stats int m_start_nr_blocks; }; +/* If true, display time usage both at startup and for each command. */ + +extern bool per_command_time; + +/* 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, bool enabled = per_command_time); + + 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/make-init-c b/gdb/make-init-c index 4f0185c..6834b43 100755 --- a/gdb/make-init-c +++ b/gdb/make-init-c @@ -25,12 +25,13 @@ # # Where SOURCE-FILES is the list of source files to extract init functions from. # -# Formatting conventions: The name of the initialization routines must begin -# with `_initialize_`, must start in column zero, and be followed with exactly -# ` ()`. For example: + +# An initialization function is introduced using the "INIT_GDB_FILE" +# macro. In ordinary code this expands to a function declaration (to +# avoid a warning) and then the start of a definition. In the +# generated init.c, though, it is expanded differently. For example: # -# void -# _initialize_foo () +# INIT_GDB_FILE (foo) # { # ... # } @@ -43,19 +44,21 @@ echo "/* Do not modify this file. */" echo "/* It is created automatically by the Makefile. */" echo "#include <algorithm>" echo "" -sed -n -e 's/^\(_initialize_[a-zA-Z0-9_]*\) ()$/\1/p' "$@" | while read -r name; do - echo "extern initialize_file_ftype $name;" -done +echo "#undef INIT_GDB_FILE" +echo "#define INIT_GDB_FILE(NAME) extern void _initialize_ ## NAME ();" +grep -h '^[ ]*INIT_GDB_FILE\b' "$@" echo "" echo "void initialize_all_files ();" echo "void" echo "initialize_all_files ()" echo "{" +echo " typedef void initialize_file_ftype (void);" +echo "" echo " std::vector<initialize_file_ftype *> functions =" echo " {" -sed -n -e 's/^\(_initialize_[a-zA-Z0-9_]*\) ()$/\1/p' "$@" | while read -r name; do - echo " $name," -done +echo "#undef INIT_GDB_FILE" +echo "#define INIT_GDB_FILE(NAME) _initialize_ ## NAME," +grep -h '^[ ]*INIT_GDB_FILE\b' "$@" echo " };" echo "" echo " /* If GDB_REVERSE_INIT_FUNCTIONS is set (any value), reverse the" diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c index 72af117..5bb15c2 100644 --- a/gdb/mdebugread.c +++ b/gdb/mdebugread.c @@ -329,7 +329,7 @@ fdr_name (FDR *f) /* Read in and parse the symtab of the file OBJFILE. Symbols from different sections are relocated via the SECTION_OFFSETS. */ -void +static void mdebug_build_psymtabs (minimal_symbol_reader &reader, struct objfile *objfile, const struct ecoff_debug_swap *swap, @@ -4792,9 +4792,28 @@ elfmdebug_build_psymtabs (struct objfile *objfile, reader.install (); } -void _initialize_mdebugread (); +/* see mdebugread.h. */ + void -_initialize_mdebugread () +mipsmdebug_build_psymtabs (struct objfile *objfile, + const struct ecoff_debug_swap *swap, + struct ecoff_debug_info *info) +{ + bfd *abfd = objfile->obfd.get (); + + minimal_symbol_reader reader (objfile); + + if (!(*swap->read_debug_info) (abfd, nullptr, + info)) + error (_("Error reading ECOFF debugging information: %s"), + bfd_errmsg (bfd_get_error ())); + + mdebug_build_psymtabs (reader, objfile, swap, info); + + reader.install (); +} + +INIT_GDB_FILE (mdebugread) { mdebug_register_index = register_symbol_register_impl (LOC_REGISTER, &mdebug_register_funcs); diff --git a/gdb/mdebugread.h b/gdb/mdebugread.h index be26f46..f7e9f98 100644 --- a/gdb/mdebugread.h +++ b/gdb/mdebugread.h @@ -37,13 +37,38 @@ struct mdebug_extra_func_info #define MDEBUG_EFI_SYMBOL_NAME "__GDB_EFI_INFO__" -extern void mdebug_build_psymtabs (minimal_symbol_reader &, - struct objfile *, - const struct ecoff_debug_swap *, - struct ecoff_debug_info *); +#if defined(MDEBUG_FORMAT_AVAILABLE) extern void elfmdebug_build_psymtabs (struct objfile *, const struct ecoff_debug_swap *, asection *); +/* Read ECOFF debugging information from a BFD section. This is + called from mipsread.c. It parses the section into a + ecoff_debug_info struct, and then lets the rest of the file handle + it as normal. */ +extern void mipsmdebug_build_psymtabs (struct objfile *, + const struct ecoff_debug_swap *, + struct ecoff_debug_info *); + +#else /* MDEBUG_FORMAT_AVAILABLE */ + +static inline void +elfmdebug_build_psymtabs (struct objfile *, + const struct ecoff_debug_swap *, + asection *) +{ + warning (_("No mdebug support available")); +} + +static inline void +mipsmdebug_build_psymtabs (struct objfile *, + const struct ecoff_debug_swap *, + struct ecoff_debug_info *) +{ + warning (_("No mdebug support available")); +} + +#endif /* MDEBUG_FORMAT_AVAILABLE */ + #endif /* GDB_MDEBUGREAD_H */ diff --git a/gdb/memattr.c b/gdb/memattr.c index 81d0de0..9d26d31 100644 --- a/gdb/memattr.c +++ b/gdb/memattr.c @@ -592,9 +592,7 @@ delete_mem_command (const char *args, int from_tty) static struct cmd_list_element *mem_set_cmdlist; static struct cmd_list_element *mem_show_cmdlist; -void _initialize_mem (); -void -_initialize_mem () +INIT_GDB_FILE (mem) { add_com ("mem", class_vars, mem_command, _("\ Define or reset attributes for memory regions.\n\ diff --git a/gdb/mep-tdep.c b/gdb/mep-tdep.c index 60bae3a..30a6018 100644 --- a/gdb/mep-tdep.c +++ b/gdb/mep-tdep.c @@ -2459,9 +2459,7 @@ mep_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_mep_tdep (); -void -_initialize_mep_tdep () +INIT_GDB_FILE (mep_tdep) { mep_csr_reggroup = reggroup_new ("csr", USER_REGGROUP); mep_cr_reggroup = reggroup_new ("cr", USER_REGGROUP); diff --git a/gdb/mi/mi-cmd-env.c b/gdb/mi/mi-cmd-env.c index e79af9c..bc2fe77 100644 --- a/gdb/mi/mi-cmd-env.c +++ b/gdb/mi/mi-cmd-env.c @@ -236,9 +236,7 @@ mi_cmd_inferior_tty_show (const char *command, const char *const *argv, current_uiout->field_string ("inferior_tty_terminal", inferior_tty); } -void _initialize_mi_cmd_env (); -void -_initialize_mi_cmd_env () +INIT_GDB_FILE (mi_cmd_env) { const char *env; diff --git a/gdb/mi/mi-cmd-file.c b/gdb/mi/mi-cmd-file.c index 2354012..cf7991f 100644 --- a/gdb/mi/mi-cmd-file.c +++ b/gdb/mi/mi-cmd-file.c @@ -25,7 +25,6 @@ #include "symtab.h" #include "source.h" #include "solib.h" -#include "solist.h" /* Return to the client the absolute path and line number of the current file being executed. */ @@ -163,10 +162,10 @@ mi_cmd_file_list_shared_libraries (const char *command, for (const solib &so : current_program_space->solibs ()) { - if (so.so_name.empty ()) + if (so.name.empty ()) continue; - if (pattern != nullptr && !re_exec (so.so_name.c_str ())) + if (pattern != nullptr && !re_exec (so.name.c_str ())) continue; ui_out_emit_tuple tuple_emitter (uiout, NULL); diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c index 53cdcc9..abd3fb9 100644 --- a/gdb/mi/mi-cmds.c +++ b/gdb/mi/mi-cmds.c @@ -369,9 +369,7 @@ mi_cmd_lookup (const char *command) return it->second.get (); } -void _initialize_mi_cmds (); -void -_initialize_mi_cmds () +INIT_GDB_FILE (mi_cmds) { add_builtin_mi_commands (); } diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index ae1070e..8373e1a 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -34,7 +34,7 @@ #include "mi-common.h" #include "observable.h" #include "gdbthread.h" -#include "solist.h" +#include "solib.h" #include "objfiles.h" #include "tracepoint.h" #include "cli-out.h" @@ -726,9 +726,9 @@ mi_output_solib_attribs_1 (ui_out *uiout, const solib &solib, { gdbarch *gdbarch = current_inferior ()->arch (); - uiout->field_string ("id", solib.so_original_name); - uiout->field_string ("target-name", solib.so_original_name); - uiout->field_string ("host-name", solib.so_name); + uiout->field_string ("id", solib.original_name); + uiout->field_string ("target-name", solib.original_name); + uiout->field_string ("host-name", solib.name); if (include_symbols_loaded_p) uiout->field_signed ("symbols-loaded", solib.symbols_loaded); if (!gdbarch_has_global_solist (current_inferior ()->arch ())) @@ -935,9 +935,7 @@ mi_interp_factory (const char *name) return new mi_interp (name); } -void _initialize_mi_interp (); -void -_initialize_mi_interp () +INIT_GDB_FILE (mi_interp) { /* The various interpreter levels. */ interp_factory_register (INTERP_MI2, mi_interp_factory); diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index 850a9ab..789e6fa 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 @@ -2768,9 +2768,7 @@ mi_parse_thread_group_id (const char *id) return (int) num; } -void _initialize_mi_main (); -void -_initialize_mi_main () +INIT_GDB_FILE (mi_main) { set_show_commands mi_async_cmds = add_setshow_boolean_cmd ("mi-async", class_run, diff --git a/gdb/microblaze-linux-tdep.c b/gdb/microblaze-linux-tdep.c index 8dcbeaa..0fdda18 100644 --- a/gdb/microblaze-linux-tdep.c +++ b/gdb/microblaze-linux-tdep.c @@ -35,6 +35,7 @@ #include "frame-unwind.h" #include "tramp-frame.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" static int microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch, @@ -125,17 +126,14 @@ microblaze_linux_init_abi (struct gdbarch_info info, microblaze_linux_memory_remove_breakpoint); /* Shared library handling. */ - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); /* Trampolines. */ tramp_frame_prepend_unwinder (gdbarch, µblaze_linux_sighandler_tramp_frame); } -void _initialize_microblaze_linux_tdep (); -void -_initialize_microblaze_linux_tdep () +INIT_GDB_FILE (microblaze_linux_tdep) { gdbarch_register_osabi (bfd_arch_microblaze, 0, GDB_OSABI_LINUX, microblaze_linux_init_abi); diff --git a/gdb/microblaze-tdep.c b/gdb/microblaze-tdep.c index bc728b6..7b58220 100644 --- a/gdb/microblaze-tdep.c +++ b/gdb/microblaze-tdep.c @@ -736,9 +736,7 @@ microblaze_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_microblaze_tdep (); -void -_initialize_microblaze_tdep () +INIT_GDB_FILE (microblaze_tdep) { gdbarch_register (bfd_arch_microblaze, microblaze_gdbarch_init); diff --git a/gdb/mingw-hdep.c b/gdb/mingw-hdep.c index dc7ca42..481bd41 100644 --- a/gdb/mingw-hdep.c +++ b/gdb/mingw-hdep.c @@ -22,6 +22,9 @@ #include "gdbsupport/event-loop.h" #include "gdbsupport/gdb_select.h" #include "inferior.h" +#include "cli/cli-style.h" +#include "command.h" +#include "cli/cli-cmds.h" #include <windows.h> #include <signal.h> @@ -212,7 +215,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 +249,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) @@ -437,3 +447,65 @@ install_sigint_handler (c_c_handler_ftype *fn) current_handler = fn; return result; } + +/* Set stdout and stderr handles to translation mode MODE. */ + +static void +set_console_translation_mode (int mode) +{ + setmode (fileno (stdout), mode); + setmode (fileno (stderr), mode); +} + +/* Arg in "maint set console-translation-mode <arg>. */ + +static std::string maint_console_translation_mode; + +/* Current value of "maint set/show console-translation-mode". */ + +static std::string console_translation_mode = "unknown"; + +/* Sets the console translation mode. */ + +static void +set_maint_console_translation_mode (const char *args, int from_tty, + struct cmd_list_element *c) +{ + if (maint_console_translation_mode == "binary") + set_console_translation_mode (O_BINARY); + else if (maint_console_translation_mode == "text") + set_console_translation_mode (O_TEXT); + else + error (_("Invalid console translation mode: %s"), + maint_console_translation_mode.c_str ()); + + console_translation_mode = maint_console_translation_mode; +} + +/* Shows the console translation mode. */ + +static void +show_maint_console_translation_mode (struct ui_file *file, int from_tty, + struct cmd_list_element *c, + const char *value) +{ + gdb_printf (file, _("Console translation mode is %s.\n"), + console_translation_mode.c_str ()); +} + +extern void _initialize_mingw_hdep (); + +void +_initialize_mingw_hdep () +{ + add_setshow_string_cmd ("console-translation-mode", + class_maintenance, + &maint_console_translation_mode, _("\ +Set the translation mode of stdout/stderr."), _("\ +Show the translation mode of stdout/stderr."), _("\ +Use \"binary\", or \"text\""), + set_maint_console_translation_mode, + show_maint_console_translation_mode, + &maintenance_set_cmdlist, + &maintenance_show_cmdlist); +} diff --git a/gdb/minsyms.c b/gdb/minsyms.c index 649a9f1..4a6459a 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" @@ -1478,9 +1479,11 @@ minimal_symbol_reader::install () msymbols = m_objfile->per_bfd->msymbols.get (); /* Arbitrarily require at least 10 elements in a thread. */ - gdb::parallel_for_each (10, &msymbols[0], &msymbols[mcount], + 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; @@ -1688,7 +1691,8 @@ find_minsym_type_and_address (minimal_symbol *msymbol, { /* Skip translation if caller does not need the address. */ if (address_p != NULL) - *address_p = target_translate_tls_address (objfile, addr); + *address_p = target_translate_tls_address + (objfile, addr, bound_msym.minsym->print_name ()); return builtin_type (objfile)->nodebug_tls_symbol; } diff --git a/gdb/mips-fbsd-nat.c b/gdb/mips-fbsd-nat.c index 667a4bc..8d97849 100644 --- a/gdb/mips-fbsd-nat.c +++ b/gdb/mips-fbsd-nat.c @@ -125,9 +125,7 @@ mips_fbsd_nat_target::store_registers (struct regcache *regcache, int regnum) } } -void _initialize_mips_fbsd_nat (); -void -_initialize_mips_fbsd_nat () +INIT_GDB_FILE (mips_fbsd_nat) { add_inf_child_target (&the_mips_fbsd_nat_target); } diff --git a/gdb/mips-fbsd-tdep.c b/gdb/mips-fbsd-tdep.c index c280527..c1b5f9c 100644 --- a/gdb/mips-fbsd-tdep.c +++ b/gdb/mips-fbsd-tdep.c @@ -476,12 +476,28 @@ mips_fbsd_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc) return fbsd_skip_solib_resolver (gdbarch, pc); } -/* FreeBSD/mips uses a slightly different `struct link_map' than the - other FreeBSD platforms as it includes an additional `l_off' - member. */ +/* solib_ops for ILP32 FreeBSD/MIPS systems. */ -static struct link_map_offsets * -mips_fbsd_ilp32_fetch_link_map_offsets (void) +struct mips_fbsd_ilp32_solib_ops : public svr4_solib_ops +{ + /* FreeBSD/MIPS uses a slightly different `struct link_map' than the + other FreeBSD platforms as it includes an additional `l_off' member. */ + + link_map_offsets *fetch_link_map_offsets () const override; +}; + +/* Return a new solib_ops for ILP32 FreeBSD/MIPS systems. */ + +static solib_ops_up +make_mips_fbsd_ilp32_solib_ops () +{ + return std::make_unique<mips_fbsd_ilp32_solib_ops> (); +} + +/* See mips_fbsd_ilp32_solib_ops. */ + +link_map_offsets * +mips_fbsd_ilp32_solib_ops::fetch_link_map_offsets () const { static struct link_map_offsets lmo; static struct link_map_offsets *lmp = NULL; @@ -508,8 +524,28 @@ mips_fbsd_ilp32_fetch_link_map_offsets (void) return lmp; } -static struct link_map_offsets * -mips_fbsd_lp64_fetch_link_map_offsets (void) +/* solib_ops for LP64 FreeBSD/MIPS systems. */ + +struct mips_fbsd_lp64_solib_ops : public svr4_solib_ops +{ + /* FreeBSD/MIPS uses a slightly different `struct link_map' than the + other FreeBSD platforms as it includes an additional `l_off' member. */ + + link_map_offsets *fetch_link_map_offsets () const override; +}; + +/* Return a new solib_ops for LP64 FreeBSD/MIPS systems. */ + +static solib_ops_up +make_mips_fbsd_lp64_solib_ops () +{ + return std::make_unique<mips_fbsd_lp64_solib_ops> (); +} + +/* See mips_fbsd_lp64_solib_ops. */ + +link_map_offsets * +mips_fbsd_lp64_solib_ops::fetch_link_map_offsets () const { static struct link_map_offsets lmo; static struct link_map_offsets *lmp = NULL; @@ -565,15 +601,12 @@ mips_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_skip_solib_resolver (gdbarch, mips_fbsd_skip_solib_resolver); /* FreeBSD/mips has SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 ? - mips_fbsd_ilp32_fetch_link_map_offsets : - mips_fbsd_lp64_fetch_link_map_offsets)); + set_solib_svr4_ops (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 + ? make_mips_fbsd_ilp32_solib_ops + : make_mips_fbsd_lp64_solib_ops)); } -void _initialize_mips_fbsd_tdep (); -void -_initialize_mips_fbsd_tdep () +INIT_GDB_FILE (mips_fbsd_tdep) { gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_FREEBSD, mips_fbsd_init_abi); diff --git a/gdb/mips-linux-nat.c b/gdb/mips-linux-nat.c index b4b5788..e22e904 100644 --- a/gdb/mips-linux-nat.c +++ b/gdb/mips-linux-nat.c @@ -784,9 +784,7 @@ mips_linux_nat_target::close () linux_nat_trad_target::close (); } -void _initialize_mips_linux_nat (); -void -_initialize_mips_linux_nat () +INIT_GDB_FILE (mips_linux_nat) { add_setshow_boolean_cmd ("show-debug-regs", class_maintenance, &show_debug_regs, _("\ diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c index 418892b..e4fa243 100644 --- a/gdb/mips-linux-tdep.c +++ b/gdb/mips-linux-tdep.c @@ -30,13 +30,13 @@ #include "gdbtypes.h" #include "objfiles.h" #include "solib.h" -#include "solist.h" #include "symtab.h" #include "target-descriptions.h" #include "regset.h" #include "mips-linux-tdep.h" #include "glibc-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "xml-syscall.h" #include "gdbsupport/gdb_signals.h" #include "inferior.h" @@ -46,8 +46,6 @@ #include "features/mips64-linux.c" #include "features/mips64-dsp-linux.c" -static solib_ops mips_svr4_so_ops; - /* This enum represents the signals' numbers on the MIPS architecture. It just contains the signal definitions which are different from the generic implementation. @@ -667,24 +665,57 @@ mips_linux_in_dynsym_stub (CORE_ADDR pc) return 1; } -/* Return non-zero iff PC belongs to the dynamic linker resolution - code, a PLT entry, or a lazy binding stub. */ +/* Mix-in class to add Linux/MIPS-specific methods to a base solib_ops + class. */ -static int -mips_linux_in_dynsym_resolve_code (CORE_ADDR pc) +template <typename Base> +struct mips_linux_svr4_solib_ops : public Base +{ + bool in_dynsym_resolve_code (CORE_ADDR pc) const override; +}; + +template <typename Base> +bool +mips_linux_svr4_solib_ops<Base>::in_dynsym_resolve_code (CORE_ADDR pc) const { /* Check whether PC is in the dynamic linker. This also checks whether it is in the .plt section, used by non-PIC executables. */ - if (svr4_in_dynsym_resolve_code (pc)) - return 1; + if (Base::in_dynsym_resolve_code (pc)) + return true; /* Likewise for the stubs. They live in the .MIPS.stubs section these days, so we check if the PC is within, than fall back to a pattern match. */ if (mips_linux_in_dynsym_stub (pc)) - return 1; + return true; - return 0; + return false; +} + +/* solib_ops for ILP32 Linux/MIPS systems. */ + +using mips_linux_ilp32_svr4_solib_ops + = mips_linux_svr4_solib_ops<linux_ilp32_svr4_solib_ops>; + +/* Return a new solib_ops for ILP32 Linux/MIPS systems. */ + +static solib_ops_up +make_mips_linux_ilp32_svr4_solib_ops () +{ + return std::make_unique<mips_linux_ilp32_svr4_solib_ops> (); +} + +/* solib_ops for LP64 Linux/MIPS systems. */ + +using mips_linux_lp64_svr4_solib_ops + = mips_linux_svr4_solib_ops<linux_lp64_svr4_solib_ops>; + +/* Return a new solib_ops for LP64 Linux/MIPS systems. */ + +static solib_ops_up +make_mips_linux_lp64_svr4_solib_ops () +{ + return std::make_unique<mips_linux_lp64_svr4_solib_ops> (); } /* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c, @@ -1538,8 +1569,7 @@ mips_linux_init_abi (struct gdbarch_info info, case MIPS_ABI_O32: set_gdbarch_get_longjmp_target (gdbarch, mips_linux_get_longjmp_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_mips_linux_ilp32_svr4_solib_ops); tramp_frame_prepend_unwinder (gdbarch, µmips_linux_o32_sigframe); tramp_frame_prepend_unwinder (gdbarch, µmips_linux_o32_rt_sigframe); @@ -1550,8 +1580,7 @@ mips_linux_init_abi (struct gdbarch_info info, case MIPS_ABI_N32: set_gdbarch_get_longjmp_target (gdbarch, mips_linux_get_longjmp_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_mips_linux_ilp32_svr4_solib_ops); set_gdbarch_long_double_bit (gdbarch, 128); set_gdbarch_long_double_format (gdbarch, floatformats_ieee_quad); tramp_frame_prepend_unwinder (gdbarch, @@ -1562,8 +1591,7 @@ mips_linux_init_abi (struct gdbarch_info info, case MIPS_ABI_N64: set_gdbarch_get_longjmp_target (gdbarch, mips64_linux_get_longjmp_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_mips_linux_lp64_svr4_solib_ops); set_gdbarch_long_double_bit (gdbarch, 128); set_gdbarch_long_double_format (gdbarch, floatformats_ieee_quad); tramp_frame_prepend_unwinder (gdbarch, @@ -1583,16 +1611,6 @@ mips_linux_init_abi (struct gdbarch_info info, set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); - /* Initialize this lazily, to avoid an initialization order - dependency on solib-svr4.c's _initialize routine. */ - if (mips_svr4_so_ops.in_dynsym_resolve_code == NULL) - { - mips_svr4_so_ops = svr4_so_ops; - mips_svr4_so_ops.in_dynsym_resolve_code - = mips_linux_in_dynsym_resolve_code; - } - set_gdbarch_so_ops (gdbarch, &mips_svr4_so_ops); - set_gdbarch_write_pc (gdbarch, mips_linux_write_pc); set_gdbarch_core_read_description (gdbarch, @@ -1629,9 +1647,7 @@ mips_linux_init_abi (struct gdbarch_info info, } } -void _initialize_mips_linux_tdep (); -void -_initialize_mips_linux_tdep () +INIT_GDB_FILE (mips_linux_tdep) { const struct bfd_arch_info *arch_info; diff --git a/gdb/mips-netbsd-nat.c b/gdb/mips-netbsd-nat.c index 51e3878..91b870b 100644 --- a/gdb/mips-netbsd-nat.c +++ b/gdb/mips-netbsd-nat.c @@ -115,9 +115,7 @@ mips_nbsd_nat_target::store_registers (struct regcache *regcache, int regno) } } -void _initialize_mipsnbsd_nat (); -void -_initialize_mipsnbsd_nat () +INIT_GDB_FILE (mipsnbsd_nat) { add_inf_child_target (&the_mips_nbsd_nat_target); } diff --git a/gdb/mips-netbsd-tdep.c b/gdb/mips-netbsd-tdep.c index c9bdaa6..42eb515 100644 --- a/gdb/mips-netbsd-tdep.c +++ b/gdb/mips-netbsd-tdep.c @@ -288,13 +288,27 @@ mipsnbsd_cannot_store_register (struct gdbarch *gdbarch, int regno) || regno == mips_regnum (gdbarch)->fp_implementation_revision); } -/* Shared library support. */ +/* solib_ops for ILP32 NetBSD/MIPS systems. */ -/* NetBSD/mips uses a slightly different `struct link_map' than the - other NetBSD platforms. */ +struct mips_nbsd_ilp32_svr4_solib_ops : public svr4_solib_ops +{ + /* NetBSD/MIPS uses a slightly different `struct link_map' than the + other NetBSD platforms. */ + link_map_offsets *fetch_link_map_offsets () const override; +}; + +/* Return a new solib_ops for ILP32 NetBSD/MIPS systems. */ -static struct link_map_offsets * -mipsnbsd_ilp32_fetch_link_map_offsets (void) +static solib_ops_up +make_mips_nbsd_ilp32_svr4_solib_ops () +{ + return std::make_unique<mips_nbsd_ilp32_svr4_solib_ops> (); +} + +/* See mips_nbsd_ilp32_svr4_solib_ops. */ + +link_map_offsets * +mips_nbsd_ilp32_svr4_solib_ops::fetch_link_map_offsets () const { static struct link_map_offsets lmo; static struct link_map_offsets *lmp = NULL; @@ -322,8 +336,27 @@ mipsnbsd_ilp32_fetch_link_map_offsets (void) return lmp; } -static struct link_map_offsets * -mipsnbsd_lp64_fetch_link_map_offsets (void) +/* solib_ops for LP64 NetBSD/MIPS systems. */ + +struct mips_nbsd_lp64_svr4_solib_ops : public svr4_solib_ops +{ + /* NetBSD/MIPS uses a slightly different `struct link_map' than the + other NetBSD platforms. */ + link_map_offsets *fetch_link_map_offsets () const override; +}; + +/* Return a new solib_ops for LP64 NetBSD/MIPS systems. */ + +static solib_ops_up +make_mips_nbsd_lp64_svr4_solib_ops () +{ + return std::make_unique<mips_nbsd_lp64_svr4_solib_ops> (); +} + +/* See mips_nbsd_lp64_svr4_solib_ops. */ + +link_map_offsets * +mips_nbsd_lp64_svr4_solib_ops::fetch_link_map_offsets () const { static struct link_map_offsets lmo; static struct link_map_offsets *lmp = NULL; @@ -369,15 +402,12 @@ mipsnbsd_init_abi (struct gdbarch_info info, set_gdbarch_software_single_step (gdbarch, mips_software_single_step); /* NetBSD/mips has SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 ? - mipsnbsd_ilp32_fetch_link_map_offsets : - mipsnbsd_lp64_fetch_link_map_offsets)); + set_solib_svr4_ops (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 + ? make_mips_nbsd_ilp32_svr4_solib_ops + : make_mips_nbsd_lp64_svr4_solib_ops)); } -void _initialize_mipsnbsd_tdep (); -void -_initialize_mipsnbsd_tdep () +INIT_GDB_FILE (mipsnbsd_tdep) { gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_NETBSD, mipsnbsd_init_abi); diff --git a/gdb/mips-sde-tdep.c b/gdb/mips-sde-tdep.c index 1f50ef0..5278afb 100644 --- a/gdb/mips-sde-tdep.c +++ b/gdb/mips-sde-tdep.c @@ -254,9 +254,7 @@ mips_sde_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) frame_base_append_sniffer (gdbarch, mips_sde_frame_base_sniffer); } -void _initialize_mips_sde_tdep (); -void -_initialize_mips_sde_tdep () +INIT_GDB_FILE (mips_sde_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_mips, bfd_target_elf_flavour, diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c index c64cd47..32f832b 100644 --- a/gdb/mips-tdep.c +++ b/gdb/mips-tdep.c @@ -8972,9 +8972,7 @@ mips_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) mips_fpu_type_str (mips_get_fpu_type (gdbarch))); } -void _initialize_mips_tdep (); -void -_initialize_mips_tdep () +INIT_GDB_FILE (mips_tdep) { static struct cmd_list_element *mipsfpulist = NULL; diff --git a/gdb/mips64-obsd-nat.c b/gdb/mips64-obsd-nat.c index 26fc1b9..d77c77e 100644 --- a/gdb/mips64-obsd-nat.c +++ b/gdb/mips64-obsd-nat.c @@ -114,9 +114,7 @@ mips64_obsd_nat_target::store_registers (struct regcache *regcache, int regnum) perror_with_name (_("Couldn't write registers")); } -void _initialize_mips64obsd_nat (); -void -_initialize_mips64obsd_nat () +INIT_GDB_FILE (mips64obsd_nat) { add_inf_child_target (&the_mips64_obsd_nat_target); } diff --git a/gdb/mips64-obsd-tdep.c b/gdb/mips64-obsd-tdep.c index d0c9a1c..b9a6fe5 100644 --- a/gdb/mips64-obsd-tdep.c +++ b/gdb/mips64-obsd-tdep.c @@ -150,13 +150,10 @@ mips64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) obsd_init_abi(info, gdbarch); /* OpenBSD/mips64 has SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); } -void _initialize_mips64obsd_tdep (); -void -_initialize_mips64obsd_tdep () +INIT_GDB_FILE (mips64obsd_tdep) { gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_OPENBSD, mips64obsd_init_abi); diff --git a/gdb/mipsread.c b/gdb/mipsread.c index 5ffb97f..1d76b8f 100644 --- a/gdb/mipsread.c +++ b/gdb/mipsread.c @@ -66,20 +66,16 @@ mipscoff_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags) { bfd *abfd = objfile->obfd.get (); - minimal_symbol_reader reader (objfile); - /* Now that the executable file is positioned at symbol table, process it and define symbols accordingly. */ - if (!((*ecoff_backend (abfd)->debug_swap.read_debug_info) - (abfd, NULL, &ecoff_data (abfd)->debug_info))) - error (_("Error reading symbol table: %s"), bfd_errmsg (bfd_get_error ())); - - mdebug_build_psymtabs (reader, objfile, &ecoff_backend (abfd)->debug_swap, - &ecoff_data (abfd)->debug_info); + mipsmdebug_build_psymtabs (objfile, &ecoff_backend (abfd)->debug_swap, + &ecoff_data (abfd)->debug_info); /* Add alpha coff dynamic symbols. */ + minimal_symbol_reader reader (objfile); + read_alphacoff_dynamic_symtab (reader, objfile); /* Install any minimal symbols that have been collected as the current @@ -374,9 +370,7 @@ static const struct sym_fns ecoff_sym_fns = NULL, /* sym_probe_fns */ }; -void _initialize_mipsread (); -void -_initialize_mipsread () +INIT_GDB_FILE (mipsread) { add_symtab_fns (bfd_target_ecoff_flavour, &ecoff_sym_fns); } diff --git a/gdb/mn10300-linux-tdep.c b/gdb/mn10300-linux-tdep.c index 3334ca0..6c31241 100644 --- a/gdb/mn10300-linux-tdep.c +++ b/gdb/mn10300-linux-tdep.c @@ -29,6 +29,7 @@ #include "trad-frame.h" #include "tramp-frame.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "gdbarch.h" /* Transliterated from <asm-mn10300/elf.h>... */ @@ -707,16 +708,13 @@ am33_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_iterate_over_regset_sections (gdbarch, am33_iterate_over_regset_sections); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); tramp_frame_prepend_unwinder (gdbarch, &am33_linux_sigframe); tramp_frame_prepend_unwinder (gdbarch, &am33_linux_rt_sigframe); } -void _initialize_mn10300_linux_tdep (); -void -_initialize_mn10300_linux_tdep () +INIT_GDB_FILE (mn10300_linux_tdep) { gdbarch_register_osabi (bfd_arch_mn10300, 0, GDB_OSABI_LINUX, am33_linux_init_osabi); diff --git a/gdb/mn10300-tdep.c b/gdb/mn10300-tdep.c index c82a302..fbaeacf 100644 --- a/gdb/mn10300-tdep.c +++ b/gdb/mn10300-tdep.c @@ -1413,9 +1413,7 @@ mn10300_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) tdep->am33_mode); } -void _initialize_mn10300_tdep (); -void -_initialize_mn10300_tdep () +INIT_GDB_FILE (mn10300_tdep) { gdbarch_register (bfd_arch_mn10300, mn10300_gdbarch_init, mn10300_dump_tdep); } diff --git a/gdb/moxie-tdep.c b/gdb/moxie-tdep.c index ddb9efb..2239406 100644 --- a/gdb/moxie-tdep.c +++ b/gdb/moxie-tdep.c @@ -1101,9 +1101,7 @@ moxie_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Register this machine's init routine. */ -void _initialize_moxie_tdep (); -void -_initialize_moxie_tdep () +INIT_GDB_FILE (moxie_tdep) { gdbarch_register (bfd_arch_moxie, moxie_gdbarch_init); } diff --git a/gdb/msp430-tdep.c b/gdb/msp430-tdep.c index 441831e..7ac26b9 100644 --- a/gdb/msp430-tdep.c +++ b/gdb/msp430-tdep.c @@ -996,9 +996,7 @@ msp430_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Register the initialization routine. */ -void _initialize_msp430_tdep (); -void -_initialize_msp430_tdep () +INIT_GDB_FILE (msp430_tdep) { gdbarch_register (bfd_arch_msp430, msp430_gdbarch_init); } diff --git a/gdb/nat/linux-namespaces.c b/gdb/nat/linux-namespaces.c index ea94fdd..2a65fb0 100644 --- a/gdb/nat/linux-namespaces.c +++ b/gdb/nat/linux-namespaces.c @@ -233,6 +233,12 @@ enum mnsh_msg_type MNSH_RET_INT. */ MNSH_REQ_SETNS, + /* A request that the helper call lstat. The single + argument (the filename) should be passed in BUF, and + should include a terminating NUL character. The helper + should respond with a MNSH_RET_INTSTR. */ + MNSH_REQ_LSTAT, + /* A request that the helper call open. Arguments should be passed in BUF, INT1 and INT2. The filename (in BUF) should include a terminating NUL character. The helper @@ -265,62 +271,69 @@ enum mnsh_msg_type MNSH_RET_INTSTR, }; -/* Print a string representation of a message using debug_printf. - This function is not async-signal-safe so should never be - called from the helper. */ +/* Return a string representation of a message. This function is not + async-signal-safe so should never be called from the helper. */ -static void +static std::string mnsh_debug_print_message (enum mnsh_msg_type type, int fd, int int1, int int2, const void *buf, int bufsiz) { + std::string res; + gdb_byte *c = (gdb_byte *) buf; gdb_byte *cl = c + bufsiz; switch (type) { case MNSH_MSG_ERROR: - debug_printf ("ERROR"); + res += "ERROR"; + break; + + case MNSH_REQ_LSTAT: + res += "LSTAT"; break; case MNSH_REQ_SETNS: - debug_printf ("SETNS"); + res += "SETNS"; break; case MNSH_REQ_OPEN: - debug_printf ("OPEN"); + res += "OPEN"; break; case MNSH_REQ_UNLINK: - debug_printf ("UNLINK"); + res += "UNLINK"; break; case MNSH_REQ_READLINK: - debug_printf ("READLINK"); + res += "READLINK"; break; case MNSH_RET_INT: - debug_printf ("INT"); + res += "INT"; break; case MNSH_RET_FD: - debug_printf ("FD"); + res += "FD"; break; case MNSH_RET_INTSTR: - debug_printf ("INTSTR"); + res += "INTSTR"; break; default: - debug_printf ("unknown-packet-%d", type); + res += string_printf ("unknown-packet-%d", type); } - debug_printf (" %d %d %d \"", fd, int1, int2); + res += string_printf (" %d %d %d \"", fd, int1, int2); for (; c < cl; c++) - debug_printf (*c >= ' ' && *c <= '~' ? "%c" : "\\%o", *c); + res += string_printf (*c >= ' ' && *c <= '~' ? "%c" : "\\%o", *c); - debug_printf ("\""); + res += "\""; + + return res; } /* Forward declaration. */ @@ -390,12 +403,10 @@ mnsh_send_message (int sock, enum mnsh_msg_type type, if (size < 0) mnsh_maybe_mourn_peer (); - if (debug_linux_namespaces) - { - debug_printf ("mnsh: send: "); - mnsh_debug_print_message (type, fd, int1, int2, buf, bufsiz); - debug_printf (" -> %s\n", pulongest (size)); - } + linux_namespaces_debug_printf + ("send: %s -> %s", + mnsh_debug_print_message (type, fd, int1, int2, buf, bufsiz).c_str (), + pulongest (size)); return size; } @@ -445,9 +456,8 @@ mnsh_recv_message (int sock, enum mnsh_msg_type *type, size = recvmsg (sock, &msg, MSG_CMSG_CLOEXEC); if (size < 0) { - if (debug_linux_namespaces) - debug_printf ("namespace-helper: recv failed (%s)\n", - pulongest (size)); + linux_namespaces_debug_printf + ("namespace-helper: recv failed (%s)", pulongest (size)); mnsh_maybe_mourn_peer (); @@ -457,9 +467,9 @@ mnsh_recv_message (int sock, enum mnsh_msg_type *type, /* Check for truncation. */ if (size < fixed_size || (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC))) { - if (debug_linux_namespaces) - debug_printf ("namespace-helper: recv truncated (%s 0x%x)\n", - pulongest (size), msg.msg_flags); + linux_namespaces_debug_printf + ("namespace-helper: recv truncated (%s 0x%x)", + pulongest (size), msg.msg_flags); mnsh_maybe_mourn_peer (); @@ -477,13 +487,10 @@ mnsh_recv_message (int sock, enum mnsh_msg_type *type, else *fd = -1; - if (debug_linux_namespaces) - { - debug_printf ("mnsh: recv: "); - mnsh_debug_print_message (*type, *fd, *int1, *int2, buf, - size - fixed_size); - debug_printf ("\n"); - } + linux_namespaces_debug_printf + ("recv: %s", + mnsh_debug_print_message (*type, *fd, *int1, *int2, buf, + size - fixed_size).c_str ()); /* Return the number of bytes of data in BUF. */ return size - fixed_size; @@ -514,6 +521,20 @@ mnsh_handle_setns (int sock, int fd, int nstype) return mnsh_return_int (sock, result, errno); } + +/* Handle a MNSH_REQ_LSTAT message. Must be async-signal-safe. */ + +static ssize_t +mnsh_handle_lstat (int sock, const char *filename) +{ + struct stat sb; + int stat_ok = lstat (filename, &sb); + + return mnsh_return_intstr (sock, stat_ok, &sb, + stat_ok == -1 ? 0 : sizeof (sb), + errno); +} + /* Handle a MNSH_REQ_OPEN message. Must be async-signal-safe. */ static ssize_t @@ -574,6 +595,11 @@ mnsh_main (int sock) response = mnsh_handle_setns (sock, fd, int1); break; + case MNSH_REQ_LSTAT: + if (size > 0 && buf[size - 1] == '\0') + response = mnsh_handle_lstat (sock, buf); + break; + case MNSH_REQ_OPEN: if (size > 0 && buf[size - 1] == '\0') response = mnsh_handle_open (sock, buf, int1, int2); @@ -673,7 +699,7 @@ linux_mntns_get_helper (void) mnsh_creator_pid = helper_creator; /* Debug printing isn't async-signal-safe. */ - debug_linux_namespaces = 0; + debug_linux_namespaces = false; mnsh_main (sv[1]); } @@ -685,9 +711,8 @@ linux_mntns_get_helper (void) helper->sock = sv[0]; helper->nsid = ns->id; - if (debug_linux_namespaces) - debug_printf ("Started mount namespace helper process %d\n", - helper->pid); + linux_namespaces_debug_printf + ("Started mount namespace helper process %d", helper->pid); } return helper; @@ -765,6 +790,10 @@ mnsh_maybe_mourn_peer (void) mnsh_send_message (helper->sock, MNSH_REQ_OPEN, -1, flags, mode, \ filename, strlen (filename) + 1) +#define mnsh_send_lstat(helper, filename) \ + mnsh_send_message (helper->sock, MNSH_REQ_LSTAT, -1, 0, 0, \ + filename, strlen (filename) + 1) + #define mnsh_send_unlink(helper, filename) \ mnsh_send_message (helper->sock, MNSH_REQ_UNLINK, -1, 0, 0, \ filename, strlen (filename) + 1) @@ -884,7 +913,7 @@ linux_mntns_access_fs (pid_t pid) struct stat sb; struct linux_mnsh *helper; ssize_t size; - int fd; + int fd, fd_user = -1; if (pid == getpid ()) return MNSH_FS_DIRECT; @@ -901,6 +930,8 @@ linux_mntns_access_fs (pid_t pid) { int save_errno = errno; close (fd); + if (fd_user >= 0) + close (fd_user); errno = save_errno; }; @@ -910,6 +941,13 @@ linux_mntns_access_fs (pid_t pid) if (sb.st_ino == ns->id) return MNSH_FS_DIRECT; + struct linux_ns *ns_user = linux_ns_get_namespace (LINUX_NS_USER); + if (ns_user != nullptr) + { + const char *ns_filename = linux_ns_filename (ns_user, pid); + fd_user = gdb_open_cloexec (ns_filename, O_RDONLY, 0).release (); + } + helper = linux_mntns_get_helper (); if (helper == NULL) return MNSH_FS_ERROR; @@ -918,6 +956,19 @@ linux_mntns_access_fs (pid_t pid) { int result, error; + /* Try to enter the user namespace first. The current user might + have elevated privileges within the user namespace, which would + then allow the attempt to enter the mount namespace to succeed. */ + if (fd_user >= 0) + { + size = mnsh_send_setns (helper, fd_user, 0); + if (size < 0) + return MNSH_FS_ERROR; + + if (mnsh_recv_int (helper, &result, &error) != 0) + return MNSH_FS_ERROR; + } + size = mnsh_send_setns (helper, fd, 0); if (size < 0) return MNSH_FS_ERROR; @@ -948,6 +999,42 @@ linux_mntns_access_fs (pid_t pid) /* See nat/linux-namespaces.h. */ int +linux_mntns_lstat (pid_t pid, const char *filename, + struct stat *sb) +{ + enum mnsh_fs_code access = linux_mntns_access_fs (pid); + + if (access == MNSH_FS_ERROR) + return -1; + + if (access == MNSH_FS_DIRECT) + return lstat (filename, sb); + + gdb_assert (access == MNSH_FS_HELPER); + + struct linux_mnsh *helper = linux_mntns_get_helper (); + + ssize_t size = mnsh_send_lstat (helper, filename); + if (size < 0) + return -1; + + int stat_ok, error; + size = mnsh_recv_intstr (helper, &stat_ok, &error, sb, sizeof (*sb)); + + if (size < 0) + { + stat_ok = -1; + errno = error; + } + else + gdb_assert (stat_ok == -1 || size == sizeof (*sb)); + + return stat_ok; +} + +/* See nat/linux-namespaces.h. */ + +int linux_mntns_open_cloexec (pid_t pid, const char *filename, int flags, mode_t mode) { diff --git a/gdb/nat/linux-namespaces.h b/gdb/nat/linux-namespaces.h index a548d83..a9a99f1 100644 --- a/gdb/nat/linux-namespaces.h +++ b/gdb/nat/linux-namespaces.h @@ -24,6 +24,12 @@ extern bool debug_linux_namespaces; +/* Print a "linux-namespaces" debug statement. */ + +#define linux_namespaces_debug_printf(fmt, ...) \ + debug_prefixed_printf_cond (debug_linux_namespaces, "linux-namespaces", \ + fmt, ##__VA_ARGS__) + /* Enumeration of Linux namespace types. */ enum linux_ns_type @@ -58,6 +64,11 @@ enum linux_ns_type extern int linux_ns_same (pid_t pid, enum linux_ns_type type); +/* Like lstat(2), but in the mount namespace of process PID. */ + +extern int linux_mntns_lstat (pid_t pid, const char *filename, + struct stat *sb); + /* Like gdb_open_cloexec, but in the mount namespace of process PID. */ diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c index a72df2a..d4f9af3 100644 --- a/gdb/nat/linux-procfs.c +++ b/gdb/nat/linux-procfs.c @@ -424,7 +424,7 @@ linux_proc_task_list_dir_exists (pid_t pid) /* See linux-procfs.h. */ const char * -linux_proc_pid_to_exec_file (int pid) +linux_proc_pid_to_exec_file (int pid, bool is_local_fs) { static char buf[PATH_MAX]; char name[PATH_MAX]; @@ -437,9 +437,31 @@ linux_proc_pid_to_exec_file (int pid) else buf[len] = '\0'; - /* Use /proc/PID/exe if the actual file can't be read, but /proc/PID/exe - can be. */ - if (access (buf, R_OK) != 0 && access (name, R_OK) == 0) + /* If the inferior and GDB can see the same filesystem, and NAME + cannot be read, maybe the file has been deleted, then we can + potentially use /proc/PID/exe instead. + + GDB always interprets the results of this function within the + current sysroot (which is 'target:' by default). This means + that, if we return /proc/PID/exe, GDB will try to find this file + within the sysroot. + + This doesn't make sense if GDB is using a sysroot like: + '/some/random/directory/', not only is it possible that NAME + could be found within the sysroot, it is unlikely that + /proc/PID/exe will exist within the sysroot. + + Similarly, if the sysroot is 'target:', but the inferior is + running within a separate MNT namespace, then it is more than + likely that the inferior will also be running in a separate PID + namespace, in this case PID is the pid on the host system, + /proc/PID/exe will not be correct within the inferiors MNT/PID + namespace. + + So, we can fallback to use /proc/PID/exe only if IS_LOCAL_FS is + true, this indicates that GDB and the inferior are using the same + MNT namespace, and GDB's sysroot is either empty, or 'target:'. */ + if (is_local_fs && access (buf, R_OK) != 0 && access (name, R_OK) == 0) strcpy (buf, name); return buf; diff --git a/gdb/nat/linux-procfs.h b/gdb/nat/linux-procfs.h index 0c80622..4b02d31 100644 --- a/gdb/nat/linux-procfs.h +++ b/gdb/nat/linux-procfs.h @@ -87,9 +87,19 @@ extern int linux_proc_task_list_dir_exists (pid_t pid); /* Return the full absolute name of the executable file that was run to create the process PID. The returned value persists until this - function is next called. */ + function is next called. -extern const char *linux_proc_pid_to_exec_file (int pid); + LOCAL_FS should be true if the file returned from the function will + be searched for in the same filesystem as GDB itself is running. + In practice, this means LOCAL_FS should be true if PID and GDB are + running in the same MNT namespace and GDB's sysroot is either the + empty string, or is 'target:'. + + When used from gdbserver, where there is no sysroot, the only check + that matters is that PID and gdbserver are running in the same MNT + namespace. */ + +extern const char *linux_proc_pid_to_exec_file (int pid, bool local_fs); /* Display possible problems on this system. Display them only once per GDB execution. */ diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c index 0510b39..f73058b 100644 --- a/gdb/nat/linux-ptrace.c +++ b/gdb/nat/linux-ptrace.c @@ -23,6 +23,7 @@ #include <sys/procfs.h> #endif #include "gdbsupport/eintr.h" +#include "gdbsupport/signals-state-save-restore.h" /* Stores the ptrace options supported by the running kernel. A value of -1 means we did not check for features yet. A value @@ -148,6 +149,9 @@ linux_ptrace_test_ret_to_nx (void) return; case 0: + /* Set signal handlers to their default because it doesn't make sense + to call GDB-specific handlers any more in the child process. */ + restore_original_signals_state (); l = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) NULL, (PTRACE_TYPE_ARG4) NULL); if (l != 0) diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c index 7d826fc..9180cd0 100644 --- a/gdb/nds32-tdep.c +++ b/gdb/nds32-tdep.c @@ -2086,9 +2086,7 @@ nds32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_nds32_tdep (); -void -_initialize_nds32_tdep () +INIT_GDB_FILE (nds32_tdep) { /* Initialize gdbarch. */ gdbarch_register (bfd_arch_nds32, nds32_gdbarch_init); diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c index 5341b05..6bba8d3 100644 --- a/gdb/objc-lang.c +++ b/gdb/objc-lang.c @@ -1327,9 +1327,7 @@ find_objc_msgcall (CORE_ADDR pc, CORE_ADDR *new_pc) return 0; } -void _initialize_objc_language (); -void -_initialize_objc_language () +INIT_GDB_FILE (objc_language) { add_info ("selectors", info_selectors_command, _("All Objective-C selectors, or those matching REGEXP.")); diff --git a/gdb/objfiles.c b/gdb/objfiles.c index 3c5a554..d25d1a0 100644 --- a/gdb/objfiles.c +++ b/gdb/objfiles.c @@ -673,18 +673,18 @@ objfile_rebase (struct objfile *objfile, CORE_ADDR slide) /* See objfiles.h. */ bool -objfile_has_full_symbols (objfile *objfile) +objfile::has_full_symbols () { - return objfile->compunit_symtabs != nullptr; + return this->compunit_symtabs != nullptr; } /* See objfiles.h. */ bool -objfile_has_symbols (objfile *objfile) +objfile::has_symbols () { - for (::objfile *o : objfile->separate_debug_objfiles ()) - if (o->has_partial_symbols () || objfile_has_full_symbols (o)) + for (::objfile *o : this->separate_debug_objfiles ()) + if (o->has_partial_symbols () || o->has_full_symbols ()) return true; return false; @@ -708,7 +708,7 @@ bool have_full_symbols (program_space *pspace) { for (objfile *ofp : pspace->objfiles ()) - if (objfile_has_full_symbols (ofp)) + if (ofp->has_full_symbols ()) return true; return false; diff --git a/gdb/objfiles.h b/gdb/objfiles.h index bb2f05d..4a34758 100644 --- a/gdb/objfiles.h +++ b/gdb/objfiles.h @@ -515,10 +515,16 @@ public: return per_bfd->gdbarch; } - /* Return true if OBJFILE has partial symbols. */ - + /* Return true if this objfile has partial symbols. */ bool has_partial_symbols (); + /* Return true if this objfile has full symbols. */ + bool has_full_symbols (); + + /* Return true if this objfile has full or partial symbols, either directly + or through a separate debug file. */ + bool has_symbols (); + /* Look for a separate debug symbol file for this objfile, make use of build-id, debug-link, and debuginfod as necessary. If a suitable separate debug symbol file is found then it is loaded using a call to @@ -938,15 +944,6 @@ extern void free_objfile_separate_debug (struct objfile *); extern void objfile_relocate (struct objfile *, const section_offsets &); extern void objfile_rebase (struct objfile *, CORE_ADDR); -/* Return true if OBJFILE has full symbols. */ - -extern bool objfile_has_full_symbols (objfile *objfile); - -/* Return true if OBJFILE has full or partial symbols, either directly - or through a separate debug file. */ - -extern bool objfile_has_symbols (objfile *objfile); - /* Return true if any objfile of PSPACE has partial symbols. */ extern bool have_partial_symbols (program_space *pspace); @@ -992,10 +989,10 @@ extern struct obj_section *find_pc_section (CORE_ADDR pc); /* Return true if PC is in a section called NAME. */ extern bool pc_in_section (CORE_ADDR, const char *); -/* Return non-zero if PC is in a SVR4-style procedure linkage table +/* Return true if PC is in a SVR4-style procedure linkage table section. */ -static inline int +static inline bool in_plt_section (CORE_ADDR pc) { return (pc_in_section (pc, ".plt") diff --git a/gdb/observable.c b/gdb/observable.c index 734a5f6..1233a19 100644 --- a/gdb/observable.c +++ b/gdb/observable.c @@ -87,9 +87,7 @@ show_observer_debug (struct ui_file *file, int from_tty, gdb_printf (file, _("Observer debugging is %s.\n"), value); } -void _initialize_observer (); -void -_initialize_observer () +INIT_GDB_FILE (observer) { add_setshow_boolean_cmd ("observer", class_maintenance, &gdb::observers::observer_debug, _("\ diff --git a/gdb/or1k-linux-nat.c b/gdb/or1k-linux-nat.c index 3a80dc9..cc682ea 100644 --- a/gdb/or1k-linux-nat.c +++ b/gdb/or1k-linux-nat.c @@ -199,9 +199,7 @@ or1k_linux_nat_target::store_registers (struct regcache *regcache, int regnum) /* Initialize OpenRISC Linux native support. */ -void _initialize_or1k_linux_nat (); -void -_initialize_or1k_linux_nat () +INIT_GDB_FILE (or1k_linux_nat) { /* Register the target. */ linux_target = &the_or1k_linux_nat_target; diff --git a/gdb/or1k-linux-tdep.c b/gdb/or1k-linux-tdep.c index 4784e0b..afefefe 100644 --- a/gdb/or1k-linux-tdep.c +++ b/gdb/or1k-linux-tdep.c @@ -20,6 +20,7 @@ #include "osabi.h" #include "glibc-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "solib-svr4.h" #include "regset.h" #include "tramp-frame.h" @@ -144,8 +145,7 @@ or1k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { linux_init_abi (info, gdbarch, 0); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); /* GNU/Linux uses SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); @@ -167,9 +167,7 @@ or1k_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Initialize OpenRISC Linux target support. */ -void _initialize_or1k_linux_tdep (); -void -_initialize_or1k_linux_tdep () +INIT_GDB_FILE (or1k_linux_tdep) { gdbarch_register_osabi (bfd_arch_or1k, 0, GDB_OSABI_LINUX, or1k_linux_init_abi); diff --git a/gdb/or1k-tdep.c b/gdb/or1k-tdep.c index 8e76419..2774840 100644 --- a/gdb/or1k-tdep.c +++ b/gdb/or1k-tdep.c @@ -1286,9 +1286,7 @@ or1k_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) } -void _initialize_or1k_tdep (); -void -_initialize_or1k_tdep () +INIT_GDB_FILE (or1k_tdep) { /* Register this architecture. */ gdbarch_register (bfd_arch_or1k, or1k_gdbarch_init, or1k_dump_tdep); diff --git a/gdb/osabi.c b/gdb/osabi.c index ae14f74..1459368 100644 --- a/gdb/osabi.c +++ b/gdb/osabi.c @@ -609,9 +609,7 @@ show_osabi (struct ui_file *file, int from_tty, struct cmd_list_element *c, gdbarch_osabi_name (GDB_OSABI_DEFAULT)); } -void _initialize_gdb_osabi (); -void -_initialize_gdb_osabi () +INIT_GDB_FILE (gdb_osabi) { /* Register a generic sniffer for ELF flavoured files. */ gdbarch_register_osabi_sniffer (bfd_arch_unknown, diff --git a/gdb/osdata.c b/gdb/osdata.c index 5cc584d..788dd2a 100644 --- a/gdb/osdata.c +++ b/gdb/osdata.c @@ -287,9 +287,7 @@ info_osdata_command (const char *arg, int from_tty) info_osdata (arg); } -void _initialize_osdata (); -void -_initialize_osdata () +INIT_GDB_FILE (osdata) { add_info ("os", info_osdata_command, _("Show OS data ARG.")); diff --git a/gdb/p-valprint.c b/gdb/p-valprint.c index 5dcdbcd..8228ca0 100644 --- a/gdb/p-valprint.c +++ b/gdb/p-valprint.c @@ -860,9 +860,7 @@ pascal_object_print_static_field (struct value *val, common_val_print (val, stream, recurse, &opts, current_language); } -void _initialize_pascal_valprint (); -void -_initialize_pascal_valprint () +INIT_GDB_FILE (pascal_valprint) { add_setshow_boolean_cmd ("pascal_static-members", class_support, &user_print_options.pascal_static_field_print, _("\ diff --git a/gdb/pager.h b/gdb/pager.h index 052337d..b5425d4 100644 --- a/gdb/pager.h +++ b/gdb/pager.h @@ -68,6 +68,16 @@ private: /* Flush the wrap buffer to STREAM, if necessary. */ void flush_wrap_buffer (); + /* Set the style of m_stream to STYLE. */ + void set_stream_style (const ui_file_style &style) + { + if (m_stream->can_emit_style_escape () && m_stream_style != style) + { + m_stream->puts (style.to_ansi ().c_str ()); + m_stream_style = style; + } + } + /* Contains characters which are waiting to be output (they have already been counted in chars_printed). */ std::string m_wrap_buffer; @@ -82,6 +92,12 @@ private: /* The style applied at the time that wrap_here was called. */ ui_file_style m_wrap_style; + /* The style currently applied to m_stream. While m_applied_style is the + style that is applied to new content added to m_wrap_buffer, the + m_stream_style reflects changes that have been flushed to the managed + stream. */ + ui_file_style m_stream_style; + /* This is temporarily set when paging. This will cause some methods to change their behavior to ignore the wrap buffer. */ bool m_paging = false; diff --git a/gdb/parse.c b/gdb/parse.c index 3108017..6ad4e71 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, @@ -617,9 +617,7 @@ parser_fprintf (FILE *x, const char *y, ...) va_end (args); } -void _initialize_parse (); -void -_initialize_parse () +INIT_GDB_FILE (parse) { add_setshow_zuinteger_cmd ("expression", class_maintenance, &expressiondebug, 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/ppc-fbsd-nat.c b/gdb/ppc-fbsd-nat.c index e8853cf..e59791d 100644 --- a/gdb/ppc-fbsd-nat.c +++ b/gdb/ppc-fbsd-nat.c @@ -200,9 +200,7 @@ ppcfbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) return 1; } -void _initialize_ppcfbsd_nat (); -void -_initialize_ppcfbsd_nat () +INIT_GDB_FILE (ppcfbsd_nat) { add_inf_child_target (&the_ppc_fbsd_nat_target); diff --git a/gdb/ppc-fbsd-tdep.c b/gdb/ppc-fbsd-tdep.c index 3bab031..a666509 100644 --- a/gdb/ppc-fbsd-tdep.c +++ b/gdb/ppc-fbsd-tdep.c @@ -332,8 +332,7 @@ ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_return_value (gdbarch, ppcfbsd_return_value); set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); frame_unwind_append_unwinder (gdbarch, &ppcfbsd_sigtramp_frame_unwind); set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpc"); @@ -347,8 +346,7 @@ ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) ppc64_elf_make_msymbol_special); set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); set_gdbarch_gcore_bfd_target (gdbarch, "elf64-powerpc"); } @@ -361,9 +359,7 @@ ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) ppcfbsd_get_thread_local_address); } -void _initialize_ppcfbsd_tdep (); -void -_initialize_ppcfbsd_tdep () +INIT_GDB_FILE (ppcfbsd_tdep) { gdbarch_register_osabi (bfd_arch_powerpc, bfd_mach_ppc, GDB_OSABI_FREEBSD, ppcfbsd_init_abi); diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c index 0397ccb..568202a 100644 --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -3232,9 +3232,7 @@ ppc_linux_nat_target::get_arch_lwp_info (struct lwp_info *lp) return lwp_arch_private_info (lp); } -void _initialize_ppc_linux_nat (); -void -_initialize_ppc_linux_nat () +INIT_GDB_FILE (ppc_linux_nat) { linux_target = &the_ppc_linux_nat_target; diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c index 3050a9e..5067b89 100644 --- a/gdb/ppc-linux-tdep.c +++ b/gdb/ppc-linux-tdep.c @@ -32,7 +32,6 @@ #include "regset.h" #include "solib-svr4.h" #include "solib.h" -#include "solist.h" #include "ppc-tdep.h" #include "ppc64-tdep.h" #include "ppc-linux-tdep.h" @@ -49,6 +48,8 @@ #include "arch-utils.h" #include "xml-syscall.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" +#include "svr4-tls-tdep.h" #include "linux-record.h" #include "record-full.h" #include "infrun.h" @@ -86,8 +87,6 @@ #include "features/rs6000/powerpc-e500l.c" #include "dwarf2/frame.h" -/* Shared library operations for PowerPC-Linux. */ -static solib_ops powerpc_so_ops; /* The syscall's XML filename for PPC and PPC64. */ #define XML_SYSCALL_FILENAME_PPC "syscalls/ppc-linux.xml" @@ -306,26 +305,43 @@ static const struct ppc_insn_pattern powerpc32_plt_stub_so_2[] = /* The max number of insns we check using ppc_insns_match_pattern. */ #define POWERPC32_PLT_CHECK_LEN (ARRAY_SIZE (powerpc32_plt_stub) - 1) -/* Check if PC is in PLT stub. For non-secure PLT, stub is in .plt - section. For secure PLT, stub is in .text and we need to check - instruction patterns. */ +/* solib_ops for ILP32 PowerPC/Linux systems. */ -static int -powerpc_linux_in_dynsym_resolve_code (CORE_ADDR pc) +struct ppc_linux_ilp32_svr4_solib_ops : public linux_ilp32_svr4_solib_ops +{ + /* Check if PC is in PLT stub. For non-secure PLT, stub is in .plt + section. For secure PLT, stub is in .text and we need to check + instruction patterns. */ + + bool in_dynsym_resolve_code (CORE_ADDR pc) const override; +}; + +/* Return a new solib_ops for ILP32 PowerPC/Linux systems. */ + +static solib_ops_up +make_ppc_linux_ilp32_svr4_solib_ops () +{ + return std::make_unique<ppc_linux_ilp32_svr4_solib_ops> (); +} + +/* See ppc_linux_ilp32_svr4_solib_ops. */ + +bool +ppc_linux_ilp32_svr4_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const { /* Check whether PC is in the dynamic linker. This also checks whether it is in the .plt section, used by non-PIC executables. */ - if (svr4_in_dynsym_resolve_code (pc)) - return 1; + if (linux_ilp32_svr4_solib_ops::in_dynsym_resolve_code (pc)) + return true; /* Check if we are in the resolver. */ bound_minimal_symbol sym = lookup_minimal_symbol_by_pc (pc); - if (sym.minsym != NULL - && (strcmp (sym.minsym->linkage_name (), "__glink") == 0 - || strcmp (sym.minsym->linkage_name (), "__glink_PLTresolve") == 0)) - return 1; - return 0; + if (sym.minsym == nullptr) + return false; + + return (strcmp (sym.minsym->linkage_name (), "__glink") == 0 + || strcmp (sym.minsym->linkage_name (), "__glink_PLTresolve") == 0); } /* Follow PLT stub to actual routine. @@ -2071,6 +2087,63 @@ ppc64_linux_gcc_target_options (struct gdbarch *gdbarch) return ""; } +/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID. + Throw a suitable TLS error if something goes wrong. */ + +static CORE_ADDR +ppc64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid, + enum svr4_tls_libc libc) +{ + /* On ppc64, the thread pointer is found in r13. Fetch this + register. */ + regcache *regcache + = get_thread_arch_regcache (current_inferior (), ptid, gdbarch); + int thread_pointer_regnum = PPC_R0_REGNUM + 13; + target_fetch_registers (regcache, thread_pointer_regnum); + ULONGEST thr_ptr; + if (regcache->cooked_read (thread_pointer_regnum, &thr_ptr) != REG_VALID) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer")); + + /* The thread pointer (r13) is an address that is 0x7000 ahead of + the *end* of the TCB (thread control block). The field + holding the DTV address is at the very end of the TCB. + Therefore, the DTV pointer address can be found by + subtracting (0x7000+8) from the thread pointer. Compute the + address of the DTV pointer, fetch it, and convert it to an + address. */ + CORE_ADDR dtv_ptr_addr = thr_ptr - 0x7000 - 8; + gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address")); + + const struct builtin_type *builtin = builtin_type (gdbarch); + CORE_ADDR dtv_addr = gdbarch_pointer_to_address + (gdbarch, builtin->builtin_data_ptr, buf.data ()); + return dtv_addr; +} + +/* For internal TLS lookup, return the DTP offset, which is the offset + to subtract from a DTV entry, in order to obtain the address of the + TLS block. */ + +static ULONGEST +ppc_linux_get_tls_dtp_offset (struct gdbarch *gdbarch, ptid_t ptid, + svr4_tls_libc libc) +{ + if (libc == svr4_tls_libc_musl) + { + /* This value is DTP_OFFSET, which represents the value to + subtract from the DTV entry. For PPC, it can be found in + MUSL's arch/powerpc64/pthread_arch.h and + arch/powerpc32/pthread_arch.h. (Both values are the same.) + It represents the value to subtract from the DTV entry, once + it has been fetched from the DTV array. */ + return 0x8000; + } + else + return 0; +} + static displaced_step_prepare_status ppc_linux_displaced_step_prepare (gdbarch *arch, thread_info *thread, CORE_ADDR &displaced_pc) @@ -2208,8 +2281,6 @@ ppc_linux_init_abi (struct gdbarch_info info, /* Shared library handling. */ set_gdbarch_skip_trampoline_code (gdbarch, ppc_skip_trampoline_code); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); /* Setting the correct XML syscall filename. */ set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_PPC); @@ -2226,14 +2297,7 @@ ppc_linux_init_abi (struct gdbarch_info info, else set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpc"); - if (powerpc_so_ops.in_dynsym_resolve_code == NULL) - { - powerpc_so_ops = svr4_so_ops; - /* Override dynamic resolve function. */ - powerpc_so_ops.in_dynsym_resolve_code = - powerpc_linux_in_dynsym_resolve_code; - } - set_gdbarch_so_ops (gdbarch, &powerpc_so_ops); + set_solib_svr4_ops (gdbarch, make_ppc_linux_ilp32_svr4_solib_ops); set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); } @@ -2260,8 +2324,7 @@ ppc_linux_init_abi (struct gdbarch_info info, /* Shared library handling. */ set_gdbarch_skip_trampoline_code (gdbarch, ppc64_skip_trampoline_code); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops); /* Setting the correct XML syscall filename. */ set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_PPC64); @@ -2284,6 +2347,11 @@ ppc_linux_init_abi (struct gdbarch_info info, set_gdbarch_gnu_triplet_regexp (gdbarch, ppc64_gnu_triplet_regexp); /* Set GCC target options. */ set_gdbarch_gcc_target_options (gdbarch, ppc64_linux_gcc_target_options); + /* Internal thread local address support. */ + set_gdbarch_get_thread_local_address (gdbarch, + svr4_tls_get_thread_local_address); + svr4_tls_register_tls_methods (info, gdbarch, ppc64_linux_get_tls_dtv_addr, + ppc_linux_get_tls_dtp_offset); } set_gdbarch_core_read_description (gdbarch, ppc_linux_core_read_description); @@ -2330,9 +2398,7 @@ ppc_linux_init_abi (struct gdbarch_info info, } -void _initialize_ppc_linux_tdep (); -void -_initialize_ppc_linux_tdep () +INIT_GDB_FILE (ppc_linux_tdep) { /* Register for all sub-families of the POWER/PowerPC: 32-bit and 64-bit PowerPC, and the older rs6k. */ diff --git a/gdb/ppc-netbsd-nat.c b/gdb/ppc-netbsd-nat.c index a8e65bb..dfb855a 100644 --- a/gdb/ppc-netbsd-nat.c +++ b/gdb/ppc-netbsd-nat.c @@ -183,9 +183,7 @@ ppcnbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) return 1; } -void _initialize_ppcnbsd_nat (); -void -_initialize_ppcnbsd_nat () +INIT_GDB_FILE (ppcnbsd_nat) { /* Support debugging kernel virtual memory images. */ bsd_kvm_add_target (ppcnbsd_supply_pcb); diff --git a/gdb/ppc-netbsd-tdep.c b/gdb/ppc-netbsd-tdep.c index 911c012..9913592 100644 --- a/gdb/ppc-netbsd-tdep.c +++ b/gdb/ppc-netbsd-tdep.c @@ -179,8 +179,7 @@ ppcnbsd_init_abi (struct gdbarch_info info, set_gdbarch_return_value (gdbarch, ppcnbsd_return_value); /* NetBSD uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); set_gdbarch_iterate_over_regset_sections (gdbarch, ppcnbsd_iterate_over_regset_sections); @@ -189,9 +188,7 @@ ppcnbsd_init_abi (struct gdbarch_info info, tramp_frame_prepend_unwinder (gdbarch, &ppcnbsd2_sigtramp); } -void _initialize_ppcnbsd_tdep (); -void -_initialize_ppcnbsd_tdep () +INIT_GDB_FILE (ppcnbsd_tdep) { gdbarch_register_osabi (bfd_arch_powerpc, 0, GDB_OSABI_NETBSD, ppcnbsd_init_abi); diff --git a/gdb/ppc-obsd-nat.c b/gdb/ppc-obsd-nat.c index 1431aa6..48df9d7 100644 --- a/gdb/ppc-obsd-nat.c +++ b/gdb/ppc-obsd-nat.c @@ -186,9 +186,7 @@ ppcobsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) return 1; } -void _initialize_ppcobsd_nat (); -void -_initialize_ppcobsd_nat () +INIT_GDB_FILE (ppcobsd_nat) { add_inf_child_target (&the_ppc_obsd_nat_target); diff --git a/gdb/ppc-obsd-tdep.c b/gdb/ppc-obsd-tdep.c index 5e271c1..18f4d79 100644 --- a/gdb/ppc-obsd-tdep.c +++ b/gdb/ppc-obsd-tdep.c @@ -254,8 +254,7 @@ ppcobsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_return_value (gdbarch, ppc_sysv_abi_broken_return_value); /* OpenBSD uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); set_gdbarch_iterate_over_regset_sections (gdbarch, ppcobsd_iterate_over_regset_sections); @@ -263,9 +262,7 @@ ppcobsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) frame_unwind_append_unwinder (gdbarch, &ppcobsd_sigtramp_frame_unwind); } -void _initialize_ppcobsd_tdep (); -void -_initialize_ppcobsd_tdep () +INIT_GDB_FILE (ppcobsd_tdep) { gdbarch_register_osabi (bfd_arch_rs6000, 0, GDB_OSABI_OPENBSD, ppcobsd_init_abi); diff --git a/gdb/ppc-sysv-tdep.c b/gdb/ppc-sysv-tdep.c index bcf4e2a..cae5aa6 100644 --- a/gdb/ppc-sysv-tdep.c +++ b/gdb/ppc-sysv-tdep.c @@ -2112,6 +2112,7 @@ ppc64_sysv_abi_return_value (struct gdbarch *gdbarch, struct value *function, /* In the ELFv2 ABI, aggregate types of up to 16 bytes are returned in registers r3:r4. */ if (tdep->elf_abi == POWERPC_ELF_V2 + && !TYPE_HAS_DYNAMIC_LENGTH (valtype) && valtype->length () <= 16 && (valtype->code () == TYPE_CODE_STRUCT || valtype->code () == TYPE_CODE_UNION diff --git a/gdb/printcmd.c b/gdb/printcmd.c index 2be5eaa..19fbc20 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 (); } @@ -2392,7 +2394,7 @@ printf_c_string (struct ui_file *stream, const char *format, } else { - CORE_ADDR tem = value_as_address (value);; + CORE_ADDR tem = value_as_address (value); if (tem == 0) { @@ -3181,9 +3183,7 @@ memory_tag_check_command (const char *args, int from_tty) } } -void _initialize_printcmd (); -void -_initialize_printcmd () +INIT_GDB_FILE (printcmd) { struct cmd_list_element *c; diff --git a/gdb/probe.c b/gdb/probe.c index e6788ff..6679f39 100644 --- a/gdb/probe.c +++ b/gdb/probe.c @@ -973,9 +973,7 @@ static const struct internalvar_funcs probe_funcs = std::vector<const static_probe_ops *> all_static_probe_ops; -void _initialize_probe (); -void -_initialize_probe () +INIT_GDB_FILE (probe) { all_static_probe_ops.push_back (&any_static_probe_ops); diff --git a/gdb/proc-api.c b/gdb/proc-api.c index 7a45ba9..f3d4e14 100644 --- a/gdb/proc-api.c +++ b/gdb/proc-api.c @@ -413,9 +413,7 @@ proc_prettyfprint_status (long flags, int why, int what, int thread) } } -void _initialize_proc_api (); -void -_initialize_proc_api () +INIT_GDB_FILE (proc_api) { add_setshow_boolean_cmd ("procfs-trace", no_class, &procfs_trace, _("\ Set tracing for /proc api calls."), _("\ diff --git a/gdb/proc-events.c b/gdb/proc-events.c index 23af639..c6ed1f4 100644 --- a/gdb/proc-events.c +++ b/gdb/proc-events.c @@ -759,9 +759,7 @@ proc_prettyprint_actionset (struct sigaction *actions, int verbose) { } -void _initialize_proc_events (); -void -_initialize_proc_events () +INIT_GDB_FILE (proc_events) { init_syscall_table (); } diff --git a/gdb/proc-service.c b/gdb/proc-service.c index af92335..4f2be0f 100644 --- a/gdb/proc-service.c +++ b/gdb/proc-service.c @@ -206,9 +206,7 @@ ps_getpid (struct ps_prochandle *ph) return ph->thread->ptid.pid (); } -void _initialize_proc_service (); -void -_initialize_proc_service () +INIT_GDB_FILE (proc_service) { /* This function solely exists to make sure this module is linked into the final binary. */ diff --git a/gdb/procfs.c b/gdb/procfs.c index 76a6168..a10574a 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -3447,9 +3447,7 @@ proc_untrace_sysexit_cmd (const char *args, int from_tty) proc_trace_syscalls (args, from_tty, PR_SYSEXIT, FLAG_RESET); } -void _initialize_procfs (); -void -_initialize_procfs () +INIT_GDB_FILE (procfs) { add_com ("proc-trace-entry", no_class, proc_trace_sysentry_cmd, _("Give a trace of entries into the syscall.")); @@ -3550,21 +3548,17 @@ procfs_corefile_thread_callback (procinfo *pi, procinfo *thread, void *data) return 0; } -static int -find_signalled_thread (struct thread_info *info, void *data) +static bool +find_signalled_thread (struct thread_info *info) { - if (info->stop_signal () != GDB_SIGNAL_0 - && info->ptid.pid () == inferior_ptid.pid ()) - return 1; - - return 0; + return (info->stop_signal () != GDB_SIGNAL_0 + && info->ptid.pid () == inferior_ptid.pid ()); } static enum gdb_signal find_stop_signal (void) { - struct thread_info *info = - iterate_over_threads (find_signalled_thread, NULL); + struct thread_info *info = iterate_over_threads (find_signalled_thread); if (info) return info->stop_signal (); diff --git a/gdb/producer.c b/gdb/producer.c index d8cbded..5d754fa 100644 --- a/gdb/producer.c +++ b/gdb/producer.c @@ -335,9 +335,7 @@ Version 18.0 Beta"; } #endif -void _initialize_producer (); -void -_initialize_producer () +INIT_GDB_FILE (producer) { #if defined GDB_SELF_TEST selftests::register_test diff --git a/gdb/progspace.c b/gdb/progspace.c index 569dfc8..1c27165 100644 --- a/gdb/progspace.c +++ b/gdb/progspace.c @@ -21,7 +21,6 @@ #include "objfiles.h" #include "gdbcore.h" #include "solib.h" -#include "solist.h" #include "gdbthread.h" #include "inferior.h" #include <algorithm> @@ -202,12 +201,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/progspace.h b/gdb/progspace.h index 1ae826d..a761e62 100644 --- a/gdb/progspace.h +++ b/gdb/progspace.h @@ -21,12 +21,13 @@ #ifndef GDB_PROGSPACE_H #define GDB_PROGSPACE_H +#include "solib.h" #include "target.h" #include "gdb_bfd.h" #include "registry.h" -#include "solist.h" #include "gdbsupport/safe-iterator.h" #include "gdbsupport/intrusive_list.h" +#include "gdbsupport/owning_intrusive_list.h" #include "gdbsupport/refcounted-object.h" #include "gdbsupport/gdb_ref_ptr.h" #include <vector> @@ -231,9 +232,30 @@ struct program_space is outside all objfiles in this progspace. */ struct objfile *objfile_for_address (CORE_ADDR address); - /* Return the list of all the solibs in this program space. */ + /* Set this program space's solib provider. + + The solib provider must be unset prior to calling this method. */ + void set_solib_ops (solib_ops_up ops) + { + gdb_assert (m_solib_ops == nullptr); + m_solib_ops = std::move (ops); + }; + + /* Unset and free this program space's solib provider. */ + void unset_solib_ops () + { m_solib_ops = nullptr; } + + /* Unset and return this program space's solib provider. */ + solib_ops_up release_solib_ops () + { return std::move (m_solib_ops); } + + /* Get this program space's solib provider. */ + const struct solib_ops *solib_ops () const + { return m_solib_ops.get (); } + + /* Return the list of all the solibs in this program space. */ owning_intrusive_list<solib> &solibs () - { return so_list; } + { return m_solib_list; } /* Similar to `bfd_get_filename (exec_bfd ())` but in original form given by user, without symbolic links and pathname resolved. It is not nullptr @@ -337,10 +359,6 @@ struct program_space (e.g. the argument to the "symbol-file" or "file" command). */ struct objfile *symfile_object_file = NULL; - /* List of shared objects mapped into this space. Managed by - solib.c. */ - owning_intrusive_list<solib> so_list; - /* Number of calls to solib_add. */ unsigned int solib_add_generation = 0; @@ -359,6 +377,13 @@ private: /* All known objfiles are kept in a linked list. */ owning_intrusive_list<objfile> m_objfiles_list; + /* solib_ops implementation used to provide solibs in this program space. */ + solib_ops_up m_solib_ops; + + /* List of shared objects mapped into this space. Managed by + solib.c. */ + owning_intrusive_list<solib> m_solib_list; + /* The set of target sections matching the sections mapped into this program space. Managed by both exec_ops and solib.c. */ std::vector<target_section> m_target_sections; diff --git a/gdb/psymtab.c b/gdb/psymtab.c index 986ef44..516ba72 100644 --- a/gdb/psymtab.c +++ b/gdb/psymtab.c @@ -1552,9 +1552,7 @@ maintenance_check_psymtabs (const char *ignore, int from_tty) } } -void _initialize_psymtab (); -void -_initialize_psymtab () +INIT_GDB_FILE (psymtab) { add_cmd ("psymbols", class_maintenance, maintenance_print_psymbols, _("\ Print dump of current partial symbol definitions.\n\ diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py index 4ea5d06..cedd897 100644 --- a/gdb/python/lib/gdb/__init__.py +++ b/gdb/python/lib/gdb/__init__.py @@ -19,17 +19,22 @@ import sys import threading import traceback from contextlib import contextmanager +from importlib import reload -# Python 3 moved "reload" -if sys.version_info >= (3, 4): - from importlib import reload -else: - from imp import reload - -import _gdb - +# The star import imports _gdb names. When the names are used locally, they +# trigger F405 warnings unless added to the explicit import list. # Note that two indicators are needed here to silence flake8. from _gdb import * # noqa: F401,F403 +from _gdb import ( + STDERR, + STDOUT, + Command, + execute, + flush, + parameter, + selected_inferior, + write, +) # isort: split @@ -60,14 +65,14 @@ class _GdbFile(object): self.write(line) def flush(self): - _gdb.flush(stream=self.stream) + flush(stream=self.stream) def write(self, s): - _gdb.write(s, stream=self.stream) + write(s, stream=self.stream) -sys.stdout = _GdbFile(_gdb.STDOUT) -sys.stderr = _GdbFile(_gdb.STDERR) +sys.stdout = _GdbFile(STDOUT) +sys.stderr = _GdbFile(STDERR) # Default prompt hook does nothing. prompt_hook = None @@ -189,7 +194,7 @@ def GdbSetPythonDirectory(dir): def current_progspace(): "Return the current Progspace." - return _gdb.selected_inferior().progspace + return selected_inferior().progspace def objfiles(): @@ -226,14 +231,14 @@ def set_parameter(name, value): value = "on" else: value = "off" - _gdb.execute("set " + name + " " + str(value), to_string=True) + execute("set " + name + " " + str(value), to_string=True) @contextmanager def with_parameter(name, value): """Temporarily set the GDB parameter NAME to VALUE. Note that this is a context manager.""" - old_value = _gdb.parameter(name) + old_value = parameter(name) set_parameter(name, value) try: # Nothing that useful to return. @@ -392,3 +397,121 @@ def _handle_missing_objfile(pspace, buildid, filename): return _handle_missing_files( pspace, "objfile", lambda h: h(pspace, buildid, filename) ) + + +class ParameterPrefix: + # A wrapper around gdb.Command for creating set/show prefixes. + # + # When creating a gdb.Parameter sub-classes, it is sometimes necessary + # to first create a gdb.Command object in order to create the needed + # command prefix. However, for parameters, we actually need two + # prefixes, a 'set' prefix, and a 'show' prefix. With this helper + # class, a single instance of this class will create both prefixes at + # once. + # + # It is important that this class-level documentation not be a __doc__ + # string. Users are expected to sub-class this ParameterPrefix class + # and add their own documentation. If they don't, then GDB will + # generate a suitable doc string. But, if this (parent) class has a + # __doc__ string of its own, then sub-classes will inherit that __doc__ + # string, and GDB will not understand that it needs to generate one. + + class _PrefixCommand(Command): + """A gdb.Command used to implement both the set and show prefixes. + + This documentation string is not used as the prefix command + documentation as it is overridden in the __init__ method below.""" + + # This private method is connected to the 'invoke' attribute within + # this _PrefixCommand object if the containing ParameterPrefix + # object has an invoke_set or invoke_show method. + # + # This method records within self.__delegate which _PrefixCommand + # object is currently active, and then calls the correct invoke + # method on the delegat object (the ParameterPrefix sub-class + # object). + # + # Recording the currently active _PrefixCommand object is important; + # if from the invoke method the user calls dont_repeat, then this is + # forwarded to the currently active _PrefixCommand object. + def __invoke(self, args, from_tty): + + # A helper class for use as part of a Python 'with' block. + # Records which gdb.Command object is currently running its + # invoke method. + class MarkActiveCallback: + # The CMD is a _PrefixCommand object, and the DELEGATE is + # the ParameterPrefix class, or sub-class object. At this + # point we simple record both of these within the + # MarkActiveCallback object. + def __init__(self, cmd, delegate): + self.__cmd = cmd + self.__delegate = delegate + + # Record the currently active _PrefixCommand object within + # the outer ParameterPrefix sub-class object. + def __enter__(self): + self.__delegate.active_prefix = self.__cmd + + # Once the invoke method has completed, then clear the + # _PrefixCommand object that was stored into the outer + # ParameterPrefix sub-class object. + def __exit__(self, exception_type, exception_value, traceback): + self.__delegate.active_prefix = None + + # The self.__cb attribute is set when the _PrefixCommand object + # is created, and is either invoke_set or invoke_show within the + # ParameterPrefix sub-class object. + assert callable(self.__cb) + + # Record the currently active _PrefixCommand object within the + # ParameterPrefix sub-class object, then call the relevant + # invoke method within the ParameterPrefix sub-class object. + with MarkActiveCallback(self, self.__delegate): + self.__cb(args, from_tty) + + @staticmethod + def __find_callback(delegate, mode): + """The MODE is either 'set' or 'show'. Look for an invoke_MODE method + on DELEGATE, if a suitable method is found, then return it, otherwise, + return None. + """ + cb = getattr(delegate, "invoke_" + mode, None) + if callable(cb): + return cb + return None + + def __init__(self, mode, name, cmd_class, delegate, doc=None): + """Setup this gdb.Command. Mode is a string, either 'set' or 'show'. + NAME is the name for this prefix command, that is, the + words that appear after both 'set' and 'show' in the + command name. CMD_CLASS is the usual enum. And DELEGATE + is the gdb.ParameterPrefix object this prefix is part of. + """ + assert mode == "set" or mode == "show" + if doc is None: + self.__doc__ = delegate.__doc__ + else: + self.__doc__ = doc + self.__cb = self.__find_callback(delegate, mode) + self.__delegate = delegate + if self.__cb is not None: + self.invoke = self.__invoke + super().__init__(mode + " " + name, cmd_class, prefix=True) + + def __init__(self, name, cmd_class, doc=None): + """Create a _PrefixCommand for both the set and show prefix commands. + NAME is the command name without either the leading 'set ' or + 'show ' strings, and CMD_CLASS is the usual enum value. + """ + self.active_prefix = None + self._set_prefix_cmd = self._PrefixCommand("set", name, cmd_class, self, doc) + self._show_prefix_cmd = self._PrefixCommand("show", name, cmd_class, self, doc) + + # When called from within an invoke method the self.active_prefix + # attribute should be set to a gdb.Command sub-class (a _PrefixCommand + # object, see above). Forward the dont_repeat call to this object to + # register the actual command as none repeating. + def dont_repeat(self): + if self.active_prefix is not None: + self.active_prefix.dont_repeat() diff --git a/gdb/python/lib/gdb/dap/__init__.py b/gdb/python/lib/gdb/dap/__init__.py index f908c91..1c3cf8e 100644 --- a/gdb/python/lib/gdb/dap/__init__.py +++ b/gdb/python/lib/gdb/dap/__init__.py @@ -96,5 +96,4 @@ def pre_command_loop(): # These are handy for bug reports. startup.exec_and_log("show version") startup.exec_and_log("show configuration") - global server startup.start_dap(server.main_loop) diff --git a/gdb/python/lib/gdb/dap/breakpoint.py b/gdb/python/lib/gdb/dap/breakpoint.py index bfb7bd3..4d4ca18 100644 --- a/gdb/python/lib/gdb/dap/breakpoint.py +++ b/gdb/python/lib/gdb/dap/breakpoint.py @@ -51,7 +51,6 @@ def suppress_new_breakpoint_event(): @in_gdb_thread def _bp_modified(event): - global _suppress_bp if not _suppress_bp: send_event( "breakpoint", @@ -64,7 +63,6 @@ def _bp_modified(event): @in_gdb_thread def _bp_created(event): - global _suppress_bp if not _suppress_bp: send_event( "breakpoint", @@ -77,7 +75,6 @@ def _bp_created(event): @in_gdb_thread def _bp_deleted(event): - global _suppress_bp if not _suppress_bp: send_event( "breakpoint", @@ -151,7 +148,6 @@ def _remove_entries(table, *names): # the breakpoint. @in_gdb_thread def _set_breakpoints_callback(kind, specs, creator): - global breakpoint_map # Try to reuse existing breakpoints if possible. if kind in breakpoint_map: saved_map = breakpoint_map[kind] @@ -330,7 +326,7 @@ def _rewrite_fn_breakpoint( } -@request("setFunctionBreakpoints") +@request("setFunctionBreakpoints", expect_stopped=False) @capability("supportsFunctionBreakpoints") def set_fn_breakpoint(*, breakpoints: Sequence, **args): specs = [_rewrite_fn_breakpoint(**bp) for bp in breakpoints] @@ -363,7 +359,7 @@ def _rewrite_insn_breakpoint( } -@request("setInstructionBreakpoints") +@request("setInstructionBreakpoints", expect_stopped=False) @capability("supportsInstructionBreakpoints") def set_insn_breakpoints( *, breakpoints: Sequence, offset: Optional[int] = None, **args @@ -414,7 +410,7 @@ def _rewrite_exception_breakpoint( } -@request("setExceptionBreakpoints") +@request("setExceptionBreakpoints", expect_stopped=False) @capability("supportsExceptionFilterOptions") @capability( "exceptionBreakpointFilters", diff --git a/gdb/python/lib/gdb/dap/completions.py b/gdb/python/lib/gdb/dap/completions.py index 85acc43..e5003ff 100644 --- a/gdb/python/lib/gdb/dap/completions.py +++ b/gdb/python/lib/gdb/dap/completions.py @@ -39,8 +39,11 @@ def completions( line = 1 else: line = import_line(line) - text = text.splitlines()[line - 1] - text = text[: column - 1] + if text: + text = text.splitlines()[line - 1] + text = text[: column - 1] + else: + text = "" mi_result = exec_mi_and_log("-complete", text) result = [] completion = None diff --git a/gdb/python/lib/gdb/dap/evaluate.py b/gdb/python/lib/gdb/dap/evaluate.py index 55538e6..fcbcc99 100644 --- a/gdb/python/lib/gdb/dap/evaluate.py +++ b/gdb/python/lib/gdb/dap/evaluate.py @@ -69,7 +69,7 @@ def _repl(command, frame_id): } -@request("evaluate") +@request("evaluate", defer_events=False) @capability("supportsEvaluateForHovers") @capability("supportsValueFormattingOptions") def eval_request( @@ -110,7 +110,7 @@ def variables( @capability("supportsSetExpression") -@request("setExpression") +@request("setExpression", defer_events=False) def set_expression( *, expression: str, value: str, frameId: Optional[int] = None, format=None, **args ): @@ -126,7 +126,7 @@ def set_expression( @capability("supportsSetVariable") -@request("setVariable") +@request("setVariable", defer_events=False) def set_variable( *, variablesReference: int, name: str, value: str, format=None, **args ): diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py index 54caea0..e8f2655 100644 --- a/gdb/python/lib/gdb/dap/events.py +++ b/gdb/python/lib/gdb/dap/events.py @@ -17,12 +17,12 @@ import gdb from .modules import is_module, make_module from .scopes import set_finish_value -from .server import send_event, send_event_maybe_later +from .server import send_event from .startup import exec_and_log, in_gdb_thread, log # True when the inferior is thought to be running, False otherwise. # This may be accessed from any thread, which can be racy. However, -# this unimportant because this global is only used for the +# this is unimportant because this global is only used for the # 'notStopped' response, which itself is inherently racy. inferior_running = False @@ -238,10 +238,9 @@ def _on_stop(event): ): obj["reason"] = "pause" else: - global stop_reason_map obj["reason"] = stop_reason_map[event.details["reason"]] _expected_pause = False - send_event_maybe_later("stopped", obj) + send_event("stopped", obj) # This keeps a bit of state between the start of an inferior call and diff --git a/gdb/python/lib/gdb/dap/frames.py b/gdb/python/lib/gdb/dap/frames.py index 6433dbe..4dacb87 100644 --- a/gdb/python/lib/gdb/dap/frames.py +++ b/gdb/python/lib/gdb/dap/frames.py @@ -53,12 +53,11 @@ gdb.events.cont.connect(_clear_frame_ids) @in_gdb_thread def frame_for_id(id): """Given a frame identifier ID, return the corresponding frame.""" - global thread_ids if id in thread_ids: thread_id = thread_ids[id] if thread_id != gdb.selected_thread().global_num: set_thread(thread_id) - global _all_frames + return _all_frames[id] @@ -103,10 +102,8 @@ def _frame_id_generator(): # Helper function to assign an ID to a frame. def get_id(frame): - global _all_frames num = len(_all_frames) _all_frames.append(frame) - global thread_ids thread_ids[num] = gdb.selected_thread().global_num return num @@ -128,7 +125,6 @@ def _frame_id_generator(): @in_gdb_thread def _get_frame_iterator(): thread_id = gdb.selected_thread().global_num - global _iter_map if thread_id not in _iter_map: _iter_map[thread_id] = _MemoizingIterator(_frame_id_generator()) return _iter_map[thread_id] diff --git a/gdb/python/lib/gdb/dap/globalvars.py b/gdb/python/lib/gdb/dap/globalvars.py index 2e4b2a6..9d64d28 100644 --- a/gdb/python/lib/gdb/dap/globalvars.py +++ b/gdb/python/lib/gdb/dap/globalvars.py @@ -78,7 +78,6 @@ def get_global_scope(frame): except RuntimeError: return None - global _id_to_scope block = block.static_block if block in _id_to_scope: return _id_to_scope[block] diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py index e093e60..8ac4c77 100644 --- a/gdb/python/lib/gdb/dap/launch.py +++ b/gdb/python/lib/gdb/dap/launch.py @@ -164,7 +164,6 @@ def attach( @request("configurationDone", on_dap_thread=True) def config_done(**args): # Handle the launch or attach. - global _launch_or_attach_promise if _launch_or_attach_promise is None: raise DAPException("launch or attach not specified") # Resolve the launch or attach, but only after the diff --git a/gdb/python/lib/gdb/dap/next.py b/gdb/python/lib/gdb/dap/next.py index ddf4e05..898fff1 100644 --- a/gdb/python/lib/gdb/dap/next.py +++ b/gdb/python/lib/gdb/dap/next.py @@ -16,7 +16,7 @@ import gdb from .events import exec_and_expect_stop -from .server import capability, request, send_gdb, send_gdb_with_response +from .server import capability, request from .startup import in_gdb_thread from .state import set_thread @@ -73,19 +73,14 @@ def step_in( exec_and_expect_stop(cmd) -@request("stepOut", defer_stop_events=True) +@request("stepOut") def step_out(*, threadId: int, singleThread: bool = False, **args): _handle_thread_step(threadId, singleThread, True) exec_and_expect_stop("finish &", propagate_exception=True) -# This is a server-side request because it is funny: it wants to -# 'continue' but also return a result, which precludes using -# response=False. Using 'continue &' would mostly work ok, but this -# yields races when a stop occurs before the response is sent back to -# the client. -@request("continue", on_dap_thread=True) +@request("continue") def continue_request(*, threadId: int, singleThread: bool = False, **args): - locked = send_gdb_with_response(lambda: _handle_thread_step(threadId, singleThread)) - send_gdb(lambda: exec_and_expect_stop("continue")) + locked = _handle_thread_step(threadId, singleThread) + exec_and_expect_stop("continue &") return {"allThreadsContinued": not locked} diff --git a/gdb/python/lib/gdb/dap/scopes.py b/gdb/python/lib/gdb/dap/scopes.py index f499dcb..7ce3a7f 100644 --- a/gdb/python/lib/gdb/dap/scopes.py +++ b/gdb/python/lib/gdb/dap/scopes.py @@ -120,7 +120,6 @@ class _FinishScopeReference(_ScopeReference): def fetch_one_child(self, idx): assert idx == 0 - global _last_return_value return ("(return)", _last_return_value) @@ -145,8 +144,6 @@ class _RegisterReference(_ScopeReference): @request("scopes") def scopes(*, frameId: int, **extra): - global _last_return_value - global frame_to_scope if frameId in frame_to_scope: scopes = frame_to_scope[frameId] else: diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py index d4314e8..7dab582 100644 --- a/gdb/python/lib/gdb/dap/server.py +++ b/gdb/python/lib/gdb/dap/server.py @@ -74,6 +74,13 @@ class DeferredRequest: self._result = result @in_dap_thread + def defer_events(self): + """Return True if events should be deferred during execution. + + This may be overridden by subclasses.""" + return True + + @in_dap_thread def invoke(self): """Implement the deferred request. @@ -95,7 +102,10 @@ class DeferredRequest: """ with _server.canceller.current_request(self._req): + if self.defer_events(): + _server.set_defer_events() _server.invoke_request(self._req, self._result, self.invoke) + _server.emit_pending_events() # A subclass of Exception that is used solely for reporting that a @@ -230,7 +240,7 @@ class Server: self._out_stream = out_stream self._child_stream = child_stream self._delayed_fns_lock = threading.Lock() - self.defer_stop_events = False + self._defer_events = False self._delayed_fns = [] # This queue accepts JSON objects that are then sent to the # DAP client. Writing is done in a separate thread to avoid @@ -307,7 +317,6 @@ class Server: args = {} def fn(): - global _commands return _commands[params["command"]](**args) self.invoke_request(req, result, fn) @@ -317,7 +326,7 @@ class Server: def _read_inferior_output(self): while True: line = self._child_stream.readline() - self.send_event( + self.send_event_maybe_later( "output", { "category": "stdout", @@ -357,6 +366,17 @@ class Server: self._read_queue.put(None) @in_dap_thread + def emit_pending_events(self): + """Emit any pending events.""" + fns = None + with self._delayed_fns_lock: + fns = self._delayed_fns + self._delayed_fns = [] + self._defer_events = False + for fn in fns: + fn() + + @in_dap_thread def main_loop(self): """The main loop of the DAP server.""" # Before looping, start the thread that writes JSON to the @@ -372,13 +392,7 @@ class Server: req = cmd["seq"] with self.canceller.current_request(req): self._handle_command(cmd) - fns = None - with self._delayed_fns_lock: - fns = self._delayed_fns - self._delayed_fns = [] - self.defer_stop_events = False - for fn in fns: - fn() + self.emit_pending_events() # Got the terminate request. This is handled by the # JSON-writing thread, so that we can ensure that all # responses are flushed to the client before exiting. @@ -387,13 +401,13 @@ class Server: send_gdb("quit") @in_dap_thread - def send_event_later(self, event, body=None): - """Send a DAP event back to the client, but only after the - current request has completed.""" + def set_defer_events(self): + """Defer any events until the current request has completed.""" with self._delayed_fns_lock: - self._delayed_fns.append(lambda: self.send_event(event, body)) + self._defer_events = True - @in_gdb_thread + # Note that this does not need to be run in any particular thread, + # because it uses locks for thread-safety. def send_event_maybe_later(self, event, body=None): """Send a DAP event back to the client, but if a request is in-flight within the dap thread and that request is configured to delay the event, @@ -402,10 +416,10 @@ class Server: with self.canceller.lock: if self.canceller.in_flight_dap_thread: with self._delayed_fns_lock: - if self.defer_stop_events: - self._delayed_fns.append(lambda: self.send_event(event, body)) + if self._defer_events: + self._delayed_fns.append(lambda: self._send_event(event, body)) return - self.send_event(event, body) + self._send_event(event, body) @in_dap_thread def call_function_later(self, fn): @@ -416,7 +430,7 @@ class Server: # Note that this does not need to be run in any particular thread, # because it just creates an object and writes it to a thread-safe # queue. - def send_event(self, event, body=None): + def _send_event(self, event, body=None): """Send an event to the DAP client. EVENT is the name of the event, a string. BODY is the body of the event, an arbitrary object.""" @@ -440,22 +454,11 @@ def send_event(event, body=None): """Send an event to the DAP client. EVENT is the name of the event, a string. BODY is the body of the event, an arbitrary object.""" - global _server - _server.send_event(event, body) - - -def send_event_maybe_later(event, body=None): - """Send a DAP event back to the client, but if a request is in-flight - within the dap thread and that request is configured to delay the event, - wait until the response has been sent until the event is sent back to - the client.""" - global _server _server.send_event_maybe_later(event, body) def call_function_later(fn): """Call FN later -- after the current request's response has been sent.""" - global _server _server.call_function_later(fn) @@ -480,7 +483,7 @@ def request( response: bool = True, on_dap_thread: bool = False, expect_stopped: bool = True, - defer_stop_events: bool = False + defer_events: bool = True ): """A decorator for DAP requests. @@ -502,9 +505,9 @@ def request( inferior is running. When EXPECT_STOPPED is False, the request will proceed regardless of the inferior's state. - If DEFER_STOP_EVENTS is True, then make sure any stop events sent - during the request processing are not sent to the client until the - response has been sent. + If DEFER_EVENTS is True, then make sure any events sent during the + request processing are not sent to the client until the response + has been sent. """ # Validate the parameters. @@ -527,27 +530,33 @@ def request( # Verify that the function is run on the correct thread. if on_dap_thread: - cmd = in_dap_thread(func) + check_cmd = in_dap_thread(func) else: func = in_gdb_thread(func) if response: - if defer_stop_events: - global _server - if _server is not None: - with _server.delayed_events_lock: - _server.defer_stop_events = True def sync_call(**args): return send_gdb_with_response(lambda: func(**args)) - cmd = sync_call + check_cmd = sync_call else: def non_sync_call(**args): return send_gdb(lambda: func(**args)) - cmd = non_sync_call + check_cmd = non_sync_call + + if defer_events: + + def deferring(**args): + _server.set_defer_events() + return check_cmd(**args) + + cmd = deferring + + else: + cmd = check_cmd # If needed, check that the inferior is not running. This # wrapping is done last, so the check is done first, before @@ -555,7 +564,6 @@ def request( if expect_stopped: cmd = _check_not_running(cmd) - global _commands assert name not in _commands _commands[name] = cmd return cmd @@ -568,7 +576,6 @@ def capability(name, value=True): the DAP capability NAME.""" def wrap(func): - global _capabilities assert name not in _capabilities _capabilities[name] = value return func @@ -581,7 +588,6 @@ def client_bool_capability(name, default=False): If the capability was not specified, or did not have boolean type, DEFAULT is returned. DEFAULT defaults to False.""" - global _server if name in _server.config and isinstance(_server.config[name], bool): return _server.config[name] return default @@ -589,9 +595,8 @@ def client_bool_capability(name, default=False): @request("initialize", on_dap_thread=True) def initialize(**args): - global _server, _capabilities _server.config = args - _server.send_event_later("initialized") + _server.send_event_maybe_later("initialized") global _lines_start_at_1 _lines_start_at_1 = client_bool_capability("linesStartAt1", True) global _columns_start_at_1 @@ -705,7 +710,6 @@ def export_line(line: int) -> int: """Rewrite LINE according to client capability. This applies the linesStartAt1 capability as needed, when sending a line number from gdb to the client.""" - global _lines_start_at_1 if not _lines_start_at_1: # In gdb, lines start at 1, so we only need to change this if # the client starts at 0. @@ -717,7 +721,6 @@ def import_line(line: int) -> int: """Rewrite LINE according to client capability. This applies the linesStartAt1 capability as needed, when the client sends a line number to gdb.""" - global _lines_start_at_1 if not _lines_start_at_1: # In gdb, lines start at 1, so we only need to change this if # the client starts at 0. diff --git a/gdb/python/lib/gdb/dap/sources.py b/gdb/python/lib/gdb/dap/sources.py index 938c93a..efcd799 100644 --- a/gdb/python/lib/gdb/dap/sources.py +++ b/gdb/python/lib/gdb/dap/sources.py @@ -37,7 +37,6 @@ def make_source(fullname, filename=None): FILENAME is the base name; if None (the default), then it is computed from FULLNAME. """ - global _source_map if fullname in _source_map: result = _source_map[fullname] else: @@ -53,7 +52,6 @@ def make_source(fullname, filename=None): global _next_source result["sourceReference"] = _next_source - global _id_map _id_map[_next_source] = result _next_source += 1 @@ -66,12 +64,11 @@ 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"] - global _id_map if ref not in _id_map: raise DAPException("no sourceReference " + str(ref)) return _id_map[ref]["path"] diff --git a/gdb/python/lib/gdb/dap/threads.py b/gdb/python/lib/gdb/dap/threads.py index c271961..89046a8 100644 --- a/gdb/python/lib/gdb/dap/threads.py +++ b/gdb/python/lib/gdb/dap/threads.py @@ -16,27 +16,32 @@ import gdb from .server import request +from .startup import in_gdb_thread +@in_gdb_thread def _thread_name(thr): if thr.name is not None: return thr.name if thr.details is not None: return thr.details - return None + # Always return a name, as the protocol doesn't allow for nameless + # threads. Use the local thread number here... it doesn't matter + # without multi-inferior but in that case it might make more + # sense. + return f"Thread #{thr.num}" -@request("threads") +@request("threads", expect_stopped=False) def threads(**args): result = [] for thr in gdb.selected_inferior().threads(): - one_result = { - "id": thr.global_num, - } - name = _thread_name(thr) - if name is not None: - one_result["name"] = name - result.append(one_result) + result.append( + { + "id": thr.global_num, + "name": _thread_name(thr), + } + ) return { "threads": result, } diff --git a/gdb/python/lib/gdb/dap/varref.py b/gdb/python/lib/gdb/dap/varref.py index 42b5302..8a13c51 100644 --- a/gdb/python/lib/gdb/dap/varref.py +++ b/gdb/python/lib/gdb/dap/varref.py @@ -69,7 +69,6 @@ class BaseReference(ABC): NAME is a string or None. None means this does not have a name, e.g., the result of expression evaluation.""" - global all_variables all_variables.append(self) self._ref = len(all_variables) self._name = name @@ -267,7 +266,7 @@ class VariableReference(BaseReference): @in_gdb_thread def find_variable(ref): """Given a variable reference, return the corresponding variable object.""" - global all_variables + # Variable references are offset by 1. ref = ref - 1 if ref < 0 or ref > len(all_variables): diff --git a/gdb/python/lib/gdb/styling.py b/gdb/python/lib/gdb/styling.py index e9387e3..60c470f 100644 --- a/gdb/python/lib/gdb/styling.py +++ b/gdb/python/lib/gdb/styling.py @@ -80,7 +80,6 @@ try: # ignore. pass - global _asm_lexers if lexer_type not in _asm_lexers: _asm_lexers[lexer_type] = lexers.get_lexer_by_name(lexer_type) _asm_lexers[lexer_type].add_filter(HandleNasmComments()) diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c index 58998f5..9ce8671 100644 --- a/gdb/python/py-breakpoint.c +++ b/gdb/python/py-breakpoint.c @@ -1537,9 +1537,7 @@ PyTypeObject breakpoint_object_type = 0, /* tp_alloc */ }; -void _initialize_py_breakpoint (); -void -_initialize_py_breakpoint () +INIT_GDB_FILE (py_breakpoint) { add_setshow_boolean_cmd ("py-breakpoint", class_maintenance, &pybp_debug, diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c index 5d98d03..dc5e270 100644 --- a/gdb/python/py-cmd.c +++ b/gdb/python/py-cmd.c @@ -105,19 +105,17 @@ cmdpy_function (const char *args, int from_tty, cmd_list_element *command) gdbpy_enter enter_py; - if (! obj) + if (obj == nullptr) error (_("Invalid invocation of Python command object.")); - if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst)) - { - if (obj->command->is_prefix ()) - { - /* A prefix command does not need an invoke method. */ - return; - } - error (_("Python command object missing 'invoke' method.")); - } - if (! args) + /* If we get here for a prefix command then the prefix command had an + 'invoke' method when it was created. If the 'invoke' method is now + missing, then the user has done something weird (like deleting the + invoke method, yuck!). */ + if (!PyObject_HasAttr ((PyObject *) obj, invoke_cst)) + error (_("Python command object missing 'invoke' method.")); + + if (args == nullptr) args = ""; gdbpy_ref<> argobj (PyUnicode_Decode (args, strlen (args), host_charset (), NULL)); @@ -336,24 +334,30 @@ cmdpy_completer (struct cmd_list_element *command, name of the new command. All earlier words must be existing prefix commands. - *BASE_LIST is set to the final prefix command's list of - *sub-commands. + *BASE_LIST is set to the final prefix command's list of sub-commands. START_LIST is the list in which the search starts. + When PREFIX_CMD is not NULL then *PREFIX_CMD is set to the prefix + command itself, or NULL, if there is no prefix command. + This function returns the name of the new command. On error sets the Python error and returns NULL. */ gdb::unique_xmalloc_ptr<char> gdbpy_parse_command_name (const char *name, struct cmd_list_element ***base_list, - struct cmd_list_element **start_list) + struct cmd_list_element **start_list, + struct cmd_list_element **prefix_cmd) { struct cmd_list_element *elt; int len = strlen (name); int i, lastchar; const char *prefix_text2; + if (prefix_cmd != nullptr) + *prefix_cmd = nullptr; + /* Skip trailing whitespace. */ for (i = len - 1; i >= 0 && (name[i] == ' ' || name[i] == '\t'); --i) ; @@ -368,9 +372,8 @@ gdbpy_parse_command_name (const char *name, for (; i > 0 && valid_cmd_char_p (name[i - 1]); --i) ; - gdb::unique_xmalloc_ptr<char> result ((char *) xmalloc (lastchar - i + 2)); - memcpy (result.get (), &name[i], lastchar - i + 1); - result.get ()[lastchar - i + 1] = '\0'; + gdb::unique_xmalloc_ptr<char> result + = make_unique_xstrndup (&name[i], lastchar - i + 1); /* Skip whitespace again. */ for (--i; i >= 0 && (name[i] == ' ' || name[i] == '\t'); --i) @@ -385,7 +388,7 @@ gdbpy_parse_command_name (const char *name, prefix_text2 = prefix_text.c_str (); elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1); - if (elt == NULL || elt == CMD_LIST_AMBIGUOUS) + if (elt == nullptr || elt == CMD_LIST_AMBIGUOUS || *prefix_text2 != '\0') { PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."), prefix_text.c_str ()); @@ -395,6 +398,8 @@ gdbpy_parse_command_name (const char *name, if (elt->is_prefix ()) { *base_list = elt->subcommands; + if (prefix_cmd != nullptr) + *prefix_cmd = elt; return result; } @@ -469,8 +474,9 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw) return -1; } + cmd_list_element *prefix_cmd = nullptr; gdb::unique_xmalloc_ptr<char> cmd_name - = gdbpy_parse_command_name (name, &cmd_list, &cmdlist); + = gdbpy_parse_command_name (name, &cmd_list, &cmdlist, &prefix_cmd); if (cmd_name == nullptr) return -1; @@ -507,26 +513,64 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw) if (is_prefix) { - int allow_unknown; - - /* If we have our own "invoke" method, then allow unknown - sub-commands. */ - allow_unknown = PyObject_HasAttr (self, invoke_cst); - cmd = add_prefix_cmd (cmd_name.get (), - (enum command_class) cmdtype, - NULL, docstring.release (), &obj->sub_list, - allow_unknown, cmd_list); + bool has_invoke = PyObject_HasAttr (self, invoke_cst) == 1; + if (has_invoke) + { + /* If there's an 'invoke' method, then create the prefix + command, but call cmdpy_function to dispatch to the invoke + method when the user runs the prefix with no sub-command. */ + cmd = add_prefix_cmd (cmd_name.get (), + (enum command_class) cmdtype, + nullptr, + docstring.release (), &obj->sub_list, + 1 /* allow_unknown */, cmd_list); + cmd->func = cmdpy_function; + } + else + { + /* If there is no 'invoke' method, then create the prefix + using the standard prefix callbacks. This means that for + 'set prefix' the user will get the help text listing all + of the sub-commands, and for 'show prefix', the user will + see all of the sub-command values. */ + if (prefix_cmd != nullptr) + { + while (prefix_cmd->prefix != nullptr) + prefix_cmd = prefix_cmd->prefix; + } + + bool is_show = (prefix_cmd != nullptr + && prefix_cmd->subcommands == &showlist); + + if (is_show) + cmd = add_show_prefix_cmd (cmd_name.get (), + (enum command_class) cmdtype, + docstring.release (), + &obj->sub_list, + 0 /* allow_unknown */, cmd_list); + else + cmd = add_basic_prefix_cmd (cmd_name.get (), + (enum command_class) cmdtype, + docstring.release (), + &obj->sub_list, + 0 /* allow_unknown */, cmd_list); + } } else - cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype, - docstring.release (), cmd_list); + { + /* For non-prefix commands, arrange to call cmdpy_function, which + invokes the Python 'invoke' method, or raises an exception if + the 'invoke' method is missing. */ + cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype, + docstring.release (), cmd_list); + cmd->func = cmdpy_function; + } /* If successful, the above takes ownership of the name, since we set name_allocated, so release it. */ cmd_name.release (); - /* There appears to be no API to set this. */ - cmd->func = cmdpy_function; + /* There appears to be no API to set these member variables. */ cmd->destroyer = cmdpy_destroyer; cmd->doc_allocated = 1; cmd->name_allocated = 1; 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/py-connection.c b/gdb/python/py-connection.c index 93757ac..a6d9ad0 100644 --- a/gdb/python/py-connection.c +++ b/gdb/python/py-connection.c @@ -428,9 +428,7 @@ connpy_send_packet (PyObject *self, PyObject *args, PyObject *kw) /* Global initialization for this file. */ -void _initialize_py_connection (); -void -_initialize_py_connection () +INIT_GDB_FILE (py_connection) { gdb::observers::connection_removed.attach (connpy_connection_removed, "py-connection"); diff --git a/gdb/python/py-dap.c b/gdb/python/py-dap.c index 7196d6c..9a9875a 100644 --- a/gdb/python/py-dap.c +++ b/gdb/python/py-dap.c @@ -110,9 +110,7 @@ dap_interp::pre_command_loop () call_dap_fn ("pre_command_loop"); } -void _initialize_py_interp (); -void -_initialize_py_interp () +INIT_GDB_FILE (py_interp) { /* The dap code uses module typing, available starting python 3.5. */ #if PY_VERSION_HEX >= 0x03050000 diff --git a/gdb/python/py-gdb-readline.c b/gdb/python/py-gdb-readline.c index ea3a385..70ceebb 100644 --- a/gdb/python/py-gdb-readline.c +++ b/gdb/python/py-gdb-readline.c @@ -29,11 +29,7 @@ static char * gdbpy_readline_wrapper (FILE *sys_stdin, FILE *sys_stdout, -#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 4 const char *prompt) -#else - char *prompt) -#endif { int n; const char *p = NULL; diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c index e4f07c9..72f427f 100644 --- a/gdb/python/py-micmd.c +++ b/gdb/python/py-micmd.c @@ -578,9 +578,7 @@ PyTypeObject micmdpy_object_type = { 0, /* tp_alloc */ }; -void _initialize_py_micmd (); -void -_initialize_py_micmd () +INIT_GDB_FILE (py_micmd) { add_setshow_boolean_cmd ("py-micmd", class_maintenance, &pymicmd_debug, diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c index 763680e..06237b6 100644 --- a/gdb/python/py-param.c +++ b/gdb/python/py-param.c @@ -495,7 +495,11 @@ get_doc_string (PyObject *object, enum doc_string_type doc_type, } } - if (result == nullptr) + /* For the set/show docs, if these strings are empty then we set then to + a non-empty string. This ensures that the command has some sane + documentation for its 'help' text. */ + if (result == nullptr + || (doc_type != doc_string_description && *result == '\0')) { if (doc_type == doc_string_description) result.reset (xstrdup (_("This command is not documented."))); @@ -904,6 +908,18 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds) show_doc = get_doc_string (self, doc_string_show, name); doc = get_doc_string (self, doc_string_description, cmd_name.get ()); + /* The set/show docs should always be a non-empty string. */ + gdb_assert (set_doc != nullptr && *set_doc != '\0'); + gdb_assert (show_doc != nullptr && *show_doc != '\0'); + + /* For the DOC string only, if it is the empty string, then we convert it + to NULL. This means GDB will not even display a blank line for this + part of the help text, instead the set/show line is all the user will + get. */ + gdb_assert (doc != nullptr); + if (*doc == '\0') + doc = nullptr; + Py_INCREF (self); try diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index f37afde..c546aa7 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -158,7 +158,7 @@ convert_field (struct type *type, int field) } else { - if (type->field (field).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK) + if (type->field (field).loc_is_dwarf_block ()) arg = gdbpy_ref<>::new_reference (Py_None); else arg = gdb_py_object_from_longest (type->field (field).loc_bitpos ()); diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index d43d7e9..dc078ec 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -1029,9 +1029,7 @@ gdbpy_initialize_unwind (void) return 0; } -void _initialize_py_unwind (); -void -_initialize_py_unwind () +INIT_GDB_FILE (py_unwind) { add_setshow_boolean_cmd ("py-unwind", class_maintenance, &pyuw_debug, diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 5262d76..7f4237e 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -88,6 +88,8 @@ #include <frameobject.h> #include "py-ref.h" +static_assert (PY_VERSION_HEX >= 0x03040000); + #define Py_TPFLAGS_CHECKTYPES 0 /* If Python.h does not define WITH_THREAD, then the various @@ -135,17 +137,6 @@ typedef unsigned long gdb_py_ulongest; #endif /* HAVE_LONG_LONG */ -#if PY_VERSION_HEX < 0x03020000 -typedef long Py_hash_t; -#endif - -/* PyMem_RawMalloc appeared in Python 3.4. For earlier versions, we can just - fall back to PyMem_Malloc. */ - -#if PY_VERSION_HEX < 0x03040000 -#define PyMem_RawMalloc PyMem_Malloc -#endif - /* A template variable holding the format character (as for Py_BuildValue) for a given type. */ template<typename T> @@ -519,7 +510,8 @@ PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args); PyObject *gdbpy_parameter_value (const setting &var); gdb::unique_xmalloc_ptr<char> gdbpy_parse_command_name (const char *name, struct cmd_list_element ***base_list, - struct cmd_list_element **start_list); + struct cmd_list_element **start_list, + struct cmd_list_element **prefix_cmd = nullptr); PyObject *gdbpy_register_tui_window (PyObject *self, PyObject *args, PyObject *kw); diff --git a/gdb/python/python.c b/gdb/python/python.c index 7e28eb6..cb0d642 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1612,16 +1612,53 @@ 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; +} + +/* Implement gdb.warning(). Takes a single text string argument and emit a + warning using GDB's 'warning' function. The input text string must not + be empty. */ + +static PyObject * +gdbpy_warning (PyObject *self, PyObject *args, PyObject *kw) +{ + const char *text; + static const char *keywords[] = { "text", nullptr }; + + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s", keywords, &text)) + return nullptr; + + if (strlen (text) == 0) + { + PyErr_SetString (PyExc_ValueError, + _("Empty text string passed to gdb.warning")); + return nullptr; + } + + try + { + warning ("%s", text); + } + catch (const gdb_exception &ex) + { + /* The warning() call probably cannot throw an exception. But just + in case it ever does. */ + return gdbpy_handle_gdb_exception (nullptr, ex); } Py_RETURN_NONE; @@ -2443,7 +2480,7 @@ py_initialize () /foo/lib/pythonX.Y/... This must be done before calling Py_Initialize. */ gdb::unique_xmalloc_ptr<char> progname - (concat (ldirname (python_libdir.c_str ()).c_str (), SLASH_STRING, "bin", + (concat (gdb_ldirname (python_libdir.c_str ()).c_str (), SLASH_STRING, "bin", SLASH_STRING, "python", (char *) NULL)); { @@ -2680,9 +2717,7 @@ test_python () /* See python.h. */ cmd_list_element *python_cmd_element = nullptr; -void _initialize_python (); -void -_initialize_python () +INIT_GDB_FILE (python) { cmd_list_element *python_interactive_cmd = add_com ("python-interactive", class_obscure, @@ -3121,6 +3156,12 @@ Return the current print options." }, METH_VARARGS | METH_KEYWORDS, "notify_mi (name, data) -> None\n\ Output async record to MI channels if any." }, + + { "warning", (PyCFunction) gdbpy_warning, + METH_VARARGS | METH_KEYWORDS, + "warning (text) -> None\n\ +Print a warning." }, + {NULL, NULL, 0, NULL} }; diff --git a/gdb/ravenscar-thread.c b/gdb/ravenscar-thread.c index 13258c4..4d79b3d 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 @@ -921,9 +921,7 @@ Support for Ravenscar task/thread switching is disabled\n")); /* Module startup initialization function, automagically called by init.c. */ -void _initialize_ravenscar (); -void -_initialize_ravenscar () +INIT_GDB_FILE (ravenscar) { /* Notice when the inferior is created in order to push the ravenscar ops if needed. */ diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c index 2d71b72..d789992 100644 --- a/gdb/record-btrace.c +++ b/gdb/record-btrace.c @@ -132,10 +132,7 @@ public: bool can_execute_reverse () override; bool stopped_by_sw_breakpoint () override; - bool supports_stopped_by_sw_breakpoint () override; - bool stopped_by_hw_breakpoint () override; - bool supports_stopped_by_hw_breakpoint () override; enum exec_direction_kind execution_direction () override; void prepare_to_generate_core () override; @@ -2581,7 +2578,7 @@ record_btrace_maybe_mark_async_event const std::vector<thread_info *> &no_history) { bool more_moving = !moving.empty (); - bool more_no_history = !no_history.empty ();; + bool more_no_history = !no_history.empty (); if (!more_moving && !more_no_history) return; @@ -2773,18 +2770,6 @@ record_btrace_target::stopped_by_sw_breakpoint () return this->beneath ()->stopped_by_sw_breakpoint (); } -/* The supports_stopped_by_sw_breakpoint method of target - record-btrace. */ - -bool -record_btrace_target::supports_stopped_by_sw_breakpoint () -{ - if (record_is_replaying (minus_one_ptid)) - return true; - - return this->beneath ()->supports_stopped_by_sw_breakpoint (); -} - /* The stopped_by_sw_breakpoint method of target record-btrace. */ bool @@ -2800,18 +2785,6 @@ record_btrace_target::stopped_by_hw_breakpoint () return this->beneath ()->stopped_by_hw_breakpoint (); } -/* The supports_stopped_by_hw_breakpoint method of target - record-btrace. */ - -bool -record_btrace_target::supports_stopped_by_hw_breakpoint () -{ - if (record_is_replaying (minus_one_ptid)) - return true; - - return this->beneath ()->supports_stopped_by_hw_breakpoint (); -} - /* The update_thread_list method of target record-btrace. */ void @@ -3213,9 +3186,7 @@ set_record_pt_event_tracing_value (const char *args, int from_tty, /* Initialize btrace commands. */ -void _initialize_record_btrace (); -void -_initialize_record_btrace () +INIT_GDB_FILE (record_btrace) { cmd_list_element *record_btrace_cmd = add_prefix_cmd ("btrace", class_obscure, cmd_record_btrace_start, diff --git a/gdb/record-full.c b/gdb/record-full.c index b52fb06..7e3da27 100644 --- a/gdb/record-full.c +++ b/gdb/record-full.c @@ -2877,9 +2877,7 @@ maintenance_print_record_instruction (const char *args, int from_tty) } } -void _initialize_record_full (); -void -_initialize_record_full () +INIT_GDB_FILE (record_full) { struct cmd_list_element *c; diff --git a/gdb/record.c b/gdb/record.c index 440c5e9..248cfaa 100644 --- a/gdb/record.c +++ b/gdb/record.c @@ -769,9 +769,7 @@ set_record_call_history_size (const char *args, int from_tty, &record_call_history_size); } -void _initialize_record (); -void -_initialize_record () +INIT_GDB_FILE (record) { struct cmd_list_element *c; diff --git a/gdb/regcache-dump.c b/gdb/regcache-dump.c index 39dd8c5..c6ef552 100644 --- a/gdb/regcache-dump.c +++ b/gdb/regcache-dump.c @@ -346,9 +346,7 @@ maintenance_print_remote_registers (const char *args, int from_tty) "maintenance print remote-registers"); } -void _initialize_regcache_dump (); -void -_initialize_regcache_dump () +INIT_GDB_FILE (regcache_dump) { add_cmd ("registers", class_maintenance, maintenance_print_registers, _("Print the internal register configuration.\n" diff --git a/gdb/regcache.c b/gdb/regcache.c index e3d435f..9892c54 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -2213,9 +2213,7 @@ regcache_thread_ptid_changed () } /* namespace selftests */ #endif /* GDB_SELF_TEST */ -void _initialize_regcache (); -void -_initialize_regcache () +INIT_GDB_FILE (regcache) { struct cmd_list_element *c; diff --git a/gdb/reggroups.c b/gdb/reggroups.c index c931434..596b336 100644 --- a/gdb/reggroups.c +++ b/gdb/reggroups.c @@ -259,9 +259,7 @@ const reggroup *const all_reggroup = &all_group; const reggroup *const save_reggroup = &save_group; const reggroup *const restore_reggroup = &restore_group; -void _initialize_reggroup (); -void -_initialize_reggroup () +INIT_GDB_FILE (reggroup) { add_cmd ("reggroups", class_maintenance, maintenance_print_reggroups, _("\ diff --git a/gdb/remote-notif.c b/gdb/remote-notif.c index 053591c..da1b578 100644 --- a/gdb/remote-notif.c +++ b/gdb/remote-notif.c @@ -234,9 +234,7 @@ remote_notif_state::~remote_notif_state () delete_async_event_handler (&get_pending_events_token); } -void _initialize_notif (); -void -_initialize_notif () +INIT_GDB_FILE (notif) { add_setshow_boolean_cmd ("notification", no_class, ¬if_debug, _("\ diff --git a/gdb/remote-sim.c b/gdb/remote-sim.c index fc2c900..425e50d 100644 --- a/gdb/remote-sim.c +++ b/gdb/remote-sim.c @@ -1284,9 +1284,7 @@ gdbsim_target::memory_map () return result; } -void _initialize_remote_sim (); -void -_initialize_remote_sim () +INIT_GDB_FILE (remote_sim) { struct cmd_list_element *c; diff --git a/gdb/remote.c b/gdb/remote.c index 75cc21c..6208a90 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -80,6 +80,7 @@ #include "async-event.h" #include "gdbsupport/selftest.h" #include "cli/cli-style.h" +#include "gdbsupport/remote-args.h" /* The remote target. */ @@ -250,6 +251,7 @@ enum { PACKET_vFile_readlink, PACKET_vFile_fstat, PACKET_vFile_stat, + PACKET_vFile_lstat, PACKET_qXfer_auxv, PACKET_qXfer_features, PACKET_qXfer_exec_file, @@ -1022,8 +1024,8 @@ public: int fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno) override; - int fileio_stat (struct inferior *inf, const char *filename, - struct stat *sb, fileio_error *target_errno) override; + int fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) override; int fileio_close (int fd, fileio_error *target_errno) override; @@ -10835,16 +10837,15 @@ remote_target::extended_remote_run (const std::string &args) if (!args.empty ()) { - int i; + std::vector<std::string> split_args = gdb::remote_args::split (args); - gdb_argv argv (args.c_str ()); - for (i = 0; argv[i] != NULL; i++) + for (const auto &a : split_args) { - if (strlen (argv[i]) * 2 + 1 + len >= get_remote_packet_size ()) + if (a.size () * 2 + 1 + len >= get_remote_packet_size ()) error (_("Argument list too long for run packet")); rs->buf[len++] = ';'; - len += 2 * bin2hex ((gdb_byte *) argv[i], rs->buf.data () + len, - strlen (argv[i])); + len += 2 * bin2hex ((gdb_byte *) a.c_str (), rs->buf.data () + len, + a.size ()); } } @@ -11650,7 +11651,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 +11716,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 +12015,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. */ @@ -13149,7 +13150,7 @@ remote_target::fileio_readlink (struct inferior *inf, const char *filename, return ret; } -/* Helper function to handle ::fileio_fstat and ::fileio_stat result +/* Helper function to handle ::fileio_fstat and ::fileio_lstat result processing. When this function is called the remote syscall has been performed and we know we didn't get an error back. @@ -13160,10 +13161,10 @@ remote_target::fileio_readlink (struct inferior *inf, const char *filename, data) is to be placed in ST. */ static int -fileio_process_fstat_and_stat_reply (const char *attachment, - int attachment_len, - int expected_len, - struct stat *st) +fileio_process_fstat_and_lstat_reply (const char *attachment, + int attachment_len, + int expected_len, + struct stat *st) { struct fio_stat fst; @@ -13225,15 +13226,15 @@ remote_target::fileio_fstat (int fd, struct stat *st, fileio_error *remote_errno return 0; } - return fileio_process_fstat_and_stat_reply (attachment, attachment_len, - ret, st); + return fileio_process_fstat_and_lstat_reply (attachment, attachment_len, + ret, st); } -/* Implementation of to_fileio_stat. */ +/* Implementation of to_fileio_lstat. */ int -remote_target::fileio_stat (struct inferior *inf, const char *filename, - struct stat *st, fileio_error *remote_errno) +remote_target::fileio_lstat (struct inferior *inf, const char *filename, + struct stat *st, fileio_error *remote_errno) { struct remote_state *rs = get_remote_state (); char *p = rs->buf.data (); @@ -13242,14 +13243,14 @@ remote_target::fileio_stat (struct inferior *inf, const char *filename, if (remote_hostio_set_filesystem (inf, remote_errno) != 0) return {}; - remote_buffer_add_string (&p, &left, "vFile:stat:"); + remote_buffer_add_string (&p, &left, "vFile:lstat:"); remote_buffer_add_bytes (&p, &left, (const gdb_byte *) filename, strlen (filename)); int attachment_len; const char *attachment; - int ret = remote_hostio_send_command (p - rs->buf.data (), PACKET_vFile_stat, + int ret = remote_hostio_send_command (p - rs->buf.data (), PACKET_vFile_lstat, remote_errno, &attachment, &attachment_len); @@ -13258,8 +13259,8 @@ remote_target::fileio_stat (struct inferior *inf, const char *filename, if (ret < 0) return ret; - return fileio_process_fstat_and_stat_reply (attachment, attachment_len, - ret, st); + return fileio_process_fstat_and_lstat_reply (attachment, attachment_len, + ret, st); } /* Implementation of to_filesystem_is_local. */ @@ -13763,7 +13764,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 +14028,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 +14044,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 +15570,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 +15809,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 +15844,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. */ @@ -16161,9 +16162,7 @@ test_packet_check_result () } /* namespace selftests */ #endif /* GDB_SELF_TEST */ -void _initialize_remote (); -void -_initialize_remote () +INIT_GDB_FILE (remote) { add_target (remote_target_info, remote_target::open); add_target (extended_remote_target_info, extended_remote_target::open); @@ -16422,6 +16421,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (PACKET_vFile_stat, "vFile:stat", "hostio-stat", 0); + add_packet_config_cmd (PACKET_vFile_lstat, "vFile:lstat", "hostio-lstat", 0); + add_packet_config_cmd (PACKET_vAttach, "vAttach", "attach", 0); add_packet_config_cmd (PACKET_vRun, "vRun", "run", 0); diff --git a/gdb/reverse.c b/gdb/reverse.c index 2c9ed72..dc35bca 100644 --- a/gdb/reverse.c +++ b/gdb/reverse.c @@ -277,9 +277,7 @@ info_bookmarks_command (const char *args, int from_tty) } } -void _initialize_reverse (); -void -_initialize_reverse () +INIT_GDB_FILE (reverse) { cmd_list_element *reverse_step_cmd = add_com ("reverse-step", class_run, reverse_step, _("\ diff --git a/gdb/riscv-fbsd-nat.c b/gdb/riscv-fbsd-nat.c index df7cc6a..2908d9f 100644 --- a/gdb/riscv-fbsd-nat.c +++ b/gdb/riscv-fbsd-nat.c @@ -65,9 +65,7 @@ riscv_fbsd_nat_target::store_registers (struct regcache *regcache, PT_SETFPREGS, &riscv_fbsd_fpregset); } -void _initialize_riscv_fbsd_nat (); -void -_initialize_riscv_fbsd_nat () +INIT_GDB_FILE (riscv_fbsd_nat) { add_inf_child_target (&the_riscv_fbsd_nat_target); } diff --git a/gdb/riscv-fbsd-tdep.c b/gdb/riscv-fbsd-tdep.c index fcb91ca..7bdc6e7 100644 --- a/gdb/riscv-fbsd-tdep.c +++ b/gdb/riscv-fbsd-tdep.c @@ -191,10 +191,9 @@ riscv_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_software_single_step (gdbarch, riscv_software_single_step); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - (riscv_isa_xlen (gdbarch) == 4 - ? svr4_ilp32_fetch_link_map_offsets - : svr4_lp64_fetch_link_map_offsets)); + set_solib_svr4_ops (gdbarch, (riscv_isa_xlen (gdbarch) == 4 + ? make_svr4_ilp32_solib_ops + : make_svr4_lp64_solib_ops)); tramp_frame_prepend_unwinder (gdbarch, &riscv_fbsd_sigframe); @@ -207,9 +206,7 @@ riscv_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) riscv_fbsd_get_thread_local_address); } -void _initialize_riscv_fbsd_tdep (); -void -_initialize_riscv_fbsd_tdep () +INIT_GDB_FILE (riscv_fbsd_tdep) { gdbarch_register_osabi (bfd_arch_riscv, 0, GDB_OSABI_FREEBSD, riscv_fbsd_init_abi); diff --git a/gdb/riscv-linux-nat.c b/gdb/riscv-linux-nat.c index 8846329..89f1ddc 100644 --- a/gdb/riscv-linux-nat.c +++ b/gdb/riscv-linux-nat.c @@ -329,9 +329,7 @@ riscv_linux_nat_target::store_registers (struct regcache *regcache, int regnum) /* Initialize RISC-V Linux native support. */ -void _initialize_riscv_linux_nat (); -void -_initialize_riscv_linux_nat () +INIT_GDB_FILE (riscv_linux_nat) { /* Register the target. */ linux_target = &the_riscv_linux_nat_target; diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c index f21039a..e5d77e7 100644 --- a/gdb/riscv-linux-tdep.c +++ b/gdb/riscv-linux-tdep.c @@ -20,6 +20,8 @@ #include "osabi.h" #include "glibc-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" +#include "svr4-tls-tdep.h" #include "solib-svr4.h" #include "regset.h" #include "tramp-frame.h" @@ -28,6 +30,7 @@ #include "record-full.h" #include "linux-record.h" #include "riscv-linux-tdep.h" +#include "inferior.h" extern unsigned int record_debug; @@ -426,6 +429,79 @@ riscv64_linux_record_tdep_init (struct gdbarch *gdbarch, riscv_linux_record_tdep.arg6 = RISCV_A5_REGNUM; } +/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID. + Throw a suitable TLS error if something goes wrong. */ + +static CORE_ADDR +riscv_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid, + svr4_tls_libc libc) +{ + /* On RISC-V, the thread pointer is found in TP. */ + regcache *regcache + = get_thread_arch_regcache (current_inferior (), ptid, gdbarch); + int thread_pointer_regnum = RISCV_TP_REGNUM; + target_fetch_registers (regcache, thread_pointer_regnum); + ULONGEST thr_ptr; + if (regcache->cooked_read (thread_pointer_regnum, &thr_ptr) != REG_VALID) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer")); + + CORE_ADDR dtv_ptr_addr; + switch (libc) + { + case svr4_tls_libc_musl: + /* MUSL: The DTV pointer is found at the very end of the pthread + struct which is located *before* the thread pointer. I.e. + the thread pointer will be just beyond the end of the struct, + so the address of the DTV pointer is found one pointer-size + before the thread pointer. */ + dtv_ptr_addr + = thr_ptr - (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + break; + case svr4_tls_libc_glibc: + /* GLIBC: The thread pointer (TP) points just beyond the end of + the TCB (thread control block). On RISC-V, this struct + (tcbhead_t) is defined to contain two pointers. The first is + a pointer to the DTV and the second is a pointer to private + data. So the DTV pointer address is 16 bytes (i.e. the size of + two pointers) before thread pointer. */ + + dtv_ptr_addr + = thr_ptr - 2 * (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + break; + default: + throw_error (TLS_GENERIC_ERROR, _("Unknown RISC-V C library")); + break; + } + + gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address")); + + const struct builtin_type *builtin = builtin_type (gdbarch); + CORE_ADDR dtv_addr = gdbarch_pointer_to_address + (gdbarch, builtin->builtin_data_ptr, buf.data ()); + return dtv_addr; +} + +/* For internal TLS lookup, return the DTP offset, which is the offset + to subtract from a DTV entry, in order to obtain the address of the + TLS block. */ + +static ULONGEST +riscv_linux_get_tls_dtp_offset (struct gdbarch *gdbarch, ptid_t ptid, + svr4_tls_libc libc) +{ + if (libc == svr4_tls_libc_musl) + { + /* This value is DTP_OFFSET in MUSL's arch/riscv64/pthread_arch.h. + It represents the value to subtract from the DTV entry, once + it has been loaded. */ + return 0x800; + } + else + return 0; +} + /* Initialize RISC-V Linux ABI info. */ static void @@ -437,10 +513,9 @@ riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_software_single_step (gdbarch, riscv_software_single_step); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - (riscv_isa_xlen (gdbarch) == 4 - ? linux_ilp32_fetch_link_map_offsets - : linux_lp64_fetch_link_map_offsets)); + set_solib_svr4_ops (gdbarch, (riscv_isa_xlen (gdbarch) == 4 + ? make_linux_ilp32_svr4_solib_ops + : make_linux_lp64_svr4_solib_ops)); /* GNU/Linux uses SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); @@ -451,6 +526,10 @@ riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); + set_gdbarch_get_thread_local_address (gdbarch, + svr4_tls_get_thread_local_address); + svr4_tls_register_tls_methods (info, gdbarch, riscv_linux_get_tls_dtv_addr, + riscv_linux_get_tls_dtp_offset); set_gdbarch_iterate_over_regset_sections (gdbarch, riscv_linux_iterate_over_regset_sections); @@ -465,9 +544,7 @@ riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Initialize RISC-V Linux target support. */ -void _initialize_riscv_linux_tdep (); -void -_initialize_riscv_linux_tdep () +INIT_GDB_FILE (riscv_linux_tdep) { gdbarch_register_osabi (bfd_arch_riscv, 0, GDB_OSABI_LINUX, riscv_linux_init_abi); diff --git a/gdb/riscv-none-tdep.c b/gdb/riscv-none-tdep.c index 7303f54..ad32057 100644 --- a/gdb/riscv-none-tdep.c +++ b/gdb/riscv-none-tdep.c @@ -163,9 +163,7 @@ riscv_none_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Initialize RISC-V bare-metal target support. */ -void _initialize_riscv_none_tdep (); -void -_initialize_riscv_none_tdep () +INIT_GDB_FILE (riscv_none_tdep) { gdbarch_register_osabi (bfd_arch_riscv, 0, GDB_OSABI_NONE, riscv_none_init_abi); diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c index a735c09..6d439b0 100644 --- a/gdb/riscv-tdep.c +++ b/gdb/riscv-tdep.c @@ -4778,9 +4778,7 @@ riscv_supply_regset (const struct regset *regset, } } -void _initialize_riscv_tdep (); -void -_initialize_riscv_tdep () +INIT_GDB_FILE (riscv_tdep) { riscv_init_reggroups (); @@ -4884,7 +4882,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; @@ -5158,7 +5156,7 @@ private: { return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival) || is_csrrwi_insn (ival) || is_csrrsi_insn (ival) - || is_csrrc_insn (ival)); + || is_csrrci_insn (ival)); } /* Returns true if instruction is classified. This function can set @@ -5270,8 +5268,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 +5378,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 +5413,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/rl78-tdep.c b/gdb/rl78-tdep.c index f325f41..5c49d1f 100644 --- a/gdb/rl78-tdep.c +++ b/gdb/rl78-tdep.c @@ -1488,9 +1488,7 @@ rl78_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Register the above initialization routine. */ -void _initialize_rl78_tdep (); -void -_initialize_rl78_tdep () +INIT_GDB_FILE (rl78_tdep) { gdbarch_register (bfd_arch_rl78, rl78_gdbarch_init); } diff --git a/gdb/rs6000-aix-nat.c b/gdb/rs6000-aix-nat.c index 225d1b8..213f197 100644 --- a/gdb/rs6000-aix-nat.c +++ b/gdb/rs6000-aix-nat.c @@ -1062,9 +1062,7 @@ rs6000_nat_target::xfer_shared_libraries } } -void _initialize_rs6000_nat (); -void -_initialize_rs6000_nat () +INIT_GDB_FILE (rs6000_nat) { add_inf_child_target (&the_rs6000_nat_target); } diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c index ab9feb3..853a66e 100644 --- a/gdb/rs6000-aix-tdep.c +++ b/gdb/rs6000-aix-tdep.c @@ -1411,13 +1411,11 @@ rs6000_aix_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_wchar_signed (gdbarch, 0); set_gdbarch_auto_wide_charset (gdbarch, rs6000_aix_auto_wide_charset); - set_gdbarch_so_ops (gdbarch, &solib_aix_so_ops); + set_gdbarch_make_solib_ops (gdbarch, make_aix_solib_ops); frame_unwind_append_unwinder (gdbarch, &aix_sighandle_frame_unwind); } -void _initialize_rs6000_aix_tdep (); -void -_initialize_rs6000_aix_tdep () +INIT_GDB_FILE (rs6000_aix_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_rs6000, bfd_target_xcoff_flavour, diff --git a/gdb/rs6000-lynx178-tdep.c b/gdb/rs6000-lynx178-tdep.c index 38af2ac..d0d9a53 100644 --- a/gdb/rs6000-lynx178-tdep.c +++ b/gdb/rs6000-lynx178-tdep.c @@ -407,9 +407,7 @@ rs6000_lynx178_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_long_double_bit (gdbarch, 8 * TARGET_CHAR_BIT); } -void _initialize_rs6000_lynx178_tdep (); -void -_initialize_rs6000_lynx178_tdep () +INIT_GDB_FILE (rs6000_lynx178_tdep) { gdbarch_register_osabi_sniffer (bfd_arch_rs6000, bfd_target_xcoff_flavour, diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index 23ccd92..1c75bb7 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -8722,9 +8722,7 @@ ppc_insn_prefix_dform (unsigned int insn1, unsigned int insn2) /* Initialization code. */ -void _initialize_rs6000_tdep (); -void -_initialize_rs6000_tdep () +INIT_GDB_FILE (rs6000_tdep) { gdbarch_register (bfd_arch_rs6000, rs6000_gdbarch_init, rs6000_dump_tdep); gdbarch_register (bfd_arch_powerpc, rs6000_gdbarch_init, rs6000_dump_tdep); diff --git a/gdb/run-on-main-thread.c b/gdb/run-on-main-thread.c index edaab08..30b0928 100644 --- a/gdb/run-on-main-thread.c +++ b/gdb/run-on-main-thread.c @@ -130,9 +130,7 @@ is_main_thread () #endif } -void _initialize_run_on_main_thread (); -void -_initialize_run_on_main_thread () +INIT_GDB_FILE (run_on_main_thread) { #if CXX_STD_THREAD /* The variable main_thread_id should be initialized when entering main, or diff --git a/gdb/rust-exp.h b/gdb/rust-exp.h index 73654f6..3185242 100644 --- a/gdb/rust-exp.h +++ b/gdb/rust-exp.h @@ -33,14 +33,6 @@ extern struct value *eval_op_rust_array (struct type *expect_type, enum exp_opcode opcode, struct value *ncopies, struct value *elt); -extern struct value *rust_subscript (struct type *expect_type, - struct expression *exp, - enum noside noside, bool for_addr, - struct value *lhs, struct value *rhs); -extern struct value *rust_range (struct type *expect_type, - struct expression *exp, - enum noside noside, enum range_flag kind, - struct value *low, struct value *high); namespace expr { @@ -75,22 +67,26 @@ public: struct expression *exp, enum noside noside) override { - value *arg1 = std::get<0> (m_storage)->evaluate (nullptr, exp, noside); - value *arg2 = std::get<1> (m_storage)->evaluate (nullptr, exp, noside); - return rust_subscript (expect_type, exp, noside, false, arg1, arg2); + return subscript (exp, noside, false); } value *slice (struct type *expect_type, struct expression *exp, enum noside noside) { - value *arg1 = std::get<0> (m_storage)->evaluate (nullptr, exp, noside); - value *arg2 = std::get<1> (m_storage)->evaluate (nullptr, exp, noside); - return rust_subscript (expect_type, exp, noside, true, arg1, arg2); + return subscript (exp, noside, true); } enum exp_opcode opcode () const override { return BINOP_SUBSCRIPT; } + +private: + + /* Helper function that does the work of evaluation. FOR_ADDR is + true if we're evaluating a slice. */ + value *subscript (struct expression *exp, enum noside noside, + bool for_addr); + }; class rust_unop_addr_operation @@ -126,17 +122,7 @@ public: value *evaluate (struct type *expect_type, struct expression *exp, - enum noside noside) override - { - auto kind = std::get<0> (m_storage); - value *low = nullptr; - if (std::get<1> (m_storage) != nullptr) - low = std::get<1> (m_storage)->evaluate (nullptr, exp, noside); - value *high = nullptr; - if (std::get<2> (m_storage) != nullptr) - high = std::get<2> (m_storage)->evaluate (nullptr, exp, noside); - return rust_range (expect_type, exp, noside, kind, low, high); - } + enum noside noside) override; enum exp_opcode opcode () const override { return OP_RANGE; } diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c index 1bb14db..3957413 100644 --- a/gdb/rust-lang.c +++ b/gdb/rust-lang.c @@ -1142,13 +1142,22 @@ rust_slice_type (const char *name, struct type *elt_type, -/* A helper for rust_evaluate_subexp that handles OP_RANGE. */ +namespace expr +{ struct value * -rust_range (struct type *expect_type, struct expression *exp, - enum noside noside, enum range_flag kind, - struct value *low, struct value *high) +rust_range_operation::evaluate (struct type *expect_type, + struct expression *exp, + enum noside noside) { + auto kind = std::get<0> (m_storage); + value *low = nullptr; + if (std::get<1> (m_storage) != nullptr) + low = std::get<1> (m_storage)->evaluate (nullptr, exp, noside); + value *high = nullptr; + if (std::get<2> (m_storage) != nullptr) + high = std::get<2> (m_storage)->evaluate (nullptr, exp, noside); + struct value *addrval, *result; CORE_ADDR addr; struct type *range_type; @@ -1225,6 +1234,8 @@ rust_range (struct type *expect_type, struct expression *exp, return result; } +} /* namespace expr */ + /* A helper function to compute the range and kind given a range value. TYPE is the type of the range value. RANGE is the range value. LOW, HIGH, and KIND are out parameters. The LOW and HIGH @@ -1266,13 +1277,16 @@ rust_compute_range (struct type *type, struct value *range, } } -/* A helper for rust_evaluate_subexp that handles BINOP_SUBSCRIPT. */ +namespace expr +{ struct value * -rust_subscript (struct type *expect_type, struct expression *exp, - enum noside noside, bool for_addr, - struct value *lhs, struct value *rhs) +rust_subscript_operation::subscript (struct expression *exp, + enum noside noside, bool for_addr) { + value *lhs = std::get<0> (m_storage)->evaluate (nullptr, exp, noside); + value *rhs = std::get<1> (m_storage)->evaluate (nullptr, exp, noside); + struct value *result; struct type *rhstype; LONGEST low, high_bound; @@ -1413,9 +1427,6 @@ rust_subscript (struct type *expect_type, struct expression *exp, return result; } -namespace expr -{ - struct value * rust_unop_ind_operation::evaluate (struct type *expect_type, struct expression *exp, diff --git a/gdb/rust-parse.c b/gdb/rust-parse.c index ef64005..b428b97 100644 --- a/gdb/rust-parse.c +++ b/gdb/rust-parse.c @@ -2421,9 +2421,7 @@ rust_lex_tests (void) -void _initialize_rust_exp (); -void -_initialize_rust_exp () +INIT_GDB_FILE (rust_exp) { int code = regcomp (&number_regex, number_regex_text, REG_EXTENDED); /* If the regular expression was incorrect, it was a programming diff --git a/gdb/rx-tdep.c b/gdb/rx-tdep.c index f767666..7b110ce 100644 --- a/gdb/rx-tdep.c +++ b/gdb/rx-tdep.c @@ -1063,9 +1063,7 @@ rx_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Register the above initialization routine. */ -void _initialize_rx_tdep (); -void -_initialize_rx_tdep () +INIT_GDB_FILE (rx_tdep) { gdbarch_register (bfd_arch_rx, rx_gdbarch_init); initialize_tdesc_rx (); diff --git a/gdb/s12z-tdep.c b/gdb/s12z-tdep.c index 70f8598..28d5635 100644 --- a/gdb/s12z-tdep.c +++ b/gdb/s12z-tdep.c @@ -662,9 +662,7 @@ s12z_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_s12z_tdep (); -void -_initialize_s12z_tdep () +INIT_GDB_FILE (s12z_tdep) { gdbarch_register (bfd_arch_s12z, s12z_gdbarch_init, NULL); } diff --git a/gdb/s390-linux-nat.c b/gdb/s390-linux-nat.c index d5964e2..5b723b1 100644 --- a/gdb/s390-linux-nat.c +++ b/gdb/s390-linux-nat.c @@ -1051,9 +1051,7 @@ s390_linux_nat_target::read_description () tdesc_s390_linux32); } -void _initialize_s390_nat (); -void -_initialize_s390_nat () +INIT_GDB_FILE (s390_nat) { /* Register the target. */ linux_target = &the_s390_linux_nat_target; diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c index 04c523b..66e571d 100644 --- a/gdb/s390-linux-tdep.c +++ b/gdb/s390-linux-tdep.c @@ -29,6 +29,8 @@ #include "gdbcore.h" #include "linux-record.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" +#include "svr4-tls-tdep.h" #include "objfiles.h" #include "osabi.h" #include "regcache.h" @@ -40,6 +42,7 @@ #include "target.h" #include "trad-frame.h" #include "xml-syscall.h" +#include "inferior.h" #include "features/s390-linux32v1.c" #include "features/s390-linux32v2.c" @@ -1124,6 +1127,45 @@ s390_init_linux_record_tdep (struct linux_record_tdep *record_tdep, record_tdep->ioctl_FIOQSIZE = 0x545e; } +/* Fetch and return the TLS DTV (dynamic thread vector) address for PTID. + Throw a suitable TLS error if something goes wrong. */ + +static CORE_ADDR +s390_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid, + enum svr4_tls_libc libc) +{ + /* On S390, the thread pointer is found in two registers A0 and A1 + (or, using gdb naming, acr0 and acr1) A0 contains the top 32 + bits of the address and A1 contains the bottom 32 bits. */ + regcache *regcache + = get_thread_arch_regcache (current_inferior (), ptid, gdbarch); + target_fetch_registers (regcache, S390_A0_REGNUM); + target_fetch_registers (regcache, S390_A1_REGNUM); + ULONGEST thr_ptr_lo, thr_ptr_hi, thr_ptr; + if (regcache->cooked_read (S390_A0_REGNUM, &thr_ptr_hi) != REG_VALID + || regcache->cooked_read (S390_A1_REGNUM, &thr_ptr_lo) != REG_VALID) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch thread pointer")); + thr_ptr = (thr_ptr_hi << 32) + thr_ptr_lo; + + /* The thread pointer points at the TCB (thread control block). The + first two members of this struct are both pointers, where the + first will be a pointer to the TCB (i.e. it points at itself) + and the second will be a pointer to the DTV (dynamic thread + vector). There are many other fields too, but the one we care + about here is the DTV pointer. Compute the address of the DTV + pointer, fetch it, and convert it to an address. */ + CORE_ADDR dtv_ptr_addr + = thr_ptr + gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT; + gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + if (target_read_memory (dtv_ptr_addr, buf.data (), buf.size ()) != 0) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch DTV address")); + + const struct builtin_type *builtin = builtin_type (gdbarch); + CORE_ADDR dtv_addr = gdbarch_pointer_to_address + (gdbarch, builtin->builtin_data_ptr, buf.data ()); + return dtv_addr; +} + /* Initialize OSABI common for GNU/Linux on 31- and 64-bit systems. */ static void @@ -1152,6 +1194,9 @@ s390_linux_init_abi_any (struct gdbarch_info info, struct gdbarch *gdbarch) /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, svr4_fetch_objfile_link_map); + set_gdbarch_get_thread_local_address (gdbarch, + svr4_tls_get_thread_local_address); + svr4_tls_register_tls_methods (info, gdbarch, s390_linux_get_tls_dtv_addr); /* Support reverse debugging. */ set_gdbarch_process_record_signal (gdbarch, s390_linux_record_signal); @@ -1170,8 +1215,7 @@ s390_linux_init_abi_31 (struct gdbarch_info info, struct gdbarch *gdbarch) s390_linux_init_abi_any (info, gdbarch); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_S390); } @@ -1186,14 +1230,11 @@ s390_linux_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch) s390_linux_init_abi_any (info, gdbarch); - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops); set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_S390X); } -void _initialize_s390_linux_tdep (); -void -_initialize_s390_linux_tdep () +INIT_GDB_FILE (s390_linux_tdep) { /* Hook us into the OSABI mechanism. */ gdbarch_register_osabi (bfd_arch_s390, bfd_mach_s390_31, GDB_OSABI_LINUX, diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c index a3b7658..8a2b405 100644 --- a/gdb/s390-tdep.c +++ b/gdb/s390-tdep.c @@ -7515,9 +7515,7 @@ disassemble_s390x () #endif /* GDB_SELF_TEST */ -void _initialize_s390_tdep (); -void -_initialize_s390_tdep () +INIT_GDB_FILE (s390_tdep) { /* Hook us into the gdbarch mechanism. */ gdbarch_register (bfd_arch_s390, s390_gdbarch_init); diff --git a/gdb/ser-go32.c b/gdb/ser-go32.c index c6b34ab..6e60569 100644 --- a/gdb/ser-go32.c +++ b/gdb/ser-go32.c @@ -914,9 +914,7 @@ info_serial_command (const char *arg, int from_tty) #endif } -void _initialize_ser_dos (); -void -_initialize_ser_dos () +INIT_GDB_FILE (ser_dos) { serial_add_interface (&dos_ops); diff --git a/gdb/ser-mingw.c b/gdb/ser-mingw.c index db733c3..086919a 100644 --- a/gdb/ser-mingw.c +++ b/gdb/ser-mingw.c @@ -1338,9 +1338,7 @@ static const struct serial_ops tcp_ops = net_windows_done_wait_handle }; -void _initialize_ser_windows (); -void -_initialize_ser_windows () +INIT_GDB_FILE (ser_windows) { WSADATA wsa_data; diff --git a/gdb/ser-pipe.c b/gdb/ser-pipe.c index 16f7419..bdf3602 100644 --- a/gdb/ser-pipe.c +++ b/gdb/ser-pipe.c @@ -236,9 +236,7 @@ static const struct serial_ops pipe_ops = ser_unix_write_prim }; -void _initialize_ser_pipe (); -void -_initialize_ser_pipe () +INIT_GDB_FILE (ser_pipe) { serial_add_interface (&pipe_ops); } diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c index 122b913..589b23d 100644 --- a/gdb/ser-tcp.c +++ b/gdb/ser-tcp.c @@ -465,9 +465,7 @@ static const struct serial_ops tcp_ops = #endif /* USE_WIN32API */ -void _initialize_ser_tcp (); -void -_initialize_ser_tcp () +INIT_GDB_FILE (ser_tcp) { #ifdef USE_WIN32API /* Do nothing; the TCP serial operations will be initialized in diff --git a/gdb/ser-uds.c b/gdb/ser-uds.c index 64e1599..2a45a90 100644 --- a/gdb/ser-uds.c +++ b/gdb/ser-uds.c @@ -110,9 +110,7 @@ static const struct serial_ops uds_ops = uds_write_prim }; -void _initialize_ser_socket (); -void -_initialize_ser_socket () +INIT_GDB_FILE (ser_socket) { serial_add_interface (&uds_ops); } diff --git a/gdb/ser-unix.c b/gdb/ser-unix.c index cb803a5..553220e 100644 --- a/gdb/ser-unix.c +++ b/gdb/ser-unix.c @@ -30,9 +30,33 @@ #include "gdbsupport/gdb_select.h" #include "cli/cli-cmds.h" #include "gdbsupport/filestuff.h" -#include <termios.h> + +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif + +#if HAVE_IOKIT_SERIAL_IOSS_H +# include <IOKit/serial/ioss.h> +#endif + +#if HAVE_ASM_TERMIOS_H +/* Workaround to resolve conflicting declarations of termios + in <asm/termbits.h> and <termios.h>. */ +# define termios asmtermios +# include <asm/termbits.h> +# undef termios +#endif + +#ifdef HAVE_TERMIOS_H +# include <termios.h> +#endif + #include "gdbsupport/scoped_ignore_sigttou.h" +#if defined(HAVE_SYS_IOCTL_H) && (defined(BOTHER) || defined(IOSSIOSPEED)) +# define HAVE_CUSTOM_BAUDRATE_SUPPORT 1 +#endif + struct hardwire_ttystate { struct termios termios; @@ -289,14 +313,32 @@ baudtab[] = 4800, B4800 } , +#ifdef B7200 + { + 7200, B7200 + } + , +#endif { 9600, B9600 } , +#ifdef B14400 + { + 14400, B14400 + } + , +#endif { 19200, B19200 } , +#ifdef B28800 + { + 28800, B28800 + } + , +#endif { 38400, B38400 } @@ -307,6 +349,12 @@ baudtab[] = } , #endif +#ifdef B76800 + { + 76800, B76800 + } + , +#endif #ifdef B115200 { 115200, B115200 @@ -412,6 +460,7 @@ rate_to_code (int rate) /* check if it is in between valid values. */ if (rate < baudtab[i].rate) { +#if !HAVE_CUSTOM_BAUDRATE_SUPPORT if (i) { error (_("Invalid baud rate %d. " @@ -423,29 +472,126 @@ rate_to_code (int rate) error (_("Invalid baud rate %d. Minimum value is %d."), rate, baudtab[0].rate); } +#else + return -1; +#endif } } } - + +#if !HAVE_CUSTOM_BAUDRATE_SUPPORT /* The requested speed was too large. */ error (_("Invalid baud rate %d. Maximum value is %d."), rate, baudtab[i - 1].rate); +#else + return -1; +#endif } +/* Set baud rate using B_code from termios.h. */ + static void -hardwire_setbaudrate (struct serial *scb, int rate) +set_baudcode_baudrate (struct serial *scb, int baud_code) { struct hardwire_ttystate state; - int baud_code = rate_to_code (rate); - + if (get_tty_state (scb, &state)) - perror_with_name ("could not get tty state"); + perror_with_name (_("could not get tty state")); cfsetospeed (&state.termios, baud_code); cfsetispeed (&state.termios, baud_code); if (set_tty_state (scb, &state)) - perror_with_name ("could not set tty state"); + perror_with_name (_("could not set tty state")); +} + +#if HAVE_CUSTOM_BAUDRATE_SUPPORT && defined(BOTHER) + +/* Set a custom baud rate using the termios BOTHER. */ + +static void +set_custom_baudrate_linux (int fd, int rate) +{ +#ifdef TCGETS2 + struct termios2 tio; + const unsigned long req_get = TCGETS2; + const unsigned long req_set = TCSETS2; +#else + struct termios tio; + const unsigned long req_get = TCGETS; + const unsigned long req_set = TCSETS; +#endif + + if (ioctl (fd, req_get, &tio) < 0) + perror_with_name (_("Can not get current baud rate")); + + /* Clear the current output baud rate and fill a new value. */ + tio.c_cflag &= ~CBAUD; + tio.c_cflag |= BOTHER; + tio.c_ospeed = rate; + + /* Clear the current input baud rate and fill a new value. */ + tio.c_cflag &= ~(CBAUD << IBSHIFT); + tio.c_cflag |= BOTHER << IBSHIFT; + tio.c_ispeed = rate; + + if (ioctl (fd, req_set, &tio) < 0) + perror_with_name (_("Can not set custom baud rate")); +} + +#elif HAVE_CUSTOM_BAUDRATE_SUPPORT && defined(IOSSIOSPEED) + +/* Set a custom baud rate using the IOSSIOSPEED ioctl call. */ + +static void +set_custom_baudrate_darwin (int fd, int rate) +{ + + if (ioctl (fd, IOSSIOSPEED, &rate) < 0) + perror_with_name (_("Can not set custom baud rate")); +} + +#endif /* HAVE_CUSTOM_BAUDRATE_SUPPORT + && (defined(BOTHER) || defined(IOSSIOSPEED)) */ + +#if HAVE_CUSTOM_BAUDRATE_SUPPORT + +/* Set a baud rate that differs from the OS B_codes. + This is possible if one of the following macros is available: + - BOTHER (Linux). + - IOSSIOSPEED (Darwin). */ + +static void +set_custom_baudrate (int fd, int rate) +{ +#if defined(BOTHER) + set_custom_baudrate_linux (fd, rate); +#elif defined(IOSSIOSPEED) + set_custom_baudrate_darwin (fd, rate); +#endif +} + +#endif /* HAVE_CUSTOM_BAUDRATE_SUPPORT */ + +/* Set the baud rate for the serial communication. */ + +static void +hardwire_setbaudrate (struct serial *scb, int rate) +{ + int baud_code = rate_to_code (rate); + + if (baud_code < 0) + { +#if HAVE_CUSTOM_BAUDRATE_SUPPORT + set_custom_baudrate (scb->fd, rate); +#else + /* An error should already have been thrown by rate_to_code(). + Add an additional error in case execution somehow reaches this line. */ + gdb_assert_not_reached ("Serial baud rate was not found in B_codes"); +#endif + } + else + set_baudcode_baudrate (scb, baud_code); } static int @@ -551,9 +697,7 @@ static const struct serial_ops hardwire_ops = ser_unix_write_prim }; -void _initialize_ser_hardwire (); -void -_initialize_ser_hardwire () +INIT_GDB_FILE (ser_hardwire) { serial_add_interface (&hardwire_ops); diff --git a/gdb/serial.c b/gdb/serial.c index bab8480..d66c637 100644 --- a/gdb/serial.c +++ b/gdb/serial.c @@ -642,9 +642,7 @@ set_parity (const char *ignore_args, int from_tty, struct cmd_list_element *c) serial_parity = GDBPARITY_NONE; } -void _initialize_serial (); -void -_initialize_serial () +INIT_GDB_FILE (serial) { #if 0 add_com ("connect", class_obscure, connect_command, _("\ diff --git a/gdb/sh-linux-tdep.c b/gdb/sh-linux-tdep.c index f0b35d3..2578192 100644 --- a/gdb/sh-linux-tdep.c +++ b/gdb/sh-linux-tdep.c @@ -28,6 +28,7 @@ #include "glibc-tdep.h" #include "sh-tdep.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "gdbarch.h" #define REGSx16(base) \ @@ -187,8 +188,7 @@ sh_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* GNU/Linux uses SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); set_gdbarch_fetch_tls_load_module_address (gdbarch, @@ -207,9 +207,7 @@ sh_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tramp_frame_prepend_unwinder (gdbarch, &sh_linux_rt_sigreturn_tramp_frame); } -void _initialize_sh_linux_tdep (); -void -_initialize_sh_linux_tdep () +INIT_GDB_FILE (sh_linux_tdep) { gdbarch_register_osabi (bfd_arch_sh, 0, GDB_OSABI_LINUX, sh_linux_init_abi); } diff --git a/gdb/sh-netbsd-nat.c b/gdb/sh-netbsd-nat.c index 25aafe1..d36dba5 100644 --- a/gdb/sh-netbsd-nat.c +++ b/gdb/sh-netbsd-nat.c @@ -98,9 +98,7 @@ sh_nbsd_nat_target::store_registers (struct regcache *regcache, int regno) } } -void _initialize_shnbsd_nat (); -void -_initialize_shnbsd_nat () +INIT_GDB_FILE (shnbsd_nat) { add_inf_child_target (&the_sh_nbsd_nat_target); } diff --git a/gdb/sh-netbsd-tdep.c b/gdb/sh-netbsd-tdep.c index f99566f..4b0181b 100644 --- a/gdb/sh-netbsd-tdep.c +++ b/gdb/sh-netbsd-tdep.c @@ -68,13 +68,10 @@ shnbsd_init_abi (struct gdbarch_info info, tdep->core_gregmap = (struct sh_corefile_regmap *)regmap; tdep->sizeof_gregset = 84; - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); } -void _initialize_shnbsd_tdep (); -void -_initialize_shnbsd_tdep () +INIT_GDB_FILE (shnbsd_tdep) { gdbarch_register_osabi (bfd_arch_sh, 0, GDB_OSABI_NETBSD, shnbsd_init_abi); diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c index 59a67bf..6bd4fd3 100644 --- a/gdb/sh-tdep.c +++ b/gdb/sh-tdep.c @@ -2394,9 +2394,7 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_sh_tdep (); -void -_initialize_sh_tdep () +INIT_GDB_FILE (sh_tdep) { gdbarch_register (bfd_arch_sh, sh_gdbarch_init, NULL); @@ -657,9 +657,7 @@ complete_skip_number (cmd_list_element *cmd, } } -void _initialize_step_skip (); -void -_initialize_step_skip () +INIT_GDB_FILE (step_skip) { static struct cmd_list_element *skiplist = NULL; struct cmd_list_element *c; diff --git a/gdb/sol-thread.c b/gdb/sol-thread.c index ae1e5c0..55e1d3e 100644 --- a/gdb/sol-thread.c +++ b/gdb/sol-thread.c @@ -1111,19 +1111,21 @@ info_solthreads (const char *args, int from_tty) ptid_t sol_thread_target::get_ada_task_ptid (long lwp, ULONGEST thread) { - struct thread_info *thread_info - = iterate_over_threads ([&] (struct thread_info *thread) + auto thread_db_find_thread_from_tid + = [&] (struct thread_info *iter) { - return thread->ptid.tid () == thread; - }); + return iter->ptid.tid () == thread; + }; + + struct thread_info *thread_info + = iterate_over_threads (thread_db_find_thread_from_tid); if (thread_info == NULL) { /* The list of threads is probably not up to date. Find any thread that is missing from the list, and try again. */ update_thread_list (); - thread_info = iterate_over_threads (thread_db_find_thread_from_tid, - &thread); + thread_info = iterate_over_threads (thread_db_find_thread_from_tid); } gdb_assert (thread_info != NULL); @@ -1131,9 +1133,7 @@ sol_thread_target::get_ada_task_ptid (long lwp, ULONGEST thread) return (thread_info->ptid); } -void _initialize_sol_thread (); -void -_initialize_sol_thread () +INIT_GDB_FILE (sol_thread) { void *dlhandle; diff --git a/gdb/solib-aix.c b/gdb/solib-aix.c index 0ad25f1..b246df8 100644 --- a/gdb/solib-aix.c +++ b/gdb/solib-aix.c @@ -17,7 +17,6 @@ #include "solib-aix.h" #include "solib.h" -#include "solist.h" #include "inferior.h" #include "gdb_bfd.h" #include "objfiles.h" @@ -25,6 +24,24 @@ #include "xcoffread.h" #include "observable.h" +/* solib_ops for AIX systems. */ + +struct aix_solib_ops : public solib_ops +{ + void relocate_section_addresses (solib &so, target_section *) const override; + void create_inferior_hook (int from_tty) const override; + owning_intrusive_list<solib> current_sos () const override; + gdb_bfd_ref_ptr bfd_open (const char *pathname) const override; +}; + +/* See solib-aix.h. */ + +solib_ops_up +make_aix_solib_ops () +{ + return std::make_unique<aix_solib_ops> (); +} + /* Our private data in struct solib. */ struct lm_info_aix final : public lm_info @@ -307,10 +324,9 @@ solib_aix_bss_data_overlap (bfd *abfd) return 0; } -/* Implement the "relocate_section_addresses" solib_ops method. */ - -static void -solib_aix_relocate_section_addresses (solib &so, target_section *sec) +void +aix_solib_ops::relocate_section_addresses (solib &so, + target_section *sec) const { struct bfd_section *bfd_sect = sec->the_bfd_section; bfd *abfd = bfd_sect->owner; @@ -411,10 +427,8 @@ solib_aix_get_section_offsets (struct objfile *objfile, return offsets; } -/* Implement the "solib_create_inferior_hook" solib_ops method. */ - -static void -solib_aix_solib_create_inferior_hook (int from_tty) +void +aix_solib_ops::create_inferior_hook (int from_tty) const { const char *warning_msg = "unable to relocate main executable"; @@ -442,10 +456,8 @@ solib_aix_solib_create_inferior_hook (int from_tty) } } -/* Implement the "current_sos" solib_ops method. */ - -static owning_intrusive_list<solib> -solib_aix_current_sos () +owning_intrusive_list<solib> +aix_solib_ops::current_sos () const { std::optional<std::vector<lm_info_aix>> &library_list = solib_aix_get_library_list (current_inferior (), NULL); @@ -481,35 +493,17 @@ solib_aix_current_sos () } /* Add it to the list. */ - auto &new_solib = sos.emplace_back (); - new_solib.so_original_name = so_name; - new_solib.so_name = so_name; + auto &new_solib = sos.emplace_back (*this); + new_solib.original_name = so_name; + new_solib.name = so_name; new_solib.lm_info = std::make_unique<lm_info_aix> (info); } return sos; } -/* Implement the "open_symbol_file_object" solib_ops method. */ - -static int -solib_aix_open_symbol_file_object (int from_tty) -{ - return 0; -} - -/* Implement the "in_dynsym_resolve_code" solib_ops method. */ - -static int -solib_aix_in_dynsym_resolve_code (CORE_ADDR pc) -{ - return 0; -} - -/* Implement the "bfd_open" solib_ops method. */ - -static gdb_bfd_ref_ptr -solib_aix_bfd_open (const char *pathname) +gdb_bfd_ref_ptr +aix_solib_ops::bfd_open (const char *pathname) const { /* The pathname is actually a synthetic filename with the following form: "/path/to/sharedlib(member.o)" (double-quotes excluded). @@ -523,7 +517,7 @@ solib_aix_bfd_open (const char *pathname) int found_file; if (pathname[path_len - 1] != ')') - return solib_bfd_open (pathname); + return solib_ops::bfd_open (pathname); /* Search for the associated parens. */ sep = strrchr (pathname, '('); @@ -533,7 +527,7 @@ solib_aix_bfd_open (const char *pathname) to open pathname without decoding, possibly leading to a failure), rather than triggering an assert failure). */ warning (_("missing '(' in shared object pathname: %s"), pathname); - return solib_bfd_open (pathname); + return solib_ops::bfd_open (pathname); } filename_len = sep - pathname; @@ -676,27 +670,7 @@ solib_aix_normal_stop_observer (struct bpstat *unused_1, int unused_2) data->library_list.reset (); } -/* The solib_ops for AIX targets. */ -const solib_ops solib_aix_so_ops = -{ - solib_aix_relocate_section_addresses, - nullptr, - nullptr, - solib_aix_solib_create_inferior_hook, - solib_aix_current_sos, - solib_aix_open_symbol_file_object, - solib_aix_in_dynsym_resolve_code, - solib_aix_bfd_open, - nullptr, - nullptr, - nullptr, - nullptr, - default_find_solib_addr, -}; - -void _initialize_solib_aix (); -void -_initialize_solib_aix () +INIT_GDB_FILE (solib_aix) { gdb::observers::normal_stop.attach (solib_aix_normal_stop_observer, "solib-aix"); diff --git a/gdb/solib-aix.h b/gdb/solib-aix.h index 7a1bc7b..628b7c8 100644 --- a/gdb/solib-aix.h +++ b/gdb/solib-aix.h @@ -18,9 +18,12 @@ #ifndef GDB_SOLIB_AIX_H #define GDB_SOLIB_AIX_H -struct solib_ops; -extern const solib_ops solib_aix_so_ops; +#include "solib.h" extern CORE_ADDR solib_aix_get_toc_value (CORE_ADDR pc); +/* Return a new solib_ops for AIX systems. */ + +solib_ops_up make_aix_solib_ops (); + #endif /* GDB_SOLIB_AIX_H */ diff --git a/gdb/solib-darwin.c b/gdb/solib-darwin.c index 368f401..aac8ab2 100644 --- a/gdb/solib-darwin.c +++ b/gdb/solib-darwin.c @@ -27,12 +27,31 @@ #include "regcache.h" #include "gdb_bfd.h" -#include "solist.h" +#include "solib.h" #include "solib-darwin.h" #include "mach-o.h" #include "mach-o/external.h" +/* solib_ops for Darwin systems. */ + +struct darwin_solib_ops : public solib_ops +{ + void relocate_section_addresses (solib &so, target_section *) const override; + void clear_solib (program_space *pspace) const override; + void create_inferior_hook (int from_tty) const override; + owning_intrusive_list<solib> current_sos () const override; + gdb_bfd_ref_ptr bfd_open (const char *pathname) const override; +}; + +/* See solib-darwin.h. */ + +solib_ops_up +make_darwin_solib_ops () +{ + return std::make_unique<darwin_solib_ops> (); +} + struct gdb_dyld_image_info { /* Base address (which corresponds to the Mach-O header). */ @@ -133,7 +152,7 @@ darwin_load_image_infos (struct darwin_info *info) (buf + 8 + ptr_type->length (), ptr_type); } -/* Link map info to include in an allocated so_list entry. */ +/* Link map info to include in an allocated solib entry. */ struct lm_info_darwin final : public lm_info { @@ -188,20 +207,8 @@ find_program_interpreter (void) return buf; } -/* Not used. I don't see how the main symbol file can be found: the - interpreter name is needed and it is known from the executable file. - Note that darwin-nat.c implements pid_to_exec_file. */ - -static int -open_symbol_file_object (int from_tty) -{ - return 0; -} - -/* Build a list of currently loaded shared objects. See solib-svr4.c. */ - -static owning_intrusive_list<solib> -darwin_current_sos () +owning_intrusive_list<solib> +darwin_solib_ops::current_sos () const { type *ptr_type = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; @@ -260,12 +267,12 @@ darwin_current_sos () break; /* Create and fill the new struct solib element. */ - auto &newobj = sos.emplace_back (); + auto &newobj = sos.emplace_back (*this); auto li = std::make_unique<lm_info_darwin> (); - newobj.so_name = file_path.get (); - newobj.so_original_name = newobj.so_name; + newobj.name = file_path.get (); + newobj.original_name = newobj.name; li->lm_addr = load_addr; newobj.lm_info = std::move (li); @@ -363,15 +370,6 @@ darwin_read_exec_load_addr_at_init (struct darwin_info *info) return darwin_validate_exec_header (load_addr); } -/* Return 1 if PC lies in the dynamic symbol resolution code of the - run time loader. */ - -static int -darwin_in_dynsym_resolve_code (CORE_ADDR pc) -{ - return 0; -} - /* A wrapper for bfd_mach_o_fat_extract that handles reference counting properly. This will either return NULL, or return a new reference to a BFD. */ @@ -476,10 +474,8 @@ darwin_solib_read_all_image_info_addr (struct darwin_info *info) info->all_image_addr = extract_unsigned_integer (buf, len, BFD_ENDIAN_BIG); } -/* Shared library startup support. See documentation in solib-svr4.c. */ - -static void -darwin_solib_create_inferior_hook (int from_tty) +void +darwin_solib_ops::create_inferior_hook (int from_tty) const { /* Everything below only makes sense if we have a running inferior. */ if (!target_has_execution ()) @@ -579,8 +575,8 @@ darwin_solib_create_inferior_hook (int from_tty) create_solib_event_breakpoint (current_inferior ()->arch (), notifier); } -static void -darwin_clear_solib (program_space *pspace) +void +darwin_solib_ops::clear_solib (program_space *pspace) const { darwin_info *info = get_darwin_info (pspace); @@ -591,8 +587,9 @@ darwin_clear_solib (program_space *pspace) /* The section table is built from bfd sections using bfd VMAs. Relocate these VMAs according to solib info. */ -static void -darwin_relocate_section_addresses (solib &so, target_section *sec) +void +darwin_solib_ops::relocate_section_addresses (solib &so, + target_section *sec) const { auto *li = gdb::checked_static_cast<lm_info_darwin *> (so.lm_info.get ()); @@ -611,9 +608,9 @@ darwin_relocate_section_addresses (solib &so, target_section *sec) if (sec->addr < so.addr_low) so.addr_low = sec->addr; } - -static gdb_bfd_ref_ptr -darwin_bfd_open (const char *pathname) + +gdb_bfd_ref_ptr +darwin_solib_ops::bfd_open (const char *pathname) const { int found_file; @@ -641,20 +638,3 @@ darwin_bfd_open (const char *pathname) return res; } - -const solib_ops darwin_so_ops = -{ - darwin_relocate_section_addresses, - nullptr, - darwin_clear_solib, - darwin_solib_create_inferior_hook, - darwin_current_sos, - open_symbol_file_object, - darwin_in_dynsym_resolve_code, - darwin_bfd_open, - nullptr, - nullptr, - nullptr, - nullptr, - default_find_solib_addr, -}; diff --git a/gdb/solib-darwin.h b/gdb/solib-darwin.h index b96e744..f5bcdd1 100644 --- a/gdb/solib-darwin.h +++ b/gdb/solib-darwin.h @@ -20,8 +20,10 @@ #ifndef GDB_SOLIB_DARWIN_H #define GDB_SOLIB_DARWIN_H -struct solib_ops; +#include "solib.h" -extern const solib_ops darwin_so_ops; +/* Return a new solib_ops for Darwin systems. */ + +extern solib_ops_up make_darwin_solib_ops (); #endif /* GDB_SOLIB_DARWIN_H */ diff --git a/gdb/solib-dsbt.c b/gdb/solib-dsbt.c index 3832a7a..f18d9a2 100644 --- a/gdb/solib-dsbt.c +++ b/gdb/solib-dsbt.c @@ -21,7 +21,6 @@ #include "inferior.h" #include "gdbcore.h" #include "solib.h" -#include "solist.h" #include "objfiles.h" #include "symtab.h" #include "command.h" @@ -121,7 +120,26 @@ struct dbst_ext_link_map ext_ptr l_next, l_prev; /* struct link_map *l_next, *l_prev; */ }; -/* Link map info to include in an allocated so_list entry */ +/* solib_ops for DSBT systems. */ + +struct dsbt_solib_ops : public solib_ops +{ + void relocate_section_addresses (solib &so, target_section *) const override; + void clear_solib (program_space *pspace) const override; + void create_inferior_hook (int from_tty) const override; + owning_intrusive_list<solib> current_sos () const override; + bool in_dynsym_resolve_code (CORE_ADDR pc) const override; +}; + +/* See solib-dsbt.h. */ + +solib_ops_up +make_dsbt_solib_ops () +{ + return std::make_unique<dsbt_solib_ops> (); +} + +/* Link map info to include in an allocated solib entry */ struct lm_info_dsbt final : public lm_info { @@ -393,15 +411,6 @@ fetch_loadmap (CORE_ADDR ldmaddr) static void dsbt_relocate_main_executable (void); static int enable_break (void); -/* See solist.h. */ - -static int -open_symbol_file_object (int from_tty) -{ - /* Unimplemented. */ - return 0; -} - /* Given a loadmap and an address, return the displacement needed to relocate the address. */ @@ -512,8 +521,8 @@ lm_base (void) themselves. The declaration of `struct solib' says which fields we provide values for. */ -static owning_intrusive_list<solib> -dsbt_current_sos (void) +owning_intrusive_list<solib> +dsbt_solib_ops::current_sos () const { bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ()); CORE_ADDR lm_addr; @@ -594,7 +603,7 @@ dsbt_current_sos (void) break; } - auto &sop = sos.emplace_back (); + auto &sop = sos.emplace_back (*this); auto li = std::make_unique<lm_info_dsbt> (); li->map = loadmap; /* Fetch the name. */ @@ -612,8 +621,8 @@ dsbt_current_sos (void) gdb_printf (gdb_stdlog, "current_sos: name = %s\n", name_buf.get ()); - sop.so_name = name_buf.get (); - sop.so_original_name = sop.so_name; + sop.name = name_buf.get (); + sop.original_name = sop.name; } sop.lm_info = std::move (li); @@ -630,11 +639,11 @@ dsbt_current_sos (void) return sos; } -/* Return 1 if PC lies in the dynamic symbol resolution code of the +/* Return true if PC lies in the dynamic symbol resolution code of the run time loader. */ -static int -dsbt_in_dynsym_resolve_code (CORE_ADDR pc) +bool +dsbt_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const { dsbt_info *info = get_dsbt_info (current_program_space); @@ -850,8 +859,8 @@ dsbt_relocate_main_executable (void) For the DSBT shared library, the main executable needs to be relocated. The shared library breakpoints also need to be enabled. */ -static void -dsbt_solib_create_inferior_hook (int from_tty) +void +dsbt_solib_ops::create_inferior_hook (int from_tty) const { /* Relocate main executable. */ dsbt_relocate_main_executable (); @@ -864,8 +873,8 @@ dsbt_solib_create_inferior_hook (int from_tty) } } -static void -dsbt_clear_solib (program_space *pspace) +void +dsbt_solib_ops::clear_solib (program_space *pspace) const { dsbt_info *info = get_dsbt_info (pspace); @@ -876,8 +885,9 @@ dsbt_clear_solib (program_space *pspace) info->main_executable_lm_info = NULL; } -static void -dsbt_relocate_section_addresses (solib &so, target_section *sec) +void +dsbt_solib_ops::relocate_section_addresses (solib &so, + target_section *sec) const { int seg; auto *li = gdb::checked_static_cast<lm_info_dsbt *> (so.lm_info.get ()); @@ -903,26 +913,7 @@ show_dsbt_debug (struct ui_file *file, int from_tty, gdb_printf (file, _("solib-dsbt debugging is %s.\n"), value); } -const solib_ops dsbt_so_ops = -{ - dsbt_relocate_section_addresses, - nullptr, - dsbt_clear_solib, - dsbt_solib_create_inferior_hook, - dsbt_current_sos, - open_symbol_file_object, - dsbt_in_dynsym_resolve_code, - solib_bfd_open, - nullptr, - nullptr, - nullptr, - nullptr, - default_find_solib_addr, -}; - -void _initialize_dsbt_solib (); -void -_initialize_dsbt_solib () +INIT_GDB_FILE (dsbt_solib) { /* Debug this file's internals. */ add_setshow_zuinteger_cmd ("solib-dsbt", class_maintenance, diff --git a/gdb/solib-dsbt.h b/gdb/solib-dsbt.h index d5c52c6..d44613c 100644 --- a/gdb/solib-dsbt.h +++ b/gdb/solib-dsbt.h @@ -20,8 +20,10 @@ #ifndef GDB_SOLIB_DSBT_H #define GDB_SOLIB_DSBT_H -struct solib_ops; +#include "solib.h" -extern const solib_ops dsbt_so_ops; +/* Return a new solib_ops for DSBT systems. */ + +solib_ops_up make_dsbt_solib_ops (); #endif /* GDB_SOLIB_DSBT_H */ diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c index bf13d36..6165d0b 100644 --- a/gdb/solib-frv.c +++ b/gdb/solib-frv.c @@ -20,13 +20,32 @@ #include "extract-store-integer.h" #include "gdbcore.h" #include "solib.h" -#include "solist.h" #include "frv-tdep.h" #include "objfiles.h" #include "symtab.h" #include "elf/frv.h" #include "gdb_bfd.h" #include "inferior.h" +#include "solib-frv.h" + +/* solib_ops for FR-V systems. */ + +struct frv_solib_ops : public solib_ops +{ + void relocate_section_addresses (solib &so, target_section *) const override; + void clear_solib (program_space *pspace) const override; + void create_inferior_hook (int from_tty) const override; + owning_intrusive_list<solib> current_sos () const override; + bool in_dynsym_resolve_code (CORE_ADDR pc) const override; +}; + +/* See solib-frv.h. */ + +solib_ops_up +make_frv_solib_ops () +{ + return std::make_unique<frv_solib_ops> (); +} /* FR-V pointers are four bytes wide. */ enum { FRV_PTR_SIZE = 4 }; @@ -194,7 +213,7 @@ struct ext_link_map ext_ptr l_next, l_prev; /* struct link_map *l_next, *l_prev; */ }; -/* Link map info to include in an allocated so_list entry. */ +/* Link map info to include in an allocated solib entry. */ struct lm_info_frv final : public lm_info { @@ -237,15 +256,6 @@ static void frv_relocate_main_executable (void); static CORE_ADDR main_got (void); static int enable_break2 (void); -/* Implement the "open_symbol_file_object" solib_ops method. */ - -static int -open_symbol_file_object (int from_tty) -{ - /* Unimplemented. */ - return 0; -} - /* Cached value for lm_base(), below. */ static CORE_ADDR lm_base_cache = 0; @@ -303,11 +313,8 @@ lm_base (void) return lm_base_cache; } - -/* Implement the "current_sos" solib_ops method. */ - -static owning_intrusive_list<solib> -frv_current_sos () +owning_intrusive_list<solib> +frv_solib_ops::current_sos () const { bfd_endian byte_order = gdbarch_byte_order (current_inferior ()->arch ()); CORE_ADDR lm_addr, mgot; @@ -377,7 +384,7 @@ frv_current_sos () break; } - auto &sop = sos.emplace_back (); + auto &sop = sos.emplace_back (*this); auto li = std::make_unique<lm_info_frv> (); li->map = loadmap; li->got_value = got_addr; @@ -397,8 +404,8 @@ frv_current_sos () warning (_("Can't read pathname for link map entry.")); else { - sop.so_name = name_buf.get (); - sop.so_original_name = sop.so_name; + sop.name = name_buf.get (); + sop.original_name = sop.name; } } else @@ -424,8 +431,8 @@ static CORE_ADDR interp_text_sect_high; static CORE_ADDR interp_plt_sect_low; static CORE_ADDR interp_plt_sect_high; -static int -frv_in_dynsym_resolve_code (CORE_ADDR pc) +bool +frv_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const { return ((pc >= interp_text_sect_low && pc < interp_text_sect_high) || (pc >= interp_plt_sect_low && pc < interp_plt_sect_high) @@ -786,8 +793,8 @@ frv_relocate_main_executable (void) to be relocated. The shared library breakpoints also need to be enabled. */ -static void -frv_solib_create_inferior_hook (int from_tty) +void +frv_solib_ops::create_inferior_hook (int from_tty) const { /* Relocate main executable. */ frv_relocate_main_executable (); @@ -800,8 +807,8 @@ frv_solib_create_inferior_hook (int from_tty) } } -static void -frv_clear_solib (program_space *pspace) +void +frv_solib_ops::clear_solib (program_space *pspace) const { lm_base_cache = 0; enable_break2_done = 0; @@ -811,8 +818,9 @@ frv_clear_solib (program_space *pspace) main_executable_lm_info = NULL; } -static void -frv_relocate_section_addresses (solib &so, target_section *sec) +void +frv_solib_ops::relocate_section_addresses (solib &so, + target_section *sec) const { int seg; auto *li = gdb::checked_static_cast<lm_info_frv *> (so.lm_info.get ()); @@ -1073,20 +1081,3 @@ frv_fetch_objfile_link_map (struct objfile *objfile) /* Not found! */ return 0; } - -const solib_ops frv_so_ops = -{ - frv_relocate_section_addresses, - nullptr, - frv_clear_solib, - frv_solib_create_inferior_hook, - frv_current_sos, - open_symbol_file_object, - frv_in_dynsym_resolve_code, - solib_bfd_open, - nullptr, - nullptr, - nullptr, - nullptr, - default_find_solib_addr, -}; diff --git a/gdb/solib-frv.h b/gdb/solib-frv.h new file mode 100644 index 0000000..710a424 --- /dev/null +++ b/gdb/solib-frv.h @@ -0,0 +1,28 @@ +/* Handle FR-V (FDPIC) shared libraries for GDB, the GNU Debugger. + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef GDB_SOLIB_FRV_H +#define GDB_SOLIB_FRV_H + +#include "solib.h" + +/* Return a new solib_ops for FR-V systems. */ + +solib_ops_up make_frv_solib_ops (); + +#endif /* GDB_SOLIB_FRV_H */ diff --git a/gdb/solib-rocm.c b/gdb/solib-rocm.c index 27b404c..2d26c3c 100644 --- a/gdb/solib-rocm.c +++ b/gdb/solib-rocm.c @@ -26,10 +26,10 @@ #include "event-top.h" #include "gdbsupport/fileio.h" #include "inferior.h" +#include "linux-tdep.h" #include "observable.h" #include "solib.h" #include "solib-svr4.h" -#include "solist.h" #include "symfile.h" #include <unordered_map> @@ -154,7 +154,69 @@ struct solib_info /* Per-inferior data key. */ static const registry<inferior>::key<solib_info> rocm_solib_data; -static solib_ops rocm_solib_ops; +/* solib_ops for ROCm systems. */ + +struct rocm_solib_ops : public solib_ops +{ + /* HOST_OPS is the host solib_ops that rocm_solib_ops hijacks / wraps, + in order to provide support for ROCm code objects. */ + explicit rocm_solib_ops (solib_ops_up host_ops) + : m_host_ops (std::move (host_ops)) + { + } + + /* The methods implemented by rocm_solib_ops. */ + owning_intrusive_list<solib> current_sos () const override; + void create_inferior_hook (int from_tty) const override; + gdb_bfd_ref_ptr bfd_open (const char *pathname) const override; + void relocate_section_addresses (solib &so, target_section *) const override; + void handle_event () const override; + + /* Implement the following methods just to forward the calls to the host + solib_ops. We currently need to implement all the methods that + svr4_solib_ops implements. */ + void clear_so (const solib &so) const override + { return m_host_ops->clear_so (so); } + + void clear_solib (program_space *pspace) const override + { return m_host_ops->clear_solib (pspace); } + + bool open_symbol_file_object (int from_tty) const override + { return m_host_ops->open_symbol_file_object (from_tty); } + + bool in_dynsym_resolve_code (CORE_ADDR pc) const override + { return m_host_ops->in_dynsym_resolve_code (pc); } + + bool same (const solib &gdb, const solib &inferior) const override + { return m_host_ops->same (gdb, inferior); } + + bool keep_data_in_core (CORE_ADDR vaddr, unsigned long size) const override + { return m_host_ops->keep_data_in_core (vaddr, size); } + + void update_breakpoints () const override + { return m_host_ops->update_breakpoints (); } + + std::optional<CORE_ADDR> find_solib_addr (solib &so) const override + { return m_host_ops->find_solib_addr (so); } + + bool supports_namespaces () const override + { return true; } + + int find_solib_ns (const solib &so) const override + { return m_host_ops->find_solib_ns (so); } + + int num_active_namespaces () const override + { return m_host_ops->num_active_namespaces (); } + + std::vector<const solib *> get_solibs_in_ns (int nsid) const override + { return m_host_ops->get_solibs_in_ns (nsid); } + +private: + owning_intrusive_list<solib> + solibs_from_rocm_sos (const std::vector<rocm_so> &sos) const; + + solib_ops_up m_host_ops; +}; /* Fetch the solib_info data for INF. */ @@ -171,13 +233,13 @@ get_solib_info (inferior *inf) /* Relocate section addresses. */ -static void -rocm_solib_relocate_section_addresses (solib &so, - struct target_section *sec) +void +rocm_solib_ops::relocate_section_addresses (solib &so, + struct target_section *sec) const { if (!is_amdgpu_arch (gdbarch_from_bfd (so.abfd.get ()))) { - svr4_so_ops.relocate_section_addresses (so, sec); + m_host_ops->relocate_section_addresses (so, sec); return; } @@ -188,34 +250,34 @@ rocm_solib_relocate_section_addresses (solib &so, static void rocm_update_solib_list (); -static void -rocm_solib_handle_event () +void +rocm_solib_ops::handle_event () const { - /* Since we sit on top of svr4_so_ops, we might get called following an event - concerning host libraries. We must therefore forward the call. If the - event was for a ROCm code object, it will be a no-op. On the other hand, + /* Since we sit on top of a host solib_ops, we might get called following an + event concerning host libraries. We must therefore forward the call. If + the event was for a ROCm code object, it will be a no-op. On the other hand if the event was for host libraries, rocm_update_solib_list will be essentially be a no-op (it will reload the same code object list as was previously loaded). */ - svr4_so_ops.handle_event (); + m_host_ops->handle_event (); rocm_update_solib_list (); } -/* Create so_list objects from rocm_so objects in SOS. */ +/* Create solib objects from rocm_so objects in SOS. */ -static owning_intrusive_list<solib> -so_list_from_rocm_sos (const std::vector<rocm_so> &sos) +owning_intrusive_list<solib> +rocm_solib_ops::solibs_from_rocm_sos (const std::vector<rocm_so> &sos) const { owning_intrusive_list<solib> dst; for (const rocm_so &so : sos) { - auto &newobj = dst.emplace_back (); + auto &newobj = dst.emplace_back (*this); newobj.lm_info = std::make_unique<lm_info_svr4> (*so.lm_info); - newobj.so_name = so.name; - newobj.so_original_name = so.unique_name; + newobj.name = so.name; + newobj.original_name = so.unique_name; } return dst; @@ -224,11 +286,11 @@ so_list_from_rocm_sos (const std::vector<rocm_so> &sos) /* Build a list of `struct solib' objects describing the shared objects currently loaded in the inferior. */ -static owning_intrusive_list<solib> -rocm_solib_current_sos () +owning_intrusive_list<solib> +rocm_solib_ops::current_sos () const { /* First, retrieve the host-side shared library list. */ - owning_intrusive_list<solib> sos = svr4_so_ops.current_sos (); + owning_intrusive_list<solib> sos = m_host_ops->current_sos (); /* Then, the device-side shared library list. */ std::vector<rocm_so> &dev_sos = get_solib_info (current_inferior ())->solib_list; @@ -236,13 +298,13 @@ rocm_solib_current_sos () if (dev_sos.empty ()) return sos; - owning_intrusive_list<solib> dev_so_list = so_list_from_rocm_sos (dev_sos); + owning_intrusive_list<solib> dev_solibs = solibs_from_rocm_sos (dev_sos); if (sos.empty ()) - return dev_so_list; + return dev_solibs; /* Append our libraries to the end of the list. */ - sos.splice (std::move (dev_so_list)); + sos.splice (std::move (dev_solibs)); return sos; } @@ -580,12 +642,12 @@ rocm_bfd_iovec_open (bfd *abfd, inferior *inferior) } } -static gdb_bfd_ref_ptr -rocm_solib_bfd_open (const char *pathname) +gdb_bfd_ref_ptr +rocm_solib_ops::bfd_open (const char *pathname) const { /* Handle regular files with SVR4 open. */ if (strstr (pathname, "://") == nullptr) - return svr4_so_ops.bfd_open (pathname); + return m_host_ops->bfd_open (pathname); auto open = [] (bfd *nbfd) -> gdb_bfd_iovec_base * { @@ -669,12 +731,12 @@ rocm_solib_bfd_open (const char *pathname) return abfd; } -static void -rocm_solib_create_inferior_hook (int from_tty) +void +rocm_solib_ops::create_inferior_hook (int from_tty) const { get_solib_info (current_inferior ())->solib_list.clear (); - svr4_so_ops.solib_create_inferior_hook (from_tty); + m_host_ops->create_inferior_hook (from_tty); } static void @@ -704,6 +766,9 @@ rocm_update_solib_list () return; } + gdb::unique_xmalloc_ptr<amd_dbgapi_code_object_id_t> code_object_list_holder + (code_object_list); + for (size_t i = 0; i < count; ++i) { CORE_ADDR l_addr; @@ -734,24 +799,6 @@ rocm_update_solib_list () sos.emplace_back (uri_bytes, std::move (unique_name), std::move (li)); } - - xfree (code_object_list); - - if (rocm_solib_ops.current_sos == NULL) - { - /* Override what we need to. */ - rocm_solib_ops = svr4_so_ops; - rocm_solib_ops.current_sos = rocm_solib_current_sos; - rocm_solib_ops.solib_create_inferior_hook - = rocm_solib_create_inferior_hook; - rocm_solib_ops.bfd_open = rocm_solib_bfd_open; - rocm_solib_ops.relocate_section_addresses - = rocm_solib_relocate_section_addresses; - rocm_solib_ops.handle_event = rocm_solib_handle_event; - - /* Engage the ROCm so_ops. */ - set_gdbarch_so_ops (current_inferior ()->arch (), &rocm_solib_ops); - } } static void @@ -759,6 +806,10 @@ rocm_solib_target_inferior_created (inferior *inf) { get_solib_info (inf)->solib_list.clear (); + auto prev_ops = inf->pspace->release_solib_ops (); + auto rocm_ops = std::make_unique<rocm_solib_ops> (std::move (prev_ops)); + inf->pspace->set_solib_ops (std::move (rocm_ops)); + rocm_update_solib_list (); /* Force GDB to reload the solibs. */ @@ -766,11 +817,22 @@ rocm_solib_target_inferior_created (inferior *inf) solib_add (nullptr, 0, auto_solib_add); } -/* -Wmissing-prototypes */ -extern initialize_file_ftype _initialize_rocm_solib; +static void +rocm_solib_target_inferior_execd (inferior *exec_inf, inferior *follow_inf) +{ + /* Engage the ROCm so_ops, but only if dbgapi is attached to the inferior + (avoiding remote inferiors and core file debugging). */ + if (get_amd_dbgapi_process_id (follow_inf) == AMD_DBGAPI_PROCESS_NONE) + return; + + auto prev_ops = follow_inf->pspace->release_solib_ops (); + auto rocm_ops = std::make_unique<rocm_solib_ops> (std::move (prev_ops)); + follow_inf->pspace->set_solib_ops (std::move (rocm_ops)); -void -_initialize_rocm_solib () + get_solib_info (exec_inf)->solib_list.clear (); +} + +INIT_GDB_FILE (rocm_solib) { /* The dependency on the amd-dbgapi exists because solib-rocm's inferior_created observer needs amd-dbgapi to have attached the process, @@ -779,4 +841,8 @@ _initialize_rocm_solib () (rocm_solib_target_inferior_created, "solib-rocm", { &get_amd_dbgapi_target_inferior_created_observer_token () }); + + gdb::observers::inferior_execd.attach + (rocm_solib_target_inferior_execd, "solib-rocm", + { &get_amd_dbgapi_target_inferior_execd_observer_token () }); } diff --git a/gdb/solib-svr4-linux.c b/gdb/solib-svr4-linux.c new file mode 100644 index 0000000..fd86cf0 --- /dev/null +++ b/gdb/solib-svr4-linux.c @@ -0,0 +1,98 @@ +/* Target-dependent code for GNU/Linux using SVR4-style libraries. + + 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/>. */ + +#include "solib-svr4-linux.h" + +/* See solib-svr4-linux.h. */ + +solib_ops_up +make_linux_ilp32_svr4_solib_ops () +{ + return std::make_unique<linux_ilp32_svr4_solib_ops> (); +} + +/* See solib-svr4-linux.h. */ + +link_map_offsets * +linux_ilp32_svr4_solib_ops::fetch_link_map_offsets () const +{ + static link_map_offsets lmo; + static link_map_offsets *lmp = nullptr; + + if (lmp == nullptr) + { + lmp = &lmo; + + lmo.r_version_offset = 0; + lmo.r_version_size = 4; + lmo.r_map_offset = 4; + lmo.r_brk_offset = 8; + lmo.r_ldsomap_offset = -1; + lmo.r_next_offset = 20; + + /* Everything we need is in the first 20 bytes. */ + lmo.link_map_size = 20; + lmo.l_addr_offset = 0; + lmo.l_name_offset = 4; + lmo.l_ld_offset = 8; + lmo.l_next_offset = 12; + lmo.l_prev_offset = 16; + } + + return lmp; +} + +/* See solib-svr4-linux.h. */ + +solib_ops_up +make_linux_lp64_svr4_solib_ops () +{ + return std::make_unique<linux_lp64_svr4_solib_ops> (); +} + +/* See linux-tdep.h. */ + +link_map_offsets * +linux_lp64_svr4_solib_ops::fetch_link_map_offsets () const +{ + static link_map_offsets lmo; + static link_map_offsets *lmp = nullptr; + + if (lmp == nullptr) + { + lmp = &lmo; + + lmo.r_version_offset = 0; + lmo.r_version_size = 4; + lmo.r_map_offset = 8; + lmo.r_brk_offset = 16; + lmo.r_ldsomap_offset = -1; + lmo.r_next_offset = 40; + + /* Everything we need is in the first 40 bytes. */ + lmo.link_map_size = 40; + lmo.l_addr_offset = 0; + lmo.l_name_offset = 8; + lmo.l_ld_offset = 16; + lmo.l_next_offset = 24; + lmo.l_prev_offset = 32; + } + + return lmp; +} diff --git a/gdb/solib-svr4-linux.h b/gdb/solib-svr4-linux.h new file mode 100644 index 0000000..623013c --- /dev/null +++ b/gdb/solib-svr4-linux.h @@ -0,0 +1,47 @@ +/* Target-dependent code for GNU/Linux using SVR4-style libraries. + + 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/>. */ + +#ifndef GDB_SOLIB_SVR4_LINUX_H +#define GDB_SOLIB_SVR4_LINUX_H + +#include "solib-svr4.h" + +/* solib_ops for ILP32 Linux systems. */ + +struct linux_ilp32_svr4_solib_ops : public svr4_solib_ops +{ + link_map_offsets *fetch_link_map_offsets () const override; +}; + +/* solib_ops for LP64 Linux systems. */ + +struct linux_lp64_svr4_solib_ops : public svr4_solib_ops +{ + link_map_offsets *fetch_link_map_offsets () const override; +}; + +/* Return a new solib_ops for ILP32 Linux systems. */ + +extern solib_ops_up make_linux_ilp32_svr4_solib_ops (); + +/* Return a new solib_ops for LP64 Linux systems. */ + +extern solib_ops_up make_linux_lp64_svr4_solib_ops (); + +#endif /* GDB_SOLIB_SVR4_LINUX_H */ diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 2a2745d..9b4cabf 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -35,7 +35,6 @@ #include "regcache.h" #include "observable.h" -#include "solist.h" #include "solib.h" #include "solib-svr4.h" @@ -48,8 +47,6 @@ #include <map> -static struct link_map_offsets *svr4_fetch_link_map_offsets (void); -static int svr4_have_link_map_offsets (void); static void svr4_relocate_main_executable (void); static void probes_table_remove_objfile_probes (struct objfile *objfile); static void svr4_iterate_over_objfiles_in_search_order @@ -91,27 +88,6 @@ static const char * const main_name_list[] = NULL }; -/* What to do when a probe stop occurs. */ - -enum probe_action -{ - /* Something went seriously wrong. Stop using probes and - revert to using the older interface. */ - PROBES_INTERFACE_FAILED, - - /* No action is required. The shared object list is still - valid. */ - DO_NOTHING, - - /* The shared object list should be reloaded entirely. */ - FULL_RELOAD, - - /* Attempt to incrementally update the shared object list. If - the update fails or is not possible, fall back to reloading - the list in full. */ - UPDATE_OR_RELOAD, -}; - /* A probe's name and its associated action. */ struct probe_info @@ -186,22 +162,22 @@ svr4_same (const char *gdb_name, const char *inferior_name, return gdb_lm_info.l_addr_inferior == inferior_lm_info.l_addr_inferior; } -static int -svr4_same (const solib &gdb, const solib &inferior) +bool +svr4_solib_ops::same (const solib &gdb, const solib &inferior) const { auto *lmg = gdb::checked_static_cast<const lm_info_svr4 *> (gdb.lm_info.get ()); auto *lmi = gdb::checked_static_cast<const lm_info_svr4 *> (inferior.lm_info.get ()); - return svr4_same (gdb.so_original_name.c_str (), - inferior.so_original_name.c_str (), *lmg, *lmi); + return svr4_same (gdb.original_name.c_str (), + inferior.original_name.c_str (), *lmg, *lmi); } -static lm_info_svr4_up -lm_info_read (CORE_ADDR lm_addr) +lm_info_svr4_up +svr4_solib_ops::read_lm_info (CORE_ADDR lm_addr) const { - struct link_map_offsets *lmo = svr4_fetch_link_map_offsets (); + link_map_offsets *lmo = this->fetch_link_map_offsets (); lm_info_svr4_up lm_info; gdb::byte_vector lm (lmo->link_map_size); @@ -231,16 +207,16 @@ lm_info_read (CORE_ADDR lm_addr) return lm_info; } -static int -has_lm_dynamic_from_link_map (void) +int +svr4_solib_ops::has_lm_dynamic_from_link_map () const { - struct link_map_offsets *lmo = svr4_fetch_link_map_offsets (); + link_map_offsets *lmo = this->fetch_link_map_offsets (); return lmo->l_ld_offset >= 0; } -static CORE_ADDR -lm_addr_check (const solib &so, bfd *abfd) +CORE_ADDR +svr4_solib_ops::lm_addr_check (const solib &so, bfd *abfd) const { auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ()); @@ -251,7 +227,7 @@ lm_addr_check (const solib &so, bfd *abfd) l_addr = li->l_addr_inferior; - if (! abfd || ! has_lm_dynamic_from_link_map ()) + if (!abfd || !this->has_lm_dynamic_from_link_map ()) goto set_addr; l_dynaddr = li->l_ld; @@ -317,7 +293,7 @@ lm_addr_check (const solib &so, bfd *abfd) gdb_printf (_("Using PIC (Position Independent Code) " "prelink displacement %s for \"%s\".\n"), paddress (current_inferior ()->arch (), l_addr), - so.so_name.c_str ()); + so.name.c_str ()); } else { @@ -333,7 +309,7 @@ lm_addr_check (const solib &so, bfd *abfd) warning (_(".dynamic section for \"%s\" " "is not at the expected address " "(wrong library or version mismatch?)"), - so.so_name.c_str ()); + so.name.c_str ()); } } @@ -369,7 +345,7 @@ struct svr4_info CORE_ADDR debug_loader_offset = 0; /* Name of the dynamic linker, valid if debug_loader_offset_p. */ - char *debug_loader_name = nullptr; + std::string debug_loader_name; /* Load map address for the main executable in default namespace. */ CORE_ADDR main_lm_addr = 0; @@ -431,6 +407,14 @@ struct svr4_info /* This identifies which namespaces are active. A namespace is considered active when there is at least one shared object loaded into it. */ std::set<size_t> active_namespaces; + + /* This flag indicates whether initializations related to the + GLIBC TLS module id tracking code have been performed. */ + bool glibc_tls_slots_inited = false; + + /* A vector of link map addresses for GLIBC TLS slots. See comment + for tls_maybe_fill_slot for more information. */ + std::vector<CORE_ADDR> glibc_tls_slots; }; /* Per-program-space data key. */ @@ -469,8 +453,8 @@ svr4_is_default_namespace (const svr4_info *info, CORE_ADDR debug_base) /* Free the probes table. */ -static void -free_probes_table (struct svr4_info *info) +void +svr4_solib_ops::free_probes_table (svr4_info *info) const { info->probes_table.reset (nullptr); } @@ -635,10 +619,10 @@ read_program_header (int type, int *p_arch_size, CORE_ADDR *base_addr) return buf; } +/* See solib-svr4.h. */ -/* Return program interpreter string. */ -static std::optional<gdb::byte_vector> -find_program_interpreter (void) +std::optional<gdb::byte_vector> +svr4_find_program_interpreter () { /* If we have a current exec_bfd, use its section table. */ if (current_program_space->exec_bfd () @@ -746,9 +730,6 @@ elf_locate_base (void) { CORE_ADDR dyn_ptr, dyn_ptr_addr; - if (!svr4_have_link_map_offsets ()) - return 0; - /* Look for DT_MIPS_RLD_MAP first. MIPS executables use this instead of DT_DEBUG, although they sometimes contain an unused DT_DEBUG. */ @@ -817,10 +798,10 @@ elf_locate_base (void) checking r_version for a known version number, or r_state for RT_CONSISTENT. */ -static CORE_ADDR -solib_svr4_r_map (CORE_ADDR debug_base) +CORE_ADDR +svr4_solib_ops::read_r_map (CORE_ADDR debug_base) const { - struct link_map_offsets *lmo = svr4_fetch_link_map_offsets (); + link_map_offsets *lmo = this->fetch_link_map_offsets (); type *ptr_type = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; CORE_ADDR addr = 0; @@ -840,10 +821,10 @@ solib_svr4_r_map (CORE_ADDR debug_base) /* Find r_brk from the inferior's debug base. */ -static CORE_ADDR -solib_svr4_r_brk (struct svr4_info *info) +CORE_ADDR +svr4_solib_ops::find_r_brk (svr4_info *info) const { - struct link_map_offsets *lmo = svr4_fetch_link_map_offsets (); + link_map_offsets *lmo = this->fetch_link_map_offsets (); type *ptr_type = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; @@ -854,10 +835,10 @@ solib_svr4_r_brk (struct svr4_info *info) /* Find the link map for the dynamic linker (if it is not in the normal list of loaded shared objects). */ -static CORE_ADDR -solib_svr4_r_ldsomap (struct svr4_info *info) +CORE_ADDR +svr4_solib_ops::find_r_ldsomap (svr4_info *info) const { - struct link_map_offsets *lmo = svr4_fetch_link_map_offsets (); + link_map_offsets *lmo = this->fetch_link_map_offsets (); type *ptr_type = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; enum bfd_endian byte_order = type_byte_order (ptr_type); @@ -885,10 +866,10 @@ solib_svr4_r_ldsomap (struct svr4_info *info) /* Find the next namespace from the r_next field. */ -static CORE_ADDR -solib_svr4_r_next (CORE_ADDR debug_base) +CORE_ADDR +svr4_solib_ops::read_r_next (CORE_ADDR debug_base) const { - link_map_offsets *lmo = svr4_fetch_link_map_offsets (); + link_map_offsets *lmo = this->fetch_link_map_offsets (); type *ptr_type = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; bfd_endian byte_order = type_byte_order (ptr_type); @@ -920,8 +901,8 @@ solib_svr4_r_next (CORE_ADDR debug_base) memory areas containing the l_name string are saved in the core file. */ -static int -svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size) +bool +svr4_solib_ops::keep_data_in_core (CORE_ADDR vaddr, unsigned long size) const { struct svr4_info *info; CORE_ADDR ldsomap; @@ -931,25 +912,25 @@ svr4_keep_data_in_core (CORE_ADDR vaddr, unsigned long size) info->debug_base = elf_locate_base (); if (info->debug_base == 0) - return 0; + return false; - ldsomap = solib_svr4_r_ldsomap (info); + ldsomap = this->find_r_ldsomap (info); if (!ldsomap) - return 0; + return false; - std::unique_ptr<lm_info_svr4> li = lm_info_read (ldsomap); + std::unique_ptr<lm_info_svr4> li = this->read_lm_info (ldsomap); name_lm = li != NULL ? li->l_name : 0; return (name_lm >= vaddr && name_lm < vaddr + size); } -/* See solist.h. */ +/* See solib.h. */ -static int -open_symbol_file_object (int from_tty) +bool +svr4_solib_ops::open_symbol_file_object (int from_tty) const { CORE_ADDR lm, l_name; - struct link_map_offsets *lmo = svr4_fetch_link_map_offsets (); + link_map_offsets *lmo = this->fetch_link_map_offsets (); type *ptr_type = builtin_type (current_inferior ()->arch ())->builtin_data_ptr; int l_name_size = ptr_type->length (); @@ -962,17 +943,17 @@ open_symbol_file_object (int from_tty) if (current_program_space->symfile_object_file) if (!query (_("Attempt to reload symbols from process? "))) - return 0; + return false; /* Always locate the debug struct, in case it has moved. */ info->debug_base = elf_locate_base (); if (info->debug_base == 0) - return 0; /* failed somehow... */ + return false; /* failed somehow... */ /* First link map member should be the executable. */ - lm = solib_svr4_r_map (info->debug_base); + lm = this->read_r_map (info->debug_base); if (lm == 0) - return 0; /* failed somehow... */ + return false; /* failed somehow... */ /* Read address of name from target memory to GDB. */ read_memory (lm + lmo->l_name_offset, l_name_buf.data (), l_name_size); @@ -981,7 +962,7 @@ open_symbol_file_object (int from_tty) l_name = extract_typed_address (l_name_buf.data (), ptr_type); if (l_name == 0) - return 0; /* No filename. */ + return false; /* No filename. */ /* Now fetch the filename from target memory. */ gdb::unique_xmalloc_ptr<char> filename @@ -990,13 +971,13 @@ open_symbol_file_object (int from_tty) if (filename == nullptr) { warning (_("failed to read exec filename from attached file")); - return 0; + return false; } /* Have a pathname: read the symbol file. */ symbol_file_add_main (filename.get (), add_flags); - return 1; + return true; } /* Data exchange structure for the XML parser as returned by @@ -1029,8 +1010,8 @@ svr4_free_objfile_observer (struct objfile *objfile) /* Implement solib_ops.clear_so. */ -static void -svr4_clear_so (const solib &so) +void +svr4_solib_ops::clear_so (const solib &so) const { auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ()); @@ -1038,19 +1019,19 @@ svr4_clear_so (const solib &so) li->l_addr_p = 0; } -/* Create the so_list objects equivalent to the svr4_sos in SOS. */ +/* Create the solib objects equivalent to the svr4_sos in SOS. */ -static owning_intrusive_list<solib> -so_list_from_svr4_sos (const std::vector<svr4_so> &sos) +owning_intrusive_list<solib> +svr4_solib_ops::solibs_from_svr4_sos (const std::vector<svr4_so> &sos) const { owning_intrusive_list<solib> dst; for (const svr4_so &so : sos) { - auto &newobj = dst.emplace_back (); + auto &newobj = dst.emplace_back (*this); - newobj.so_name = so.name; - newobj.so_original_name = so.name; + newobj.name = so.name; + newobj.original_name = so.name; newobj.lm_info = std::make_unique<lm_info_svr4> (*so.lm_info); } @@ -1168,11 +1149,10 @@ static const struct gdb_xml_element svr4_library_list_elements[] = { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } }; -/* Parse qXfer:libraries:read packet into *SO_LIST_RETURN. Return 1 if +/* Parse qXfer:libraries:read packet into *LIST. - Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such - case. Return 1 if *SO_LIST_RETURN contains the library list, it may be - empty, caller is responsible for freeing all its entries. */ + Return 0 if packet not supported, *LIST is not modified in such case. + Return 1 if *LIST contains the library list. */ static int svr4_parse_libraries (const char *document, struct svr4_library_list *list) @@ -1194,11 +1174,11 @@ svr4_parse_libraries (const char *document, struct svr4_library_list *list) return 0; } -/* Attempt to get so_list from target via qXfer:libraries-svr4:read packet. +/* Attempt to get the shared object list from target via + qXfer:libraries-svr4:read packet. - Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such - case. Return 1 if *SO_LIST_RETURN contains the library list, it may be - empty, caller is responsible for freeing all its entries. + Return 0 if packet not supported, *LIST is not modified in such case. + Return 1 if *LIST contains the library list. Note that ANNEX must be NULL if the remote does not explicitly allow qXfer:libraries-svr4:read packets with non-empty annexes. Support for @@ -1235,8 +1215,8 @@ svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list, /* If no shared library information is available from the dynamic linker, build a fallback list from other sources. */ -static owning_intrusive_list<solib> -svr4_default_sos (svr4_info *info) +owning_intrusive_list<solib> +svr4_solib_ops::default_sos (svr4_info *info) const { if (!info->debug_loader_offset_p) return {}; @@ -1248,11 +1228,11 @@ svr4_default_sos (svr4_info *info) li->l_addr_p = 1; owning_intrusive_list<solib> sos; - auto &newobj = sos.emplace_back (); + auto &newobj = sos.emplace_back (*this); newobj.lm_info = std::move (li); - newobj.so_name = info->debug_loader_name; - newobj.so_original_name = newobj.so_name; + newobj.name = info->debug_loader_name; + newobj.original_name = newobj.name; return sos; } @@ -1264,16 +1244,16 @@ svr4_default_sos (svr4_info *info) is returned the entries stored to LINK_PTR_PTR are still valid although they may represent only part of the inferior library list. */ -static int -svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm, - std::vector<svr4_so> &sos, int ignore_first) +int +svr4_solib_ops::read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm, + std::vector<svr4_so> &sos, int ignore_first) const { CORE_ADDR first_l_name = 0; CORE_ADDR next_lm; for (; lm != 0; prev_lm = lm, lm = next_lm) { - lm_info_svr4_up li = lm_info_read (lm); + lm_info_svr4_up li = this->read_lm_info (lm); if (li == NULL) return 0; @@ -1329,8 +1309,8 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm, stored by the probes interface. Handle special cases relating to the first elements of the list in default namespace. */ -static void -svr4_current_sos_direct (struct svr4_info *info) +void +svr4_solib_ops::current_sos_direct (svr4_info *info) const { CORE_ADDR lm; bool ignore_first; @@ -1396,15 +1376,15 @@ svr4_current_sos_direct (struct svr4_info *info) /* Collect the sos in each namespace. */ CORE_ADDR debug_base = info->debug_base; for (; debug_base != 0; - ignore_first = false, debug_base = solib_svr4_r_next (debug_base)) + ignore_first = false, debug_base = this->read_r_next (debug_base)) { /* Walk the inferior's link map list, and build our so_list list. */ - lm = solib_svr4_r_map (debug_base); + lm = this->read_r_map (debug_base); if (lm != 0) { svr4_maybe_add_namespace (info, debug_base); - svr4_read_so_list (info, lm, 0, info->solib_lists[debug_base], - ignore_first); + this->read_so_list (info, lm, 0, info->solib_lists[debug_base], + ignore_first); } } @@ -1417,15 +1397,15 @@ svr4_current_sos_direct (struct svr4_info *info) r_debug object. If we added it to the default namespace (as it was), we would probably run into inconsistencies with the load map's prev/next links (I wonder if we did). */ - debug_base = solib_svr4_r_ldsomap (info); + debug_base = this->find_r_ldsomap (info); if (debug_base != 0) { /* Add the dynamic linker's namespace unless we already did. */ if (info->solib_lists.find (debug_base) == info->solib_lists.end ()) { svr4_maybe_add_namespace (info, debug_base); - svr4_read_so_list (info, debug_base, 0, info->solib_lists[debug_base], - 0); + this->read_so_list (info, debug_base, 0, + info->solib_lists[debug_base], 0); } } @@ -1434,15 +1414,15 @@ svr4_current_sos_direct (struct svr4_info *info) /* Collect sos read and stored by the probes interface. */ -static owning_intrusive_list<solib> -svr4_collect_probes_sos (svr4_info *info) +owning_intrusive_list<solib> +svr4_solib_ops::collect_probes_sos (svr4_info *info) const { owning_intrusive_list<solib> res; for (const auto &tuple : info->solib_lists) { const std::vector<svr4_so> &sos = tuple.second; - res.splice (so_list_from_svr4_sos (sos)); + res.splice (this->solibs_from_svr4_sos (sos)); } return res; @@ -1451,26 +1431,26 @@ svr4_collect_probes_sos (svr4_info *info) /* Implement the main part of the "current_sos" solib_ops method. */ -static owning_intrusive_list<solib> -svr4_current_sos_1 (svr4_info *info) +owning_intrusive_list<solib> +svr4_solib_ops::current_sos_1 (svr4_info *info) const { owning_intrusive_list<solib> sos; /* If we're using the probes interface, we can use the cache as it will be maintained by probe update/reload actions. */ if (info->probes_table != nullptr) - sos = svr4_collect_probes_sos (info); + sos = this->collect_probes_sos (info); /* If we're not using the probes interface or if we didn't cache anything, read the sos to fill the cache, then collect them from the cache. */ if (sos.empty ()) { - svr4_current_sos_direct (info); + this->current_sos_direct (info); - sos = svr4_collect_probes_sos (info); + sos = this->collect_probes_sos (info); if (sos.empty ()) - sos = svr4_default_sos (info); + sos = this->default_sos (info); } return sos; @@ -1478,11 +1458,11 @@ svr4_current_sos_1 (svr4_info *info) /* Implement the "current_sos" solib_ops method. */ -static owning_intrusive_list<solib> -svr4_current_sos () +owning_intrusive_list<solib> +svr4_solib_ops::current_sos () const { svr4_info *info = get_svr4_info (current_program_space); - owning_intrusive_list<solib> sos = svr4_current_sos_1 (info); + owning_intrusive_list<solib> sos = this->current_sos_1 (info); struct mem_range vsyscall_range; /* Filter out the vDSO module, if present. Its symbol file would @@ -1580,6 +1560,208 @@ svr4_fetch_objfile_link_map (struct objfile *objfile) return 0; } +/* Return true if bfd section BFD_SECT is a thread local section + (i.e. either named ".tdata" or ".tbss"), and false otherwise. */ + +static bool +is_thread_local_section (struct bfd_section *bfd_sect) +{ + return ((strcmp (bfd_sect->name, ".tdata") == 0 + || strcmp (bfd_sect->name, ".tbss") == 0) + && bfd_sect->size != 0); +} + +/* Return true if objfile OBJF contains a thread local section, and + false otherwise. */ + +static bool +has_thread_local_section (const objfile *objf) +{ + for (obj_section *objsec : objf->sections ()) + if (is_thread_local_section (objsec->the_bfd_section)) + return true; + return false; +} + +/* Return true if solib SO contains a thread local section, and false + otherwise. */ + +static bool +has_thread_local_section (const solib &so) +{ + for (const target_section &p : so.sections) + if (is_thread_local_section (p.the_bfd_section)) + return true; + return false; +} + +/* For the MUSL C library, given link map address LM_ADDR, return the + corresponding TLS module id, or 0 if not found. + + Background: Unlike the mechanism used by glibc (see below), the + scheme used by the MUSL C library is pretty simple. If the + executable contains TLS variables it gets module id 1. Otherwise, + the first shared object loaded which contains TLS variables is + assigned to module id 1. TLS-containing shared objects are then + assigned consecutive module ids, based on the order that they are + loaded. When unloaded via dlclose, module ids are reassigned as if + that module had never been loaded. */ + +int +musl_link_map_to_tls_module_id (CORE_ADDR lm_addr) +{ + /* When lm_addr is zero, the program is statically linked. Any TLS + variables will be in module id 1. */ + if (lm_addr == 0) + return 1; + + int mod_id = 0; + if (has_thread_local_section (current_program_space->symfile_object_file)) + mod_id++; + + struct svr4_info *info = get_svr4_info (current_program_space); + + /* Cause svr4_current_sos() to be run if it hasn't been already. */ + if (info->main_lm_addr == 0) + solib_add (NULL, 0, auto_solib_add); + + /* Handle case where lm_addr corresponds to the main program. + Return value is either 0, when there are no TLS variables, or 1, + when there are. */ + if (lm_addr == info->main_lm_addr) + return mod_id; + + /* Iterate through the shared objects, possibly incrementing the + module id, and returning mod_id should a match be found. */ + for (const solib &so : current_program_space->solibs ()) + { + if (has_thread_local_section (so)) + mod_id++; + + auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ()); + if (li->lm_addr == lm_addr) + return mod_id; + } + return 0; +} + +/* For GLIBC, given link map address LM_ADDR, return the corresponding TLS + module id, or 0 if not found. */ + +int +glibc_link_map_to_tls_module_id (CORE_ADDR lm_addr) +{ + /* When lm_addr is zero, the program is statically linked. Any TLS + variables will be in module id 1. */ + if (lm_addr == 0) + return 1; + + /* Look up lm_addr in the TLS slot data structure. */ + struct svr4_info *info = get_svr4_info (current_program_space); + auto it = std::find (info->glibc_tls_slots.begin (), + info->glibc_tls_slots.end (), + lm_addr); + if (it == info->glibc_tls_slots.end ()) + return 0; + else + return 1 + it - info->glibc_tls_slots.begin (); +} + +/* Conditionally, based on whether the shared object, SO, contains TLS + variables, assign a link map address to a TLS module id slot. This + code is GLIBC-specific and may only work for specific GLIBC + versions. That said, it is known to work for (at least) GLIBC + versions 2.27 thru 2.40. + + Background: In order to implement internal TLS address lookup + code, it is necessary to find the module id that has been + associated with a specific link map address. In GLIBC, the TLS + module id is stored in struct link_map, in the member + 'l_tls_modid'. While the first several members of struct link_map + are part of the SVR4 ABI, the offset to l_tls_modid definitely is + not. Therefore, since we don't know the offset to l_tls_modid, we + cannot simply look it up - which is a shame, because things would + be so much more easy and obviously accurate, if we could access + l_tls_modid. + + GLIBC has a concept of TLS module id slots. These slots are + allocated consecutively as shared objects containing TLS variables + are loaded. When unloaded (e.g. via dlclose()), the corresponding + slot is marked as unused, but may be used again when later loading + a shared object. + + The functions tls_maybe_fill_slot and tls_maybe_erase_slot are + associated with the observers 'solib_loaded' and 'solib_unloaded'. + They (attempt to) track use of TLS module id slots in the same way + that GLIBC does, which will hopefully provide an accurate module id + when asked to provide it via glibc_link_map_to_tls_module_id(), + above. */ + +static void +tls_maybe_fill_slot (solib &so) +{ + auto *li = dynamic_cast<lm_info_svr4 *> (so.lm_info.get ()); + if (li == nullptr) + return; + + struct svr4_info *info = get_svr4_info (current_program_space); + if (!info->glibc_tls_slots_inited) + { + /* Cause svr4_current_sos() to be run if it hasn't been already. */ + if (info->main_lm_addr == 0) + { + auto &ops + = gdb::checked_static_cast<const svr4_solib_ops &> (so.ops ()); + ops.current_sos_direct (info); + } + + /* Quit early when main_lm_addr is still 0. */ + if (info->main_lm_addr == 0) + return; + + /* Also quit early when symfile_object_file is not yet known. */ + if (current_program_space->symfile_object_file == nullptr) + return; + + if (has_thread_local_section (current_program_space->symfile_object_file)) + info->glibc_tls_slots.push_back (info->main_lm_addr); + info->glibc_tls_slots_inited = true; + } + + if (has_thread_local_section (so)) + { + auto it = std::find (info->glibc_tls_slots.begin (), + info->glibc_tls_slots.end (), + 0); + if (it == info->glibc_tls_slots.end ()) + info->glibc_tls_slots.push_back (li->lm_addr); + else + *it = li->lm_addr; + } +} + +/* Remove a link map address from the TLS module slot data structure. + As noted above, this code is GLIBC-specific. */ + +static void +tls_maybe_erase_slot (program_space *pspace, const solib &so, + bool still_in_use, bool silent) +{ + if (still_in_use) + return; + + auto *li = dynamic_cast<lm_info_svr4 *> (so.lm_info.get ()); + if (li == nullptr) + return; + + struct svr4_info *info = get_svr4_info (pspace); + auto it = std::find (info->glibc_tls_slots.begin (), + info->glibc_tls_slots.end (), + li->lm_addr); + if (it != info->glibc_tls_slots.end ()) + *it = 0; +} + /* On some systems, the only way to recognize the link map entry for the main executable file is by looking at its name. Return non-zero iff SONAME matches one of the known main executable names. */ @@ -1598,11 +1780,11 @@ match_main (const char *soname) return (0); } -/* Return 1 if PC lies in the dynamic symbol resolution code of the +/* Return true if PC lies in the dynamic symbol resolution code of the SVR4 run time loader. */ -int -svr4_in_dynsym_resolve_code (CORE_ADDR pc) +bool +svr4_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const { struct svr4_info *info = get_svr4_info (current_program_space); @@ -1801,12 +1983,10 @@ solib_event_probe_action (struct probe_and_action *pa) shared objects from the inferior. Handle special cases relating to the first elements of the list. Returns nonzero on success. */ -static int -solist_update_full (struct svr4_info *info) +void +svr4_solib_ops::update_full (svr4_info *info) const { - svr4_current_sos_direct (info); - - return 1; + this->current_sos_direct (info); } /* Update the shared object list starting from the link-map entry @@ -1814,9 +1994,9 @@ solist_update_full (struct svr4_info *info) nonzero if the list was successfully updated, or zero to indicate failure. */ -static int -solist_update_incremental (svr4_info *info, CORE_ADDR debug_base, - CORE_ADDR lm) +int +svr4_solib_ops::update_incremental (svr4_info *info, CORE_ADDR debug_base, + CORE_ADDR lm) const { /* Fall back to a full update if we are using a remote target that does not support incremental transfers. */ @@ -1858,9 +2038,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; @@ -1894,7 +2074,7 @@ solist_update_incremental (svr4_info *info, CORE_ADDR debug_base, above check and deferral to solist_update_full ensures that this call to svr4_read_so_list will never see the first element. */ - if (!svr4_read_so_list (info, lm, prev_lm, solist, 0)) + if (!this->read_so_list (info, lm, prev_lm, solist, 0)) return 0; } @@ -1905,8 +2085,8 @@ solist_update_incremental (svr4_info *info, CORE_ADDR debug_base, original interface. We don't reset the breakpoints as the ones set up for the probes-based interface are adequate. */ -static void -disable_probes_interface (svr4_info *info) +void +svr4_solib_ops::disable_probes_interface (svr4_info *info) const { warning (_("Probes-based dynamic linker interface failed.\n" "Reverting to original interface.")); @@ -1921,8 +2101,8 @@ disable_probes_interface (svr4_info *info) probes-based linker interface. Do nothing if using the standard interface. */ -static void -svr4_handle_solib_event (void) +void +svr4_solib_ops::handle_event () const { struct svr4_info *info = get_svr4_info (current_program_space); struct probe_and_action *pa; @@ -1947,9 +2127,9 @@ svr4_handle_solib_event (void) /* If anything goes wrong we revert to the original linker interface. */ - auto cleanup = make_scope_exit ([info] () + auto cleanup = make_scope_exit ([this, info] () { - disable_probes_interface (info); + this->disable_probes_interface (info); }); action = solib_event_probe_action (pa); @@ -2051,15 +2231,12 @@ svr4_handle_solib_event (void) if (action == UPDATE_OR_RELOAD) { - if (!solist_update_incremental (info, debug_base, lm)) + if (!this->update_incremental (info, debug_base, lm)) action = FULL_RELOAD; } if (action == FULL_RELOAD) - { - if (!solist_update_full (info)) - return; - } + this->update_full (info); cleanup.release (); } @@ -2106,8 +2283,8 @@ svr4_update_solib_event_breakpoint (struct breakpoint *b) /* Enable or disable optional solib event breakpoints as appropriate. Called whenever stop_on_solib_events is changed. */ -static void -svr4_update_solib_event_breakpoints (void) +void +svr4_solib_ops::update_breakpoints () const { for (breakpoint &bp : all_breakpoints_safe ()) svr4_update_solib_event_breakpoint (&bp); @@ -2118,10 +2295,10 @@ svr4_update_solib_event_breakpoints (void) solib event breakpoint will be created and registered for each probe. */ -static void -svr4_create_probe_breakpoints (svr4_info *info, struct gdbarch *gdbarch, - const std::vector<probe *> *probes, - struct objfile *objfile) +void +svr4_solib_ops::create_probe_breakpoints (svr4_info *info, gdbarch *gdbarch, + const std::vector<probe *> *probes, + objfile *objfile) const { for (int i = 0; i < NUM_PROBES; i++) { @@ -2139,17 +2316,17 @@ svr4_create_probe_breakpoints (svr4_info *info, struct gdbarch *gdbarch, } } - svr4_update_solib_event_breakpoints (); + this->update_breakpoints (); } /* Find all the glibc named probes. Only if all of the probes are found, then create them and return true. Otherwise return false. If WITH_PREFIX is set then add "rtld" to the front of the probe names. */ -static bool -svr4_find_and_create_probe_breakpoints (svr4_info *info, - struct gdbarch *gdbarch, - struct obj_section *os, - bool with_prefix) +bool +svr4_solib_ops::find_and_create_probe_breakpoints (svr4_info *info, + gdbarch *gdbarch, + obj_section *os, + bool with_prefix) const { SOLIB_SCOPED_DEBUG_START_END ("objfile=%s, with_prefix=%d", os->objfile->original_name, with_prefix); @@ -2227,7 +2404,7 @@ svr4_find_and_create_probe_breakpoints (svr4_info *info, /* All probes found. Now create them. */ solib_debug_printf ("using probes interface"); - svr4_create_probe_breakpoints (info, gdbarch, probes, os->objfile); + this->create_probe_breakpoints (info, gdbarch, probes, os->objfile); return true; } @@ -2243,15 +2420,16 @@ svr4_find_and_create_probe_breakpoints (svr4_info *info, probes aren't found, a single breakpoint is set on the original marker function. */ -static void -svr4_create_solib_event_breakpoints (svr4_info *info, struct gdbarch *gdbarch, - CORE_ADDR address) +void +svr4_solib_ops::create_event_breakpoints (svr4_info *info, gdbarch *gdbarch, + CORE_ADDR address) const { struct obj_section *os = find_pc_section (address); if (os == nullptr - || (!svr4_find_and_create_probe_breakpoints (info, gdbarch, os, false) - && !svr4_find_and_create_probe_breakpoints (info, gdbarch, os, true))) + || (!this->find_and_create_probe_breakpoints (info, gdbarch, os, false) + && !this->find_and_create_probe_breakpoints (info, gdbarch, os, + true))) { solib_debug_printf ("falling back to r_brk breakpoint: addr=%s", paddress (gdbarch, address)); @@ -2291,8 +2469,8 @@ svr4_create_solib_event_breakpoints (svr4_info *info, struct gdbarch *gdbarch, depending upon whether or not the library is being mapped or unmapped, and then set to RT_CONSISTENT after the library is mapped/unmapped. */ -static int -enable_break (struct svr4_info *info, int from_tty) +int +svr4_solib_ops::enable_break (svr4_info *info, int from_tty) const { const char * const *bkpt_namep; asection *interp_sect; @@ -2308,8 +2486,8 @@ enable_break (struct svr4_info *info, int from_tty) solib_add (NULL, from_tty, auto_solib_add); sym_addr = 0; - if (info->debug_base && solib_svr4_r_map (info->debug_base) != 0) - sym_addr = solib_svr4_r_brk (info); + if (info->debug_base && this->read_r_map (info->debug_base) != 0) + sym_addr = this->find_r_brk (info); if (sym_addr != 0) { @@ -2368,8 +2546,8 @@ enable_break (struct svr4_info *info, int from_tty) = info->interp_plt_sect_low + bfd_section_size (interp_sect); } - svr4_create_solib_event_breakpoints - (info, current_inferior ()->arch (), sym_addr); + this->create_event_breakpoints (info, current_inferior ()->arch (), + sym_addr); return 1; } } @@ -2377,7 +2555,7 @@ enable_break (struct svr4_info *info, int from_tty) /* Find the program interpreter; if not found, warn the user and drop into the old breakpoint at symbol code. */ std::optional<gdb::byte_vector> interp_name_holder - = find_program_interpreter (); + = svr4_find_program_interpreter (); if (interp_name_holder) { const char *interp_name = (const char *) interp_name_holder->data (); @@ -2417,11 +2595,11 @@ enable_break (struct svr4_info *info, int from_tty) address from the shared library table. */ for (const solib &so : current_program_space->solibs ()) { - if (svr4_same_1 (interp_name, so.so_original_name.c_str ())) + if (svr4_same_1 (interp_name, so.original_name.c_str ())) { load_addr_found = 1; loader_found_in_list = 1; - load_addr = lm_addr_check (so, tmp_bfd.get ()); + load_addr = this->lm_addr_check (so, tmp_bfd.get ()); break; } } @@ -2477,7 +2655,7 @@ enable_break (struct svr4_info *info, int from_tty) if (!loader_found_in_list) { - info->debug_loader_name = xstrdup (interp_name); + info->debug_loader_name = interp_name; info->debug_loader_offset_p = 1; info->debug_loader_offset = load_addr; solib_add (NULL, from_tty, auto_solib_add); @@ -2528,9 +2706,8 @@ enable_break (struct svr4_info *info, int from_tty) if (sym_addr != 0) { - svr4_create_solib_event_breakpoints (info, - current_inferior ()->arch (), - load_addr + sym_addr); + this->create_event_breakpoints (info, current_inferior ()->arch (), + load_addr + sym_addr); return 1; } @@ -2557,9 +2734,8 @@ enable_break (struct svr4_info *info, int from_tty) sym_addr = gdbarch_convert_from_func_ptr_addr (current_inferior ()->arch (), sym_addr, current_inferior ()->top_target ()); - svr4_create_solib_event_breakpoints (info, - current_inferior ()->arch (), - sym_addr); + this->create_event_breakpoints (info, current_inferior ()->arch (), + sym_addr); return 1; } } @@ -2577,8 +2753,9 @@ enable_break (struct svr4_info *info, int from_tty) sym_addr = gdbarch_convert_from_func_ptr_addr (current_inferior ()->arch (), sym_addr, current_inferior ()->top_target ()); - svr4_create_solib_event_breakpoints - (info, current_inferior ()->arch (), sym_addr); + this->create_event_breakpoints (info, + current_inferior ()->arch (), + sym_addr); return 1; } } @@ -3102,15 +3279,15 @@ svr4_relocate_main_executable (void) addresses, and saving sufficient information about them to allow their symbols to be read at a later time. */ -static void -svr4_solib_create_inferior_hook (int from_tty) +void +svr4_solib_ops::create_inferior_hook (int from_tty) const { struct svr4_info *info; info = get_svr4_info (current_program_space); /* Clear the probes-based interface's state. */ - free_probes_table (info); + this->free_probes_table (info); info->solib_lists.clear (); info->namespace_id.clear (); info->active_namespaces.clear (); @@ -3123,22 +3300,18 @@ svr4_solib_create_inferior_hook (int from_tty) if (!target_has_execution ()) return; - if (!svr4_have_link_map_offsets ()) - return; - - if (!enable_break (info, from_tty)) + if (!this->enable_break (info, from_tty)) return; } -static void -svr4_clear_solib (program_space *pspace) +void +svr4_solib_ops::clear_solib (program_space *pspace) const { svr4_info *info = get_svr4_info (pspace); info->debug_base = 0; info->debug_loader_offset_p = 0; info->debug_loader_offset = 0; - xfree (info->debug_loader_name); - info->debug_loader_name = NULL; + info->debug_loader_name.clear (); } /* Clear any bits of ADDR that wouldn't fit in a target-format @@ -3195,15 +3368,15 @@ find_loadable_elf_internal_phdr (bfd *abfd, bfd_section *asect) return nullptr; } -/* Implement solib_ops::relocate_section_addresses() for svr4 targets. */ - -static void -svr4_relocate_section_addresses (solib &so, target_section *sec) +void +svr4_solib_ops::relocate_section_addresses (solib &so, + target_section *sec) const { bfd *abfd = sec->the_bfd_section->owner; - sec->addr = svr4_truncate_ptr (sec->addr + lm_addr_check (so, abfd)); - sec->endaddr = svr4_truncate_ptr (sec->endaddr + lm_addr_check (so, abfd)); + sec->addr = svr4_truncate_ptr (sec->addr + this->lm_addr_check (so, abfd)); + sec->endaddr + = svr4_truncate_ptr (sec->endaddr + this->lm_addr_check (so, abfd)); struct bfd_section *asect = sec->the_bfd_section; gdb_assert (asect != nullptr); @@ -3273,69 +3446,25 @@ svr4_relocate_section_addresses (solib &so, target_section *sec) } } } - - -/* Architecture-specific operations. */ - -struct solib_svr4_ops -{ - /* Return a description of the layout of `struct link_map'. */ - struct link_map_offsets *(*fetch_link_map_offsets)(void) = nullptr; -}; - -/* Per-architecture data key. */ -static const registry<gdbarch>::key<struct solib_svr4_ops> solib_svr4_data; - -/* Return a default for the architecture-specific operations. */ - -static struct solib_svr4_ops * -get_ops (struct gdbarch *gdbarch) -{ - struct solib_svr4_ops *ops = solib_svr4_data.get (gdbarch); - if (ops == nullptr) - ops = solib_svr4_data.emplace (gdbarch); - return ops; -} -/* Set the architecture-specific `struct link_map_offsets' fetcher for - GDBARCH to FLMO. Also, install SVR4 solib_ops into GDBARCH. */ +/* See solib-svr4.h. */ void -set_solib_svr4_fetch_link_map_offsets (struct gdbarch *gdbarch, - struct link_map_offsets *(*flmo) (void)) +set_solib_svr4_ops (gdbarch *gdbarch, gdbarch_make_solib_ops_ftype make_solib_ops) { - struct solib_svr4_ops *ops = get_ops (gdbarch); - - ops->fetch_link_map_offsets = flmo; - - set_gdbarch_so_ops (gdbarch, &svr4_so_ops); + set_gdbarch_make_solib_ops (gdbarch, make_solib_ops); set_gdbarch_iterate_over_objfiles_in_search_order (gdbarch, svr4_iterate_over_objfiles_in_search_order); } -/* Fetch a link_map_offsets structure using the architecture-specific - `struct link_map_offsets' fetcher. */ +/* See solib-svr4.h. */ -static struct link_map_offsets * -svr4_fetch_link_map_offsets (void) +solib_ops_up +make_svr4_ilp32_solib_ops () { - struct solib_svr4_ops *ops = get_ops (current_inferior ()->arch ()); - - gdb_assert (ops->fetch_link_map_offsets); - return ops->fetch_link_map_offsets (); + return std::make_unique<ilp32_svr4_solib_ops> (); } -/* Return 1 if a link map offset fetcher has been defined, 0 otherwise. */ - -static int -svr4_have_link_map_offsets (void) -{ - struct solib_svr4_ops *ops = get_ops (current_inferior ()->arch ()); - - return (ops->fetch_link_map_offsets != NULL); -} - - /* Most OS'es that have SVR4-style ELF dynamic libraries define a `struct r_debug' and a `struct link_map' that are binary compatible with the original SVR4 implementation. */ @@ -3343,8 +3472,8 @@ svr4_have_link_map_offsets (void) /* Fetch (and possibly build) an appropriate `struct link_map_offsets' for an ILP32 SVR4 system. */ -struct link_map_offsets * -svr4_ilp32_fetch_link_map_offsets (void) +link_map_offsets * +ilp32_svr4_solib_ops::fetch_link_map_offsets () const { static struct link_map_offsets lmo; static struct link_map_offsets *lmp = NULL; @@ -3372,11 +3501,26 @@ svr4_ilp32_fetch_link_map_offsets (void) return lmp; } +/* solib_ops for LP64 SVR4 systems. */ + +struct lp64_svr4_solib_ops : public svr4_solib_ops +{ + link_map_offsets *fetch_link_map_offsets () const override; +}; + +/* See solib-svr4.h. */ + +solib_ops_up +make_svr4_lp64_solib_ops () +{ + return std::make_unique<lp64_svr4_solib_ops> (); +} + /* Fetch (and possibly build) an appropriate `struct link_map_offsets' for an LP64 SVR4 system. */ -struct link_map_offsets * -svr4_lp64_fetch_link_map_offsets (void) +link_map_offsets * +lp64_svr4_solib_ops::fetch_link_map_offsets () const { static struct link_map_offsets lmo; static struct link_map_offsets *lmp = NULL; @@ -3450,7 +3594,7 @@ find_debug_base_for_solib (const solib *solib) const std::vector<svr4_so> &sos = tuple.second; for (const svr4_so &so : sos) - if (svr4_same (solib->so_original_name.c_str (), so.name.c_str (), + if (svr4_same (solib->original_name.c_str (), so.name.c_str (), *lm_info, *so.lm_info)) return debug_base; } @@ -3523,19 +3667,15 @@ svr4_iterate_over_objfiles_in_search_order } } -/* See solib_ops::find_solib_addr in solist.h. */ - -static std::optional<CORE_ADDR> -svr4_find_solib_addr (solib &so) +std::optional<CORE_ADDR> +svr4_solib_ops::find_solib_addr (solib &so) const { auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ()); return li->l_addr_inferior; } -/* See solib_ops::find_solib_ns in solist.h. */ - -static int -svr4_find_solib_ns (const solib &so) +int +svr4_solib_ops::find_solib_ns (const solib &so) const { CORE_ADDR debug_base = find_debug_base_for_solib (&so); svr4_info *info = get_svr4_info (current_program_space); @@ -3550,17 +3690,15 @@ svr4_find_solib_ns (const solib &so) error (_("No namespace found")); } -/* see solib_ops::num_active_namespaces in solist.h. */ -static int -svr4_num_active_namespaces () +int +svr4_solib_ops::num_active_namespaces () const { svr4_info *info = get_svr4_info (current_program_space); return info->active_namespaces.size (); } -/* See solib_ops::get_solibs_in_ns in solist.h. */ -static std::vector<const solib *> -svr4_get_solibs_in_ns (int nsid) +std::vector<const solib *> +svr4_solib_ops::get_solibs_in_ns (int nsid) const { std::vector<const solib*> ns_solibs; svr4_info *info = get_svr4_info (current_program_space); @@ -3592,44 +3730,26 @@ svr4_get_solibs_in_ns (int nsid) /* This is inspired by the svr4_same, by finding the svr4_so object in the map, and then double checking if the lm_info is considered the same. */ - if (namespace_solibs.count (so.so_original_name) > 0 - && namespace_solibs[so.so_original_name]->l_addr_inferior + if (namespace_solibs.count (so.original_name) > 0 + && namespace_solibs[so.original_name]->l_addr_inferior == lm_inferior->l_addr_inferior) { ns_solibs.push_back (&so); /* Remove the SO from the map, so that we don't end up printing the dynamic linker multiple times. */ - namespace_solibs.erase (so.so_original_name); + namespace_solibs.erase (so.original_name); } } return ns_solibs; } -const struct solib_ops svr4_so_ops = -{ - svr4_relocate_section_addresses, - svr4_clear_so, - svr4_clear_solib, - svr4_solib_create_inferior_hook, - svr4_current_sos, - open_symbol_file_object, - svr4_in_dynsym_resolve_code, - solib_bfd_open, - svr4_same, - svr4_keep_data_in_core, - svr4_update_solib_event_breakpoints, - svr4_handle_solib_event, - svr4_find_solib_addr, - svr4_find_solib_ns, - svr4_num_active_namespaces, - svr4_get_solibs_in_ns, -}; - -void _initialize_svr4_solib (); -void -_initialize_svr4_solib () +INIT_GDB_FILE (svr4_solib) { gdb::observers::free_objfile.attach (svr4_free_objfile_observer, "solib-svr4"); + + /* Set up observers for tracking GLIBC TLS module id slots. */ + gdb::observers::solib_loaded.attach (tls_maybe_fill_slot, "solib-svr4"); + gdb::observers::solib_unloaded.attach (tls_maybe_erase_slot, "solib-svr4"); } diff --git a/gdb/solib-svr4.h b/gdb/solib-svr4.h index c08bacf..b331fa7 100644 --- a/gdb/solib-svr4.h +++ b/gdb/solib-svr4.h @@ -20,14 +20,17 @@ #ifndef GDB_SOLIB_SVR4_H #define GDB_SOLIB_SVR4_H -#include "solist.h" +#include "gdbarch.h" +#include "solib.h" struct objfile; -struct solib_ops; +struct link_map_offsets; +struct probe_and_action; +struct svr4_info; +struct svr4_library_list; +struct svr4_so; -extern const solib_ops svr4_so_ops; - -/* Link map info to include in an allocated so_list entry. */ +/* Link map info to include in an allocated solib entry. */ struct lm_info_svr4 final : public lm_info { @@ -50,6 +53,101 @@ struct lm_info_svr4 final : public lm_info using lm_info_svr4_up = std::unique_ptr<lm_info_svr4>; +/* What to do when a probe stop occurs. */ + +enum probe_action +{ + /* Something went seriously wrong. Stop using probes and + revert to using the older interface. */ + PROBES_INTERFACE_FAILED, + + /* No action is required. The shared object list is still + valid. */ + DO_NOTHING, + + /* The shared object list should be reloaded entirely. */ + FULL_RELOAD, + + /* Attempt to incrementally update the shared object list. If + the update fails or is not possible, fall back to reloading + the list in full. */ + UPDATE_OR_RELOAD, +}; + +/* solib_ops for SVR4 systems. */ + +struct svr4_solib_ops : public solib_ops +{ + void relocate_section_addresses (solib &so, target_section *) const override; + void clear_so (const solib &so) const override; + void clear_solib (program_space *pspace) const override; + void create_inferior_hook (int from_tty) const override; + owning_intrusive_list<solib> current_sos () const override; + bool open_symbol_file_object (int from_tty) const override; + bool in_dynsym_resolve_code (CORE_ADDR pc) const override; + bool same (const solib &gdb, const solib &inferior) const override; + bool keep_data_in_core (CORE_ADDR vaddr, unsigned long size) const override; + void update_breakpoints () const override; + void handle_event () const override; + std::optional<CORE_ADDR> find_solib_addr (solib &so) const override; + bool supports_namespaces () const override { return true; } + int find_solib_ns (const solib &so) const override; + int num_active_namespaces () const override; + std::vector<const solib *> get_solibs_in_ns (int nsid) const override; + + /* Return the appropriate link map offsets table for the architecture. */ + virtual link_map_offsets *fetch_link_map_offsets () const = 0; + + /* This needs to be public because it's accessed from an observer. */ + void current_sos_direct (svr4_info *info) const; + +private: + void create_probe_breakpoints (svr4_info *info, gdbarch *gdbarch, + const std::vector<probe *> *probes, + objfile *objfile) const; + bool find_and_create_probe_breakpoints (svr4_info *info, gdbarch *gdbarch, + obj_section *os, + bool with_prefix) const; + void create_event_breakpoints (svr4_info *info, gdbarch *gdbarch, + CORE_ADDR address) const; + int enable_break (svr4_info *info, int from_tty) const; + bool is_default_namespace (CORE_ADDR debug_base) const; + void free_probes_table (svr4_info *info) const; + CORE_ADDR find_r_brk (svr4_info *info) const; + CORE_ADDR find_r_ldsomap (svr4_info *info) const; + owning_intrusive_list<solib> default_sos (svr4_info *info) const; + int read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm, + std::vector<svr4_so> &sos, int ignore_first) const; + lm_info_svr4_up read_lm_info (CORE_ADDR lm_addr) const; + int has_lm_dynamic_from_link_map () const; + CORE_ADDR lm_addr_check (const solib &so, bfd *abfd) const; + CORE_ADDR read_r_next (CORE_ADDR debug_base) const; + CORE_ADDR read_r_map (CORE_ADDR debug_base) const; + int parse_libraries (const char *document, svr4_library_list *list); + int current_sos_via_xfer_libraries (svr4_library_list *list, + const char *annex) const; + owning_intrusive_list<solib> collect_probes_sos (svr4_info *info) const; + owning_intrusive_list<solib> current_sos_1 (svr4_info *info) const; + owning_intrusive_list<solib> solibs_from_svr4_sos + (const std::vector<svr4_so> &sos) const; + void register_event_probe (objfile *objfile, probe *prob, CORE_ADDR address, + enum probe_action action) const; + void disable_probes_interface (svr4_info *info) const; + probe_and_action *event_probe_at (CORE_ADDR address) const; + void update_full (svr4_info *info) const; + int update_incremental (svr4_info *info, CORE_ADDR debug_base, + CORE_ADDR lm) const; + bool update_event_breakpoint (breakpoint *b) const; + CORE_ADDR find_debug_base (const solib *solib) const; +}; + +/* solib_ops for ILP32 SVR4 systems. */ + +struct ilp32_svr4_solib_ops : public svr4_solib_ops +{ + link_map_offsets *fetch_link_map_offsets () const override; +}; + /* Critical offsets and sizes which describe struct r_debug and struct link_map on SVR4-like targets. All offsets and sizes are in bytes unless otherwise specified. */ @@ -91,25 +189,33 @@ struct link_map_offsets int l_name_offset; }; -/* set_solib_svr4_fetch_link_map_offsets() is intended to be called by - a <arch>_gdbarch_init() function. It is used to establish an - architecture specific link_map_offsets fetcher for the architecture - being defined. */ +/* Set the gdbarch methods for SVR4 systems. */ -extern void set_solib_svr4_fetch_link_map_offsets - (struct gdbarch *gdbarch, struct link_map_offsets *(*func) (void)); +extern void set_solib_svr4_ops (gdbarch *gdbarch, + gdbarch_make_solib_ops_ftype make_solib_ops); /* This function is called by thread_db.c. Return the address of the link map for the given objfile. */ extern CORE_ADDR svr4_fetch_objfile_link_map (struct objfile *objfile); -/* Fetch (and possibly build) an appropriate `struct link_map_offsets' - for ILP32 and LP64 SVR4 systems. */ -extern struct link_map_offsets *svr4_ilp32_fetch_link_map_offsets (void); -extern struct link_map_offsets *svr4_lp64_fetch_link_map_offsets (void); +/* Return a new solib_ops for ILP32 SVR4 systems. */ + +extern solib_ops_up make_svr4_ilp32_solib_ops (); + +/* Return a new solib_ops for LP64 SVR4 systems. */ + +extern solib_ops_up make_svr4_lp64_solib_ops (); + +/* For the MUSL C library, given link map address LM_ADDR, return the + corresponding TLS module id, or 0 if not found. */ +int musl_link_map_to_tls_module_id (CORE_ADDR lm_addr); + +/* For GLIBC, given link map address LM_ADDR, return the corresponding TLS + module id, or 0 if not found. */ +int glibc_link_map_to_tls_module_id (CORE_ADDR lm_addr); + +/* Return program interpreter string. */ -/* Return 1 if PC lies in the dynamic symbol resolution code of the - SVR4 run time loader. */ -int svr4_in_dynsym_resolve_code (CORE_ADDR pc); +std::optional<gdb::byte_vector> svr4_find_program_interpreter (); #endif /* GDB_SOLIB_SVR4_H */ diff --git a/gdb/solib-target.c b/gdb/solib-target.c index f304431..770028d 100644 --- a/gdb/solib-target.c +++ b/gdb/solib-target.c @@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "objfiles.h" -#include "solist.h" +#include "solib.h" #include "symtab.h" #include "symfile.h" #include "target.h" @@ -30,7 +30,7 @@ struct lm_info_target final : public lm_info { /* The library's name. The name is normally kept in the struct - so_list; it is only here during XML parsing. */ + solib; it is only here during XML parsing. */ std::string name; /* The target can either specify segment bases or section bases, not @@ -226,8 +226,8 @@ solib_target_parse_libraries (const char *library) } #endif -static owning_intrusive_list<solib> -solib_target_current_sos (void) +owning_intrusive_list<solib> +target_solib_ops::current_sos () const { owning_intrusive_list<solib> sos; @@ -245,25 +245,20 @@ solib_target_current_sos (void) /* Build a struct solib for each entry on the list. */ for (lm_info_target_up &info : library_list) { - auto &new_solib = sos.emplace_back (); + auto &new_solib = sos.emplace_back (*this); /* We don't need a copy of the name in INFO anymore. */ - new_solib.so_name = std::move (info->name); - new_solib.so_original_name = new_solib.so_name; + new_solib.name = std::move (info->name); + new_solib.original_name = new_solib.name; new_solib.lm_info = std::move (info); } return sos; } -static void -solib_target_solib_create_inferior_hook (int from_tty) -{ - /* Nothing needed. */ -} - -static void -solib_target_relocate_section_addresses (solib &so, target_section *sec) +void +target_solib_ops::relocate_section_addresses (solib &so, + target_section *sec) const { CORE_ADDR offset; auto *li = gdb::checked_static_cast<lm_info_target *> (so.lm_info.get ()); @@ -291,7 +286,7 @@ solib_target_relocate_section_addresses (solib &so, target_section *sec) if (num_alloc_sections != li->section_bases.size ()) warning (_("\ Could not relocate shared library \"%s\": wrong number of ALLOC sections"), - so.so_name.c_str ()); + so.name.c_str ()); else { int bases_index = 0; @@ -334,7 +329,7 @@ Could not relocate shared library \"%s\": wrong number of ALLOC sections"), if (data == NULL) warning (_("\ -Could not relocate shared library \"%s\": no segments"), so.so_name.c_str ()); +Could not relocate shared library \"%s\": no segments"), so.name.c_str ()); else { ULONGEST orig_delta; @@ -345,7 +340,7 @@ Could not relocate shared library \"%s\": no segments"), so.so_name.c_str ()); li->segment_bases.size (), li->segment_bases.data ())) warning (_("\ -Could not relocate shared library \"%s\": bad offsets"), so.so_name.c_str ()); +Could not relocate shared library \"%s\": bad offsets"), so.name.c_str ()); /* Find the range of addresses to report for this library in "info sharedlibrary". Report any consecutive segments @@ -382,16 +377,8 @@ Could not relocate shared library \"%s\": bad offsets"), so.so_name.c_str ()); sec->endaddr += offset; } -static int -solib_target_open_symbol_file_object (int from_tty) -{ - /* We can't locate the main symbol file based on the target's - knowledge; the user has to specify it. */ - return 0; -} - -static int -solib_target_in_dynsym_resolve_code (CORE_ADDR pc) +bool +target_solib_ops::in_dynsym_resolve_code (CORE_ADDR pc) const { /* We don't have a range of addresses for the dynamic linker; there may not be one in the program's address space. So only report @@ -399,19 +386,10 @@ solib_target_in_dynsym_resolve_code (CORE_ADDR pc) return in_plt_section (pc); } -const solib_ops solib_target_so_ops = +/* See solib-target.h. */ + +solib_ops_up +make_target_solib_ops () { - solib_target_relocate_section_addresses, - nullptr, - nullptr, - solib_target_solib_create_inferior_hook, - solib_target_current_sos, - solib_target_open_symbol_file_object, - solib_target_in_dynsym_resolve_code, - solib_bfd_open, - nullptr, - nullptr, - nullptr, - nullptr, - default_find_solib_addr, -}; + return std::make_unique<target_solib_ops> (); +} diff --git a/gdb/solib-target.h b/gdb/solib-target.h index f8a22fd..89ae2bc 100644 --- a/gdb/solib-target.h +++ b/gdb/solib-target.h @@ -20,7 +20,19 @@ #ifndef GDB_SOLIB_TARGET_H #define GDB_SOLIB_TARGET_H -struct solib_ops; -extern const solib_ops solib_target_so_ops; +#include "solib.h" + +/* solib_ops for systems fetching solibs from the target. */ + +struct target_solib_ops : solib_ops +{ + void relocate_section_addresses (solib &so, target_section *) const override; + owning_intrusive_list<solib> current_sos () const override; + bool in_dynsym_resolve_code (CORE_ADDR pc) const override; +}; + +/* Return a new solib_ops for systems fetching solibs from the target. */ + +solib_ops_up make_target_solib_ops (); #endif /* GDB_SOLIB_TARGET_H */ diff --git a/gdb/solib.c b/gdb/solib.c index 5c5cfbd..e43b1a3 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -37,7 +37,6 @@ #include "elf/common.h" #include "filenames.h" #include "exec.h" -#include "solist.h" #include "observable.h" #include "readline/tilde.h" #include "solib.h" @@ -468,6 +467,12 @@ solib_bfd_open (const char *pathname) return abfd; } +gdb_bfd_ref_ptr +solib_ops::bfd_open (const char *pathname) const +{ + return solib_bfd_open (pathname); +} + /* Given a pointer to one of the shared objects in our list of mapped objects, use the recorded name to open a bfd descriptor for the object, build a section table, relocate all the section addresses @@ -483,10 +488,8 @@ solib_bfd_open (const char *pathname) static int solib_map_sections (solib &so) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); - - gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.so_name.c_str ())); - gdb_bfd_ref_ptr abfd (ops->bfd_open (filename.get ())); + gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.name.c_str ())); + gdb_bfd_ref_ptr abfd (so.ops ().bfd_open (filename.get ())); /* If we have a core target then the core target might have some helpful information (i.e. build-ids) about the shared libraries we are trying @@ -496,9 +499,9 @@ solib_map_sections (solib &so) If we don't have a core target then this will return an empty struct with no hint information, we then lookup the shared library based on its filename. */ - std::optional<CORE_ADDR> solib_addr = ops->find_solib_addr (so); + std::optional<CORE_ADDR> solib_addr = so.ops ().find_solib_addr (so); std::optional <const core_target_mapped_file_info> mapped_file_info - = core_target_find_mapped_file (so.so_name.c_str (), solib_addr); + = core_target_find_mapped_file (so.name.c_str (), solib_addr); /* If we already know the build-id of this solib from a core file, verify it matches ABFD's build-id. If there is a mismatch or the solib wasn't @@ -520,14 +523,14 @@ solib_map_sections (solib &so) However, if it was good enough during the mapped file processing, we assume it's good enough now. */ if (!mapped_file_info->filename ().empty ()) - abfd = ops->bfd_open (mapped_file_info->filename ().c_str ()); + abfd = so.ops ().bfd_open (mapped_file_info->filename ().c_str ()); else abfd = nullptr; if (abfd == nullptr) abfd = find_objfile_by_build_id (current_program_space, mapped_file_info->build_id (), - so.so_name.c_str ()); + so.name.c_str ()); if (abfd == nullptr && mismatch) { @@ -545,14 +548,14 @@ solib_map_sections (solib &so) /* Leave bfd open, core_xfer_memory and "info files" need it. */ so.abfd = std::move (abfd); - /* Copy the full path name into so_name, allowing symbol_file_add + /* Copy the full path name into `so.name`, allowing symbol_file_add to find it later. This also affects the =library-loaded GDB/MI event, and in particular the part of that notification providing the library's host-side path. If we let the target dictate that objfile's path, and the target is different from the host, GDB/MI will not provide the correct host-side path. */ - so.so_name = bfd_get_filename (so.abfd.get ()); + so.name = bfd_get_filename (so.abfd.get ()); so.sections = build_section_table (so.abfd.get ()); for (target_section &p : so.sections) @@ -560,7 +563,7 @@ solib_map_sections (solib &so) /* Relocate the section binding addresses as recorded in the shared object's file by the base address to which the object was actually mapped. */ - ops->relocate_section_addresses (so, &p); + so.ops ().relocate_section_addresses (so, &p); /* If the target didn't provide information about the address range of the shared object, assume we want the location of @@ -582,13 +585,11 @@ solib_map_sections (solib &so) return 1; } -/* See solist.h. */ +/* See solib.h. */ void solib::clear () { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); - this->sections.clear (); this->abfd = nullptr; @@ -600,11 +601,10 @@ solib::clear () /* Restore the target-supplied file name. SO_NAME may be the path of the symbol file. */ - this->so_name = this->so_original_name; + this->name = this->original_name; /* Do the same for target-specific data. */ - if (ops->clear_so != NULL) - ops->clear_so (*this); + this->ops ().clear_so (*this); } lm_info::~lm_info () = default; @@ -634,7 +634,7 @@ solib_read_symbols (solib &so, symfile_add_flags flags) so.objfile = nullptr; for (objfile *objfile : current_program_space->objfiles ()) { - if (filename_cmp (objfile_name (objfile), so.so_name.c_str ()) + if (filename_cmp (objfile_name (objfile), so.name.c_str ()) == 0 && objfile->addr_low == so.addr_low) { @@ -648,7 +648,7 @@ solib_read_symbols (solib &so, symfile_add_flags flags) = build_section_addr_info_from_section_table (so.sections); gdb_bfd_ref_ptr tmp_bfd = so.abfd; so.objfile - = symbol_file_add_from_bfd (tmp_bfd, so.so_name.c_str (), + = symbol_file_add_from_bfd (tmp_bfd, so.name.c_str (), flags, &sap, OBJF_SHARED, nullptr); so.objfile->addr_low = so.addr_low; } @@ -660,7 +660,7 @@ solib_read_symbols (solib &so, symfile_add_flags flags) exception_fprintf (gdb_stderr, e, _ ("Error while reading shared" " library symbols for %s:\n"), - so.so_name.c_str ()); + so.name.c_str ()); } return true; @@ -712,7 +712,10 @@ notify_solib_unloaded (program_space *pspace, const solib &so, void update_solib_list (int from_tty) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); + const solib_ops *ops = current_program_space->solib_ops (); + + if (ops == nullptr) + return; /* We can reach here due to changing solib-search-path or the sysroot, before having any inferior. */ @@ -724,7 +727,7 @@ update_solib_list (int from_tty) have not opened a symbol file, we may be able to get its symbols now! */ if (inf->attach_flag - && current_program_space->symfile_object_file == NULL) + && current_program_space->symfile_object_file == nullptr) { try { @@ -765,27 +768,16 @@ update_solib_list (int from_tty) owning_intrusive_list<solib> inferior = ops->current_sos (); owning_intrusive_list<solib>::iterator gdb_iter - = current_program_space->so_list.begin (); - while (gdb_iter != current_program_space->so_list.end ()) + = current_program_space->solibs ().begin (); + while (gdb_iter != current_program_space->solibs ().end ()) { intrusive_list<solib>::iterator inferior_iter = inferior.begin (); /* Check to see whether the shared object *gdb also appears in the inferior's current list. */ for (; inferior_iter != inferior.end (); ++inferior_iter) - { - if (ops->same) - { - if (ops->same (*gdb_iter, *inferior_iter)) - break; - } - else - { - if (!filename_cmp (gdb_iter->so_original_name.c_str (), - inferior_iter->so_original_name.c_str ())) - break; - } - } + if (ops->same (*gdb_iter, *inferior_iter)) + break; /* If the shared object appears on the inferior's list too, then it's still loaded, so we don't need to do anything. Delete @@ -814,13 +806,13 @@ update_solib_list (int from_tty) && !still_in_use) gdb_iter->objfile->unlink (); - current_program_space->deleted_solibs.push_back (gdb_iter->so_name); + current_program_space->deleted_solibs.push_back (gdb_iter->name); /* Some targets' section tables might be referring to sections from so.abfd; remove them. */ current_program_space->remove_target_sections (&*gdb_iter); - gdb_iter = current_program_space->so_list.erase (gdb_iter); + gdb_iter = current_program_space->solibs ().erase (gdb_iter); } } @@ -844,7 +836,7 @@ update_solib_list (int from_tty) { not_found++; if (not_found_filename == NULL) - not_found_filename = new_so.so_original_name.c_str (); + not_found_filename = new_so.original_name.c_str (); } } @@ -861,7 +853,7 @@ update_solib_list (int from_tty) } /* Add the new shared objects to GDB's list. */ - current_program_space->so_list.splice (std::move (inferior)); + current_program_space->solibs ().splice (std::move (inferior)); /* If a library was not found, issue an appropriate warning message. We have to use a single call to warning in case the @@ -918,7 +910,7 @@ libpthread_name_p (const char *name) static bool libpthread_solib_p (const solib &so) { - return libpthread_name_p (so.so_name.c_str ()); + return libpthread_name_p (so.name.c_str ()); } /* Read in symbolic information for any shared objects whose names @@ -968,7 +960,7 @@ solib_add (const char *pattern, int from_tty, int readsyms) add_flags |= SYMFILE_VERBOSE; for (solib &gdb : current_program_space->solibs ()) - if (!pattern || re_exec (gdb.so_name.c_str ())) + if (!pattern || re_exec (gdb.name.c_str ())) { /* Normally, we would read the symbols from that library only if READSYMS is set. However, we're making a small @@ -987,7 +979,7 @@ solib_add (const char *pattern, int from_tty, int readsyms) if (pattern && (from_tty || info_verbose)) gdb_printf (_ ("Symbols already loaded for %ps\n"), styled_string (file_name_style.style (), - gdb.so_name.c_str ())); + gdb.name.c_str ())); } else if (solib_read_symbols (gdb, add_flags)) loaded_any_symbols = true; @@ -1025,16 +1017,21 @@ print_solib_list_table (std::vector<const solib *> solib_list, gdbarch *gdbarch = current_inferior ()->arch (); /* "0x", a little whitespace, and two hex digits per byte of pointers. */ int addr_width = 4 + (gdbarch_ptr_bit (gdbarch) / 4); - const solib_ops *ops = gdbarch_so_ops (gdbarch); + const solib_ops *ops = current_program_space->solib_ops (); struct ui_out *uiout = current_uiout; bool so_missing_debug_info = false; + if (ops == nullptr) + return; + /* There are 3 conditions for this command to print solib namespaces, first PRINT_NAMESPACE has to be true, second the solib_ops has to support multiple namespaces, and third there must be more than one active namespace. Fold all these into the PRINT_NAMESPACE condition. */ - print_namespace = print_namespace && ops->num_active_namespaces != nullptr - && ops->num_active_namespaces () > 1; + print_namespace = (print_namespace + && ops != nullptr + && ops->supports_namespaces () + && ops->num_active_namespaces () > 1); int num_cols = 4; if (print_namespace) @@ -1056,7 +1053,7 @@ print_solib_list_table (std::vector<const solib *> solib_list, for (const solib *so : solib_list) { - if (so->so_name.empty ()) + if (so->name.empty ()) continue; ui_out_emit_tuple tuple_emitter (uiout, "lib"); @@ -1085,7 +1082,7 @@ print_solib_list_table (std::vector<const solib *> solib_list, } if (!top_level_interpreter ()->interp_ui_out ()->is_mi_like_p () - && so->symbols_loaded && !objfile_has_symbols (so->objfile)) + && so->symbols_loaded && !so->objfile->has_symbols ()) { so_missing_debug_info = true; uiout->field_string ("syms-read", "Yes (*)"); @@ -1093,7 +1090,7 @@ print_solib_list_table (std::vector<const solib *> solib_list, else uiout->field_string ("syms-read", so->symbols_loaded ? "Yes" : "No"); - uiout->field_string ("name", so->so_name, file_name_style.style ()); + uiout->field_string ("name", so->name, file_name_style.style ()); uiout->text ("\n"); } @@ -1130,9 +1127,9 @@ info_sharedlibrary_command (const char *pattern, int from_tty) std::vector<const solib *> print_libs; for (const solib &so : current_program_space->solibs ()) { - if (!so.so_name.empty ()) + if (!so.name.empty ()) { - if (pattern && !re_exec (so.so_name.c_str ())) + if (pattern && !re_exec (so.name.c_str ())) continue; print_libs.push_back (&so); } @@ -1159,12 +1156,13 @@ info_sharedlibrary_command (const char *pattern, int from_tty) static void info_linker_namespace_command (const char *pattern, int from_tty) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); + const solib_ops *ops = current_program_space->solib_ops (); + /* This command only really makes sense for inferiors that support linker namespaces, so we can leave early. */ - if (ops->num_active_namespaces == nullptr) - error (_("Current inferior does not support linker namespaces." \ - "Use \"info sharedlibrary\" instead")); + if (ops == nullptr || !ops->supports_namespaces ()) + error (_("Current inferior does not support linker namespaces. " + "Use \"info sharedlibrary\" instead.")); struct ui_out *uiout = current_uiout; std::vector<std::pair<int, std::vector<const solib *>>> all_solibs_to_print; @@ -1230,7 +1228,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); @@ -1265,24 +1263,28 @@ solib_contains_address_p (const solib &solib, CORE_ADDR address) const char * solib_name_from_address (struct program_space *pspace, CORE_ADDR address) { - for (const solib &so : pspace->so_list) + for (const solib &so : pspace->solibs ()) if (solib_contains_address_p (so, address)) - return so.so_name.c_str (); + return so.name.c_str (); return nullptr; } +bool +solib_ops::same (const solib &a, const solib &b) const +{ + return (filename_cmp (a.original_name.c_str (), b.original_name.c_str ()) + == 0); +} + /* See solib.h. */ bool solib_keep_data_in_core (CORE_ADDR vaddr, unsigned long size) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); + const solib_ops *ops = current_program_space->solib_ops (); - if (ops->keep_data_in_core) - return ops->keep_data_in_core (vaddr, size) != 0; - else - return false; + return ops != nullptr && ops->keep_data_in_core (vaddr, size); } /* See solib.h. */ @@ -1290,9 +1292,7 @@ solib_keep_data_in_core (CORE_ADDR vaddr, unsigned long size) void clear_solib (program_space *pspace) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); - - for (solib &so : pspace->so_list) + for (solib &so : pspace->solibs ()) { bool still_in_use = (so.objfile != nullptr && solib_used (pspace, so)); @@ -1301,9 +1301,10 @@ clear_solib (program_space *pspace) pspace->remove_target_sections (&so); }; - pspace->so_list.clear (); + pspace->solibs ().clear (); - if (ops->clear_solib != nullptr) + if (const solib_ops *ops = pspace->solib_ops (); + ops != nullptr) ops->clear_solib (pspace); } @@ -1315,9 +1316,9 @@ clear_solib (program_space *pspace) void solib_create_inferior_hook (int from_tty) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); - - ops->solib_create_inferior_hook (from_tty); + if (const solib_ops *ops = current_program_space->solib_ops (); + ops != nullptr) + ops->create_inferior_hook (from_tty); } /* See solib.h. */ @@ -1325,9 +1326,9 @@ solib_create_inferior_hook (int from_tty) bool in_solib_dynsym_resolve_code (CORE_ADDR pc) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); + const solib_ops *ops = current_program_space->solib_ops (); - return ops->in_dynsym_resolve_code (pc) != 0; + return ops != nullptr && ops->in_dynsym_resolve_code (pc); } /* Implements the "sharedlibrary" command. */ @@ -1369,9 +1370,9 @@ no_shared_libraries_command (const char *ignored, int from_tty) void update_solib_breakpoints (void) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); + const solib_ops *ops = current_program_space->solib_ops (); - if (ops->update_breakpoints != NULL) + if (ops != nullptr) ops->update_breakpoints (); } @@ -1380,9 +1381,8 @@ update_solib_breakpoints (void) void handle_solib_event (void) { - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); - - if (ops->handle_event != NULL) + if (const solib_ops *ops = current_program_space->solib_ops (); + ops != nullptr) ops->handle_event (); current_inferior ()->pspace->clear_solib_cache (); @@ -1414,8 +1414,9 @@ reload_shared_libraries_1 (int from_tty) add_flags |= SYMFILE_VERBOSE; gdb::unique_xmalloc_ptr<char> filename ( - tilde_expand (so.so_original_name.c_str ())); - gdb_bfd_ref_ptr abfd (solib_bfd_open (filename.get ())); + tilde_expand (so.original_name.c_str ())); + + gdb_bfd_ref_ptr abfd = so.ops ().bfd_open (filename.get ()); if (abfd != NULL) found_pathname = bfd_get_filename (abfd.get ()); @@ -1423,7 +1424,7 @@ reload_shared_libraries_1 (int from_tty) symbol file, close that. */ if ((found_pathname == NULL && was_loaded) || (found_pathname != NULL - && filename_cmp (found_pathname, so.so_name.c_str ()) != 0)) + && filename_cmp (found_pathname, so.name.c_str ()) != 0)) { if (so.objfile && !(so.objfile->flags & OBJF_USERLOADED) && !solib_used (current_program_space, so)) @@ -1436,7 +1437,7 @@ reload_shared_libraries_1 (int from_tty) file, open it. */ if (found_pathname != NULL && (!was_loaded - || filename_cmp (found_pathname, so.so_name.c_str ()) != 0)) + || filename_cmp (found_pathname, so.name.c_str ()) != 0)) { bool got_error = false; @@ -1466,8 +1467,6 @@ reload_shared_libraries (const char *ignored, int from_tty, { reload_shared_libraries_1 (from_tty); - const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); - /* Creating inferior hooks here has two purposes. First, if we reload shared libraries then the address of solib breakpoint we've computed previously might be no longer valid. For example, if we forgot to set @@ -1480,8 +1479,9 @@ reload_shared_libraries (const char *ignored, int from_tty, if (target_has_execution ()) { /* Reset or free private data structures not associated with - so_list entries. */ - if (ops->clear_solib != nullptr) + solib entries. */ + if (const solib_ops *ops = current_program_space->solib_ops (); + ops != nullptr) ops->clear_solib (current_program_space); /* Remove any previous solib event breakpoint. This is usually @@ -1808,56 +1808,42 @@ remove_user_added_objfile (struct objfile *objfile) } } -/* See solist.h. */ +/* Implementation of the linker_namespace convenience variable. -std::optional<CORE_ADDR> -default_find_solib_addr (solib &so) -{ - return {}; -} - -/* Implementation of the current_linker_namespace convenience variable. This returns the GDB internal identifier of the linker namespace, - for the current frame, in the form '[[<number>]]'. If the inferior - doesn't support linker namespaces, this always returns [[0]]. */ + for the selected frame, as an integer. If the inferior doesn't support + linker namespaces, this always returns 0. */ static value * -current_linker_namespace_make_value (gdbarch *gdbarch, internalvar *var, +linker_namespace_make_value (gdbarch *gdbarch, internalvar *var, void *ignore) { - const solib_ops *ops = gdbarch_so_ops (gdbarch); - const language_defn *lang = language_def (get_frame_language - (get_current_frame ())); - std::string nsid = "[[0]]"; - if (ops->find_solib_ns != nullptr) - { - CORE_ADDR curr_pc = get_frame_pc (get_current_frame ()); - for (const solib &so : current_program_space->solibs ()) - if (solib_contains_address_p (so, curr_pc)) - { - nsid = string_printf ("[[%d]]", ops->find_solib_ns (so)); - break; - } - } + int nsid = 0; + CORE_ADDR curr_pc = get_frame_pc (get_selected_frame ()); + + for (const solib &so : current_program_space->solibs ()) + if (solib_contains_address_p (so, curr_pc)) + { + if (so.ops ().supports_namespaces ()) + nsid = so.ops ().find_solib_ns (so); + break; + } /* If the PC is not in an SO, or the solib_ops doesn't support linker namespaces, the inferior is in the default namespace. */ - return lang->value_string (gdbarch, nsid.c_str (), nsid.length ()); + return value_from_longest (builtin_type (gdbarch)->builtin_int, nsid); } -/* Implementation of `$_current_linker_namespace' variable. */ +/* Implementation of `$_linker_namespace' variable. */ -static const struct internalvar_funcs current_linker_namespace_funcs = +static const struct internalvar_funcs linker_namespace_funcs = { - current_linker_namespace_make_value, + linker_namespace_make_value, nullptr, }; -void _initialize_solib (); - -void -_initialize_solib () +INIT_GDB_FILE (solib) { gdb::observers::free_objfile.attach (remove_user_added_objfile, "solib"); gdb::observers::inferior_execd.attach ( @@ -1869,8 +1855,8 @@ _initialize_solib () /* Convenience variables for debugging linker namespaces. These are set here, even if the solib_ops doesn't support them, for consistency. */ - create_internalvar_type_lazy ("_current_linker_namespace", - ¤t_linker_namespace_funcs, nullptr); + create_internalvar_type_lazy ("_linker_namespace", + &linker_namespace_funcs, nullptr); set_internalvar_integer (lookup_internalvar ("_active_linker_namespaces"), 1); add_com ( diff --git a/gdb/solib.h b/gdb/solib.h index 360b6ef..b9465e1 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -20,15 +20,14 @@ #ifndef GDB_SOLIB_H #define GDB_SOLIB_H -/* Forward decl's for prototypes */ -struct solib; -struct target_ops; -struct solib_ops; -struct program_space; - #include "gdb_bfd.h" -#include "symfile-add-flags.h" #include "gdbsupport/function-view.h" +#include "gdbsupport/intrusive_list.h" +#include "gdbsupport/owning_intrusive_list.h" +#include "symfile-add-flags.h" +#include "target-section.h" + +struct program_space; /* Value of the 'set debug solib' configuration variable. */ @@ -42,6 +41,243 @@ extern bool debug_solib; #define SOLIB_SCOPED_DEBUG_START_END(fmt, ...) \ scoped_debug_start_end (debug_solib, "solib", fmt, ##__VA_ARGS__) +#define SO_NAME_MAX_PATH_SIZE 512 /* FIXME: Should be dynamic */ + +/* Base class for target-specific link map information. */ + +struct lm_info +{ + lm_info () = default; + lm_info (const lm_info &) = default; + virtual ~lm_info () = 0; +}; + +using lm_info_up = std::unique_ptr<lm_info>; + +struct solib_ops; + +struct solib : intrusive_list_node<solib> +{ + /* Constructor + + OPS is the solib_ops implementation providing this solib. */ + explicit solib (const solib_ops &ops) : m_ops (&ops) {} + + /* Return the solib_ops implementation providing this solib. */ + const solib_ops &ops () const + { return *m_ops; } + + /* Free symbol-file related contents of SO and reset for possible reloading + of SO. If we have opened a BFD for SO, close it. If we have placed SO's + sections in some target's section table, the caller is responsible for + removing them. + + This function doesn't mess with objfiles at all. If there is an + objfile associated with SO that needs to be removed, the caller is + responsible for taking care of that. */ + void clear () ; + + /* The following fields of the structure come directly from the + dynamic linker's tables in the inferior, and are initialized by + current_sos. */ + + /* A pointer to target specific link map information. Often this + will be a copy of struct link_map from the user process, but + it need not be; it can be any collection of data needed to + traverse the dynamic linker's data structures. */ + lm_info_up lm_info; + + /* Shared object file name, exactly as it appears in the + inferior's link map. This may be a relative path, or something + which needs to be looked up in LD_LIBRARY_PATH, etc. We use it + to tell which entries in the inferior's dynamic linker's link + map we've already loaded. */ + std::string original_name; + + /* Shared object file name, expanded to something GDB can open. */ + std::string name; + + /* The following fields of the structure are built from + information gathered from the shared object file itself, and + are set when we actually add it to our symbol tables. + + current_sos must initialize these fields to 0. */ + + gdb_bfd_ref_ptr abfd; + + /* True if symbols have been read in. */ + bool symbols_loaded = false; + + /* objfile with symbols for a loaded library. Target memory is read from + ABFD. OBJFILE may be NULL either before symbols have been loaded, if + the file cannot be found or after the command "nosharedlibrary". */ + struct objfile *objfile = nullptr; + + std::vector<target_section> sections; + + /* Record the range of addresses belonging to this shared library. + There may not be just one (e.g. if two segments are relocated + differently). This is used for "info sharedlibrary" and + the MI command "-file-list-shared-libraries". The latter has a format + that supports outputting multiple segments once the related code + supports them. */ + CORE_ADDR addr_low = 0, addr_high = 0; + +private: + /* The solib_ops responsible for this solib. */ + const solib_ops *m_ops; +}; + +/* A unique pointer to an solib. */ +using solib_up = std::unique_ptr<solib>; + +struct solib_ops +{ + virtual ~solib_ops () = default; + + /* Adjust the section binding addresses by the base address at + which the object was actually mapped. */ + virtual void relocate_section_addresses (solib &so, target_section *) const + = 0; + + /* Reset private data structures associated with SO. + This is called when SO is about to be reloaded. + It is also called when SO is about to be freed. + + Defaults to no-op. */ + virtual void clear_so (const solib &so) const {} + + /* Free private data structures associated to PSPACE. This method + should not free resources associated to individual solib entries, + those are cleared by the clear_so method. + + Defaults to no-op. */ + virtual void clear_solib (program_space *pspace) const {} + + /* Target dependent code to run after child process fork. + + Defaults to no-op. */ + virtual void create_inferior_hook (int from_tty) const {}; + + /* Construct a list of the currently loaded shared objects. This + list does not include an entry for the main executable file. + + Note that we only gather information directly available from the + inferior --- we don't examine any of the shared library files + themselves. The declaration of `struct solib' says which fields + we provide values for. */ + virtual owning_intrusive_list<solib> current_sos () const = 0; + + /* Find, open, and read the symbols for the main executable. If + FROM_TTY is non-zero, allow messages to be printed. + + Return true if this was done successfully. Defaults to false. */ + virtual bool open_symbol_file_object (int from_tty) const { return false; } + + /* Determine if PC lies in the dynamic symbol resolution code of + the run time loader. + + Defaults to false. */ + virtual bool in_dynsym_resolve_code (CORE_ADDR pc) const + { return false; }; + + /* Find and open shared library binary file. */ + virtual gdb_bfd_ref_ptr bfd_open (const char *pathname) const; + + /* Given two solib objects, GDB from the GDB thread list and INFERIOR from the + list returned by current_sos, return true if they represent the same library. + + Defaults to comparing the solib original names using filename_cmp. */ + virtual bool same (const solib &gdb, const solib &inferior) const; + + /* Return whether a region of memory must be kept in a core file + for shared libraries loaded before "gcore" is used to be + handled correctly when the core file is loaded. This only + applies when the section would otherwise not be kept in the + core file (in particular, for readonly sections). + + Defaults to false. */ + virtual bool keep_data_in_core (CORE_ADDR vaddr, unsigned long size) const + { return false; }; + + /* Enable or disable optional solib event breakpoints as appropriate. This + should be called whenever stop_on_solib_events is changed. + + Defaults to no-op. */ + virtual void update_breakpoints () const {}; + + /* Target-specific processing of solib events that will be performed before + solib_add is called. + + Defaults to no-op. */ + virtual void handle_event () const {}; + + /* Return an address within the inferior's address space which is known + to be part of SO. If there is no such address, or GDB doesn't know + how to figure out such an address then an empty optional is + returned. + + The returned address can be used when loading the shared libraries + for a core file. GDB knows the build-ids for (some) files mapped + into the inferior's address space, and knows the address ranges which + those mapped files cover. If GDB can figure out a representative + address for the library then this can be used to match a library to a + mapped file, and thus to a build-id. GDB can then use this + information to help locate the shared library objfile, if the objfile + is not in the expected place (as defined by the shared libraries file + name). + + The default implementation of returns an empty option, indicating GDB is + unable to find an address within the library SO. */ + virtual std::optional<CORE_ADDR> find_solib_addr (solib &so) const + { return {}; }; + + /* Return true if the linker or libc supports linkage namespaces. + + Defaults to false. */ + virtual bool supports_namespaces () const { return false; } + + /* Return which linker namespace contains SO. + + The supports_namespaces method must return true for this to be + called. + + Throw an error if the namespace can not be determined (such as when we're + stepping though the dynamic linker). */ + virtual int find_solib_ns (const solib &so) const + { gdb_assert_not_reached ("namespaces not supported"); } + + /* Returns the number of active namespaces in the inferior. + + The supports_namespaces method must return true for this to be called. */ + virtual int num_active_namespaces () const + { gdb_assert_not_reached ("namespaces not supported"); } + + /* Returns all solibs for a given namespace. If the namespace is not + active, returns an empty vector. + + The supports_namespaces method must return true for this to be called. */ + virtual std::vector<const solib *> get_solibs_in_ns (int ns) const + { gdb_assert_not_reached ("namespaces not supported"); } +}; + +/* A unique pointer to an solib_ops. */ +using solib_ops_up = std::unique_ptr<solib_ops>; + +/* Find main executable binary file. */ +extern gdb::unique_xmalloc_ptr<char> exec_file_find (const char *in_pathname, + int *fd); + +/* Find shared library binary file. */ +extern gdb::unique_xmalloc_ptr<char> solib_find (const char *in_pathname, + int *fd); + +/* Open BFD for shared library file. */ +extern gdb_bfd_ref_ptr solib_bfd_fopen (const char *pathname, int fd); + +/* Find solib binary file and open it. */ +extern gdb_bfd_ref_ptr solib_bfd_open (const char *in_pathname); + /* Called when we free all symtabs of PSPACE, to free the shared library information as well. */ @@ -89,7 +325,8 @@ extern void no_shared_libraries (program_space *pspace); Extract the list of currently loaded shared objects from the inferior, and compare it with the list of shared objects in the current program space's list of shared libraries. Edit - so_list_head to bring it in sync with the inferior's new list. + the current program space's solib list to bring it in sync with the + inferior's new list. If we notice that the inferior has unloaded some shared objects, free any symbolic info GDB had read about those shared objects. diff --git a/gdb/solist.h b/gdb/solist.h deleted file mode 100644 index 6ab5a06..0000000 --- a/gdb/solist.h +++ /dev/null @@ -1,225 +0,0 @@ -/* Shared library declarations for GDB, the GNU Debugger. - Copyright (C) 1990-2025 Free Software Foundation, Inc. - - This file is part of GDB. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#ifndef GDB_SOLIST_H -#define GDB_SOLIST_H - -#define SO_NAME_MAX_PATH_SIZE 512 /* FIXME: Should be dynamic */ - -/* For domain_enum domain. */ -#include "symtab.h" -#include "gdb_bfd.h" -#include "gdbsupport/owning_intrusive_list.h" -#include "target-section.h" - -/* Base class for target-specific link map information. */ - -struct lm_info -{ - lm_info () = default; - lm_info (const lm_info &) = default; - virtual ~lm_info () = 0; -}; - -using lm_info_up = std::unique_ptr<lm_info>; - -struct solib : intrusive_list_node<solib> -{ - /* Free symbol-file related contents of SO and reset for possible reloading - of SO. If we have opened a BFD for SO, close it. If we have placed SO's - sections in some target's section table, the caller is responsible for - removing them. - - This function doesn't mess with objfiles at all. If there is an - objfile associated with SO that needs to be removed, the caller is - responsible for taking care of that. */ - void clear () ; - - /* The following fields of the structure come directly from the - dynamic linker's tables in the inferior, and are initialized by - current_sos. */ - - /* A pointer to target specific link map information. Often this - will be a copy of struct link_map from the user process, but - it need not be; it can be any collection of data needed to - traverse the dynamic linker's data structures. */ - lm_info_up lm_info; - - /* Shared object file name, exactly as it appears in the - inferior's link map. This may be a relative path, or something - which needs to be looked up in LD_LIBRARY_PATH, etc. We use it - to tell which entries in the inferior's dynamic linker's link - map we've already loaded. */ - std::string so_original_name; - - /* Shared object file name, expanded to something GDB can open. */ - std::string so_name; - - /* The following fields of the structure are built from - information gathered from the shared object file itself, and - are set when we actually add it to our symbol tables. - - current_sos must initialize these fields to 0. */ - - gdb_bfd_ref_ptr abfd; - - /* True if symbols have been read in. */ - bool symbols_loaded = false; - - /* objfile with symbols for a loaded library. Target memory is read from - ABFD. OBJFILE may be NULL either before symbols have been loaded, if - the file cannot be found or after the command "nosharedlibrary". */ - struct objfile *objfile = nullptr; - - std::vector<target_section> sections; - - /* Record the range of addresses belonging to this shared library. - There may not be just one (e.g. if two segments are relocated - differently). This is used for "info sharedlibrary" and - the MI command "-file-list-shared-libraries". The latter has a format - that supports outputting multiple segments once the related code - supports them. */ - CORE_ADDR addr_low = 0, addr_high = 0; -}; - -struct solib_ops -{ - /* Adjust the section binding addresses by the base address at - which the object was actually mapped. */ - void (*relocate_section_addresses) (solib &so, target_section *); - - /* Reset private data structures associated with SO. - This is called when SO is about to be reloaded. - It is also called when SO is about to be freed. */ - void (*clear_so) (const solib &so); - - /* Free private data structures associated to PSPACE. This method - should not free resources associated to individual so_list entries, - those are cleared by the clear_so method. */ - void (*clear_solib) (program_space *pspace); - - /* Target dependent code to run after child process fork. */ - void (*solib_create_inferior_hook) (int from_tty); - - /* Construct a list of the currently loaded shared objects. This - list does not include an entry for the main executable file. - - Note that we only gather information directly available from the - inferior --- we don't examine any of the shared library files - themselves. The declaration of `struct solib' says which fields - we provide values for. */ - owning_intrusive_list<solib> (*current_sos) (); - - /* Find, open, and read the symbols for the main executable. If - FROM_TTY is non-zero, allow messages to be printed. */ - int (*open_symbol_file_object) (int from_ttyp); - - /* Determine if PC lies in the dynamic symbol resolution code of - the run time loader. */ - int (*in_dynsym_resolve_code) (CORE_ADDR pc); - - /* Find and open shared library binary file. */ - gdb_bfd_ref_ptr (*bfd_open) (const char *pathname); - - /* Given two so_list objects, one from the GDB thread list - and another from the list returned by current_sos, return 1 - if they represent the same library. - Falls back to using strcmp on so_original_name field when set - to NULL. */ - int (*same) (const solib &gdb, const solib &inferior); - - /* Return whether a region of memory must be kept in a core file - for shared libraries loaded before "gcore" is used to be - handled correctly when the core file is loaded. This only - applies when the section would otherwise not be kept in the - core file (in particular, for readonly sections). */ - int (*keep_data_in_core) (CORE_ADDR vaddr, - unsigned long size); - - /* Enable or disable optional solib event breakpoints as - appropriate. This should be called whenever - stop_on_solib_events is changed. This pointer can be - NULL, in which case no enabling or disabling is necessary - for this target. */ - void (*update_breakpoints) (void); - - /* Target-specific processing of solib events that will be - performed before solib_add is called. This pointer can be - NULL, in which case no specific preprocessing is necessary - for this target. */ - void (*handle_event) (void); - - /* Return an address within the inferior's address space which is known - to be part of SO. If there is no such address, or GDB doesn't know - how to figure out such an address then an empty optional is - returned. - - The returned address can be used when loading the shared libraries - for a core file. GDB knows the build-ids for (some) files mapped - into the inferior's address space, and knows the address ranges which - those mapped files cover. If GDB can figure out a representative - address for the library then this can be used to match a library to a - mapped file, and thus to a build-id. GDB can then use this - information to help locate the shared library objfile, if the objfile - is not in the expected place (as defined by the shared libraries file - name). */ - - std::optional<CORE_ADDR> (*find_solib_addr) (solib &so); - - /* Return which linker namespace contains the current so. - If the linker or libc does not support linkage namespaces at all - (which is basically all of them but solib-svr4), this function should - be set to nullptr, so that "info shared" won't add an unnecessary - column. - - If the namespace can not be determined (such as when we're stepping - though the dynamic linker), this function should throw a - gdb_exception_error. */ - int (*find_solib_ns) (const solib &so); - - /* Returns the number of active namespaces in the inferior. */ - int (*num_active_namespaces) (); - - /* Returns all solibs for a given namespace. If the namespace is not - active, returns an empty vector. */ - std::vector<const solib *> (*get_solibs_in_ns) (int ns); -}; - -/* A unique pointer to a so_list. */ -using solib_up = std::unique_ptr<solib>; - -/* Find main executable binary file. */ -extern gdb::unique_xmalloc_ptr<char> exec_file_find (const char *in_pathname, - int *fd); - -/* Find shared library binary file. */ -extern gdb::unique_xmalloc_ptr<char> solib_find (const char *in_pathname, - int *fd); - -/* Open BFD for shared library file. */ -extern gdb_bfd_ref_ptr solib_bfd_fopen (const char *pathname, int fd); - -/* Find solib binary file and open it. */ -extern gdb_bfd_ref_ptr solib_bfd_open (const char *in_pathname); - -/* A default implementation of the solib_ops::find_solib_addr callback. - This just returns an empty std::optional<CORE_ADDR> indicating GDB is - unable to find an address within the library SO. */ -extern std::optional<CORE_ADDR> default_find_solib_addr (solib &so); - -#endif /* GDB_SOLIST_H */ diff --git a/gdb/source-cache.c b/gdb/source-cache.c index ebe451d..9d5151a 100644 --- a/gdb/source-cache.c +++ b/gdb/source-cache.c @@ -511,9 +511,7 @@ static void extract_lines_test () } #endif -void _initialize_source_cache (); -void -_initialize_source_cache () +INIT_GDB_FILE (source_cache) { add_cmd ("source-cache", class_maintenance, source_cache_flush_command, _("Force gdb to flush its source code cache."), diff --git a/gdb/source.c b/gdb/source.c index 13cb8d6..0fd370b 100644 --- a/gdb/source.c +++ b/gdb/source.c @@ -1910,9 +1910,7 @@ source_lines_range::source_lines_range (int startline, } -void _initialize_source (); -void -_initialize_source () +INIT_GDB_FILE (source) { init_source_path (); diff --git a/gdb/sparc-linux-nat.c b/gdb/sparc-linux-nat.c index 8dccce9..18db599 100644 --- a/gdb/sparc-linux-nat.c +++ b/gdb/sparc-linux-nat.c @@ -65,9 +65,7 @@ fill_fpregset (const struct regcache *regcache, sparc32_collect_fpregset (sparc_fpregmap, regcache, regnum, fpregs); } -void _initialize_sparc_linux_nat (); -void -_initialize_sparc_linux_nat () +INIT_GDB_FILE (sparc_linux_nat) { sparc_fpregmap = &sparc32_bsd_fpregmap; diff --git a/gdb/sparc-linux-tdep.c b/gdb/sparc-linux-tdep.c index 27706b7..6e23fdd 100644 --- a/gdb/sparc-linux-tdep.c +++ b/gdb/sparc-linux-tdep.c @@ -33,6 +33,7 @@ #include "tramp-frame.h" #include "xml-syscall.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" /* The syscall's XML filename for sparc 32-bit. */ #define XML_SYSCALL_FILENAME_SPARC32 "syscalls/sparc-linux.xml" @@ -436,8 +437,7 @@ sparc32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* GNU/Linux has SVR4-style shared libraries... */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); /* ...which means that we need some special handling when doing prologue analysis. */ @@ -466,9 +466,7 @@ sparc32_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) sparc32_linux_gdb_signal_to_target); } -void _initialize_sparc_linux_tdep (); -void -_initialize_sparc_linux_tdep () +INIT_GDB_FILE (sparc_linux_tdep) { gdbarch_register_osabi (bfd_arch_sparc, 0, GDB_OSABI_LINUX, sparc32_linux_init_abi); diff --git a/gdb/sparc-nat.c b/gdb/sparc-nat.c index c2d3833..1219612 100644 --- a/gdb/sparc-nat.c +++ b/gdb/sparc-nat.c @@ -309,9 +309,7 @@ sparc_xfer_wcookie (enum target_object object, } -void _initialize_sparc_nat (); -void -_initialize_sparc_nat () +INIT_GDB_FILE (sparc_nat) { /* Default to using SunOS 4 register sets. */ if (sparc_gregmap == NULL) diff --git a/gdb/sparc-netbsd-nat.c b/gdb/sparc-netbsd-nat.c index 8eee741..54cba0b 100644 --- a/gdb/sparc-netbsd-nat.c +++ b/gdb/sparc-netbsd-nat.c @@ -56,9 +56,7 @@ sparc32nbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) static sparc_target<inf_ptrace_target> the_sparc_nbsd_nat_target; -void _initialize_sparcnbsd_nat (); -void -_initialize_sparcnbsd_nat () +INIT_GDB_FILE (sparcnbsd_nat) { sparc_gregmap = &sparc32nbsd_gregmap; sparc_fpregmap = &sparc32_bsd_fpregmap; diff --git a/gdb/sparc-netbsd-tdep.c b/gdb/sparc-netbsd-tdep.c index 84e3a97..8e8a1ba 100644 --- a/gdb/sparc-netbsd-tdep.c +++ b/gdb/sparc-netbsd-tdep.c @@ -313,13 +313,10 @@ sparc32nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) frame_unwind_append_unwinder (gdbarch, &sparc32nbsd_sigcontext_frame_unwind); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); } -void _initialize_sparcnbsd_tdep (); -void -_initialize_sparcnbsd_tdep () +INIT_GDB_FILE (sparcnbsd_tdep) { gdbarch_register_osabi (bfd_arch_sparc, 0, GDB_OSABI_NETBSD, sparc32nbsd_init_abi); diff --git a/gdb/sparc-obsd-tdep.c b/gdb/sparc-obsd-tdep.c index 3486799..ff6e915 100644 --- a/gdb/sparc-obsd-tdep.c +++ b/gdb/sparc-obsd-tdep.c @@ -254,9 +254,7 @@ sparc32obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) bsd_uthread_set_collect_uthread (gdbarch, sparc32obsd_collect_uthread); } -void _initialize_sparc32obsd_tdep (); -void -_initialize_sparc32obsd_tdep () +INIT_GDB_FILE (sparc32obsd_tdep) { gdbarch_register_osabi (bfd_arch_sparc, 0, GDB_OSABI_OPENBSD, sparc32obsd_init_abi); diff --git a/gdb/sparc-sol2-tdep.c b/gdb/sparc-sol2-tdep.c index 5c6085a..337b929 100644 --- a/gdb/sparc-sol2-tdep.c +++ b/gdb/sparc-sol2-tdep.c @@ -207,8 +207,7 @@ sparc32_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Solaris has SVR4-style shared libraries... */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); /* ...which means that we need some special handling when doing prologue analysis. */ @@ -220,9 +219,7 @@ sparc32_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) frame_unwind_append_unwinder (gdbarch, &sparc32_sol2_sigtramp_frame_unwind); } -void _initialize_sparc_sol2_tdep (); -void -_initialize_sparc_sol2_tdep () +INIT_GDB_FILE (sparc_sol2_tdep) { gdbarch_register_osabi (bfd_arch_sparc, 0, GDB_OSABI_SOLARIS, sparc32_sol2_init_abi); diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c index 80914d9..4a12516 100644 --- a/gdb/sparc-tdep.c +++ b/gdb/sparc-tdep.c @@ -2264,9 +2264,7 @@ const struct sparc_fpregmap sparc32_bsd_fpregmap = 32 * 4, /* %fsr */ }; -void _initialize_sparc_tdep (); -void -_initialize_sparc_tdep () +INIT_GDB_FILE (sparc_tdep) { gdbarch_register (bfd_arch_sparc, sparc32_gdbarch_init); } diff --git a/gdb/sparc64-fbsd-nat.c b/gdb/sparc64-fbsd-nat.c index 4848c77..d697841 100644 --- a/gdb/sparc64-fbsd-nat.c +++ b/gdb/sparc64-fbsd-nat.c @@ -61,9 +61,7 @@ sparc64fbsd_kvm_supply_pcb (struct regcache *regcache, struct pcb *pcb) /* Add some extra features to the generic SPARC target. */ static sparc_target<fbsd_nat_target> the_sparc64_fbsd_nat_target; -void _initialize_sparc64fbsd_nat (); -void -_initialize_sparc64fbsd_nat () +INIT_GDB_FILE (sparc64fbsd_nat) { add_inf_child_target (&the_sparc64_fbsd_nat_target); diff --git a/gdb/sparc64-fbsd-tdep.c b/gdb/sparc64-fbsd-tdep.c index 738c3d1..ba04a71 100644 --- a/gdb/sparc64-fbsd-tdep.c +++ b/gdb/sparc64-fbsd-tdep.c @@ -238,13 +238,10 @@ sparc64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* FreeBSD/sparc64 has SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); } -void _initialize_sparc64fbsd_tdep (); -void -_initialize_sparc64fbsd_tdep () +INIT_GDB_FILE (sparc64fbsd_tdep) { gdbarch_register_osabi (bfd_arch_sparc, bfd_mach_sparc_v9, GDB_OSABI_FREEBSD, sparc64fbsd_init_abi); diff --git a/gdb/sparc64-linux-nat.c b/gdb/sparc64-linux-nat.c index 9a4b228..2884faa 100644 --- a/gdb/sparc64-linux-nat.c +++ b/gdb/sparc64-linux-nat.c @@ -87,9 +87,7 @@ fill_fpregset (const struct regcache *regcache, sparc64_collect_fpregset (&sparc64_bsd_fpregmap, regcache, regnum, fpregs); } -void _initialize_sparc64_linux_nat (); -void -_initialize_sparc64_linux_nat () +INIT_GDB_FILE (sparc64_linux_nat) { sparc_fpregmap = &sparc64_bsd_fpregmap; diff --git a/gdb/sparc64-linux-tdep.c b/gdb/sparc64-linux-tdep.c index 6e7281c..373dfb6 100644 --- a/gdb/sparc64-linux-tdep.c +++ b/gdb/sparc64-linux-tdep.c @@ -32,6 +32,7 @@ #include "tramp-frame.h" #include "xml-syscall.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" /* ADI specific si_code */ #ifndef SEGV_ACCADI @@ -383,8 +384,7 @@ sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* GNU/Linux has SVR4-style shared libraries... */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops); /* ...which means that we need some special handling when doing prologue analysis. */ @@ -409,9 +409,7 @@ sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_report_signal_info (gdbarch, sparc64_linux_report_signal_info); } -void _initialize_sparc64_linux_tdep (); -void -_initialize_sparc64_linux_tdep () +INIT_GDB_FILE (sparc64_linux_tdep) { gdbarch_register_osabi (bfd_arch_sparc, bfd_mach_sparc_v9, GDB_OSABI_LINUX, sparc64_linux_init_abi); diff --git a/gdb/sparc64-nat.c b/gdb/sparc64-nat.c index 6bb37d8..66451a0 100644 --- a/gdb/sparc64-nat.c +++ b/gdb/sparc64-nat.c @@ -68,9 +68,7 @@ sparc64_fpregset_supplies_p (struct gdbarch *gdbarch, int regnum) return 0; } -void _initialize_sparc64_nat (); -void -_initialize_sparc64_nat () +INIT_GDB_FILE (sparc64_nat) { sparc_supply_gregset = sparc64_supply_gregset; sparc_collect_gregset = sparc64_collect_gregset; diff --git a/gdb/sparc64-netbsd-nat.c b/gdb/sparc64-netbsd-nat.c index c4bec4c..1036160 100644 --- a/gdb/sparc64-netbsd-nat.c +++ b/gdb/sparc64-netbsd-nat.c @@ -169,9 +169,7 @@ sparc64nbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) /* We've got nothing to add to the generic SPARC target. */ static sparc_target<inf_ptrace_target> the_sparc64_nbsd_nat_target; -void _initialize_sparc64nbsd_nat (); -void -_initialize_sparc64nbsd_nat () +INIT_GDB_FILE (sparc64nbsd_nat) { sparc_supply_gregset = sparc64nbsd_supply_gregset; sparc_collect_gregset = sparc64nbsd_collect_gregset; diff --git a/gdb/sparc64-netbsd-tdep.c b/gdb/sparc64-netbsd-tdep.c index 1919598..0227ec4 100644 --- a/gdb/sparc64-netbsd-tdep.c +++ b/gdb/sparc64-netbsd-tdep.c @@ -266,13 +266,10 @@ sparc64nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* NetBSD/sparc64 has SVR4-style shared libraries. */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); } -void _initialize_sparc64nbsd_tdep (); -void -_initialize_sparc64nbsd_tdep () +INIT_GDB_FILE (sparc64nbsd_tdep) { gdbarch_register_osabi (bfd_arch_sparc, bfd_mach_sparc_v9, GDB_OSABI_NETBSD, sparc64nbsd_init_abi); diff --git a/gdb/sparc64-obsd-nat.c b/gdb/sparc64-obsd-nat.c index 5f9bd46..60dd188 100644 --- a/gdb/sparc64-obsd-nat.c +++ b/gdb/sparc64-obsd-nat.c @@ -108,9 +108,7 @@ sparc64obsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) /* Add some extra features to the generic SPARC target. */ static sparc_target<obsd_nat_target> the_sparc64_obsd_nat_target; -void _initialize_sparc64obsd_nat (); -void -_initialize_sparc64obsd_nat () +INIT_GDB_FILE (sparc64obsd_nat) { sparc_supply_gregset = sparc64_supply_gregset; sparc_collect_gregset = sparc64_collect_gregset; diff --git a/gdb/sparc64-obsd-tdep.c b/gdb/sparc64-obsd-tdep.c index 657f548..83cc9c0 100644 --- a/gdb/sparc64-obsd-tdep.c +++ b/gdb/sparc64-obsd-tdep.c @@ -440,8 +440,7 @@ sparc64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) obsd_init_abi (info, gdbarch); /* OpenBSD/sparc64 has SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); set_gdbarch_skip_solib_resolver (gdbarch, obsd_skip_solib_resolver); /* OpenBSD provides a user-level threads implementation. */ @@ -449,9 +448,7 @@ sparc64obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) bsd_uthread_set_collect_uthread (gdbarch, sparc64obsd_collect_uthread); } -void _initialize_sparc64obsd_tdep (); -void -_initialize_sparc64obsd_tdep () +INIT_GDB_FILE (sparc64obsd_tdep) { gdbarch_register_osabi (bfd_arch_sparc, bfd_mach_sparc_v9, GDB_OSABI_OPENBSD, sparc64obsd_init_abi); diff --git a/gdb/sparc64-sol2-tdep.c b/gdb/sparc64-sol2-tdep.c index 72a07ec..6d091f5 100644 --- a/gdb/sparc64-sol2-tdep.c +++ b/gdb/sparc64-sol2-tdep.c @@ -214,8 +214,7 @@ sparc64_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Solaris has SVR4-style shared libraries... */ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_lp64_solib_ops); /* ...which means that we need some special handling when doing prologue analysis. */ @@ -225,9 +224,7 @@ sparc64_sol2_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_software_single_step (gdbarch, NULL); } -void _initialize_sparc64_sol2_tdep (); -void -_initialize_sparc64_sol2_tdep () +INIT_GDB_FILE (sparc64_sol2_tdep) { gdbarch_register_osabi (bfd_arch_sparc, bfd_mach_sparc_v9, GDB_OSABI_SOLARIS, sparc64_sol2_init_abi); diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c index ee7d7ed..6846421 100644 --- a/gdb/sparc64-tdep.c +++ b/gdb/sparc64-tdep.c @@ -529,9 +529,7 @@ adi_assign_command (const char *args, int from_tty) do_assign (next_address, cnt, version); } -void _initialize_sparc64_adi_tdep (); -void -_initialize_sparc64_adi_tdep () +INIT_GDB_FILE (sparc64_adi_tdep) { add_basic_prefix_cmd ("adi", class_support, _("ADI version related commands."), diff --git a/gdb/stabsread.c b/gdb/stabsread.c index 409f303..6ee61e0 100644 --- a/gdb/stabsread.c +++ b/gdb/stabsread.c @@ -7200,9 +7200,7 @@ hashname (const char *name) /* Initializer for this module. */ -void _initialize_stabsread (); -void -_initialize_stabsread () +INIT_GDB_FILE (stabsread) { undef_types_allocated = 20; undef_types_length = 0; diff --git a/gdb/stack.c b/gdb/stack.c index 6542840..e633566 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -3262,9 +3262,7 @@ static struct cmd_list_element *select_frame_cmd_list = NULL; /* Commands with a prefix of `info frame'. */ static struct cmd_list_element *info_frame_cmd_list = NULL; -void _initialize_stack (); -void -_initialize_stack () +INIT_GDB_FILE (stack) { struct cmd_list_element *cmd; diff --git a/gdb/stap-probe.c b/gdb/stap-probe.c index 8cc7599..3b692e2 100644 --- a/gdb/stap-probe.c +++ b/gdb/stap-probe.c @@ -1748,9 +1748,7 @@ info_probes_stap_command (const char *arg, int from_tty) info_probes_for_spops (arg, from_tty, &stap_static_probe_ops); } -void _initialize_stap_probe (); -void -_initialize_stap_probe () +INIT_GDB_FILE (stap_probe) { all_static_probe_ops.push_back (&stap_static_probe_ops); diff --git a/gdb/std-regs.c b/gdb/std-regs.c index 2a3f93d..9940cb0 100644 --- a/gdb/std-regs.c +++ b/gdb/std-regs.c @@ -93,9 +93,7 @@ value_of_builtin_frame_ps_reg (const frame_info_ptr &frame, const void *baton) error (_("Standard register ``$ps'' is not available for this target")); } -void _initialize_frame_reg (); -void -_initialize_frame_reg () +INIT_GDB_FILE (frame_reg) { /* Frame based $fp, $pc, $sp and $ps. These only come into play when the target does not define its own version of these diff --git a/gdb/svr4-tls-tdep.c b/gdb/svr4-tls-tdep.c new file mode 100644 index 0000000..75d06a4 --- /dev/null +++ b/gdb/svr4-tls-tdep.c @@ -0,0 +1,254 @@ +/* Target-dependent code for GNU/Linux, architecture independent. + + Copyright (C) 2009-2024 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "svr4-tls-tdep.h" +#include "solib-svr4.h" +#include "inferior.h" +#include "objfiles.h" +#include "cli/cli-cmds.h" +#include <optional> + +struct svr4_tls_gdbarch_data +{ + /* Method for looking up TLS DTV. */ + get_tls_dtv_addr_ftype *get_tls_dtv_addr = nullptr; + + /* Method for looking up the TLS DTP offset. */ + get_tls_dtp_offset_ftype *get_tls_dtp_offset = nullptr; + + /* Cached libc value for TLS lookup purposes. */ + enum svr4_tls_libc libc = svr4_tls_libc_unknown; +}; + +static const registry<gdbarch>::key<svr4_tls_gdbarch_data> + svr4_tls_gdbarch_data_handle; + +static struct svr4_tls_gdbarch_data * +get_svr4_tls_gdbarch_data (struct gdbarch *gdbarch) +{ + struct svr4_tls_gdbarch_data *result = svr4_tls_gdbarch_data_handle.get (gdbarch); + if (result == nullptr) + result = svr4_tls_gdbarch_data_handle.emplace (gdbarch); + return result; +} + +/* When true, force internal TLS address lookup instead of lookup via + the thread stratum. */ + +static bool force_internal_tls_address_lookup = false; + +/* For TLS lookup purposes, use heuristics to decide whether program + was linked against MUSL or GLIBC. */ + +static enum svr4_tls_libc +libc_tls_sniffer (struct gdbarch *gdbarch) +{ + /* Check for cached libc value. */ + svr4_tls_gdbarch_data *gdbarch_data = get_svr4_tls_gdbarch_data (gdbarch); + if (gdbarch_data->libc != svr4_tls_libc_unknown) + return gdbarch_data->libc; + + svr4_tls_libc libc = svr4_tls_libc_unknown; + + /* Fetch the program interpreter. */ + std::optional<gdb::byte_vector> interp_name_holder + = svr4_find_program_interpreter (); + if (interp_name_holder) + { + /* A dynamically linked program linked against MUSL will have a + "ld-musl-" in its interpreter name. (Two examples of MUSL + interpreter names are "/lib/ld-musl-x86_64.so.1" and + "lib/ld-musl-aarch64.so.1".) If it's not found, assume GLIBC. */ + const char *interp_name = (const char *) interp_name_holder->data (); + if (strstr (interp_name, "/ld-musl-") != nullptr) + libc = svr4_tls_libc_musl; + else + libc = svr4_tls_libc_glibc; + gdbarch_data->libc = libc; + return libc; + } + + /* If there is no interpreter name, it's statically linked. For + programs with TLS data, a program statically linked against MUSL + will have the symbols 'main_tls' and 'builtin_tls'. If both of + these are present, assume that it was statically linked against + MUSL, otherwise assume GLIBC. */ + if (lookup_minimal_symbol (current_program_space, "main_tls").minsym + != nullptr + && lookup_minimal_symbol (current_program_space, "builtin_tls").minsym + != nullptr) + libc = svr4_tls_libc_musl; + else + libc = svr4_tls_libc_glibc; + gdbarch_data->libc = libc; + return libc; +} + +/* Implement gdbarch method, get_thread_local_address, for architectures + which provide a method for determining the DTV and possibly the DTP + offset. */ + +CORE_ADDR +svr4_tls_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, + CORE_ADDR lm_addr, CORE_ADDR offset) +{ + svr4_tls_gdbarch_data *gdbarch_data = get_svr4_tls_gdbarch_data (gdbarch); + + /* Use the target's get_thread_local_address method when: + + - No method has been provided for finding the TLS DTV. + + or + + - The thread stratum has been pushed (at some point) onto the + target stack, except when 'force_internal_tls_address_lookup' + has been set. + + The idea here is to prefer use of of the target's thread_stratum + method since it should be more accurate. */ + if (gdbarch_data->get_tls_dtv_addr == nullptr + || (find_target_at (thread_stratum) != nullptr + && !force_internal_tls_address_lookup)) + { + struct target_ops *target = current_inferior ()->top_target (); + return target->get_thread_local_address (ptid, lm_addr, offset); + } + else + { + /* Details, found below, regarding TLS layout is for the GNU C + library (glibc) and the MUSL C library (musl), circa 2024. + While some of this layout is defined by the TLS ABI, some of + it, such as how/where to find the DTV pointer in the TCB, is + not. A good source of ABI info for some architectures can be + found in "ELF Handling For Thread-Local Storage" by Ulrich + Drepper. That document is worth consulting even for + architectures not described there, since the general approach + and terminology is used regardless. + + Some architectures, such as aarch64, are not described in + that document, so some details had to ferreted out using the + glibc source code. Likewise, the MUSL source code was + consulted for details which differ from GLIBC. */ + enum svr4_tls_libc libc = libc_tls_sniffer (gdbarch); + int mod_id; + if (libc == svr4_tls_libc_glibc) + mod_id = glibc_link_map_to_tls_module_id (lm_addr); + else /* Assume MUSL. */ + mod_id = musl_link_map_to_tls_module_id (lm_addr); + if (mod_id == 0) + throw_error (TLS_GENERIC_ERROR, _("Unable to determine TLS module id")); + + /* Use the architecture specific DTV fetcher to obtain the DTV. */ + CORE_ADDR dtv_addr = gdbarch_data->get_tls_dtv_addr (gdbarch, ptid, libc); + + /* In GLIBC, The DTV (dynamic thread vector) is an array of + structs consisting of two fields, the first of which is a + pointer to the TLS block of interest. (The second field is a + pointer that assists with memory management, but that's not + of interest here.) Also, the 0th entry is the generation + number, but although it's a single scalar, the 0th entry is + padded to be the same size as all the rest. Thus each + element of the DTV array is two pointers in size. + + In MUSL, the DTV is simply an array of pointers. The 0th + entry is still the generation number, but contains no padding + aside from that which is needed to make it pointer sized. */ + int m; /* Multiplier, for size of DTV entry. */ + switch (libc) + { + case svr4_tls_libc_glibc: + m = 2; + break; + default: + m = 1; + break; + } + + /* Obtain TLS block address. Module ids start at 1, so there's + no need to adjust it to skip over the 0th entry of the DTV, + which is the generation number. */ + CORE_ADDR dtv_elem_addr + = dtv_addr + mod_id * m * (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + gdb::byte_vector buf (gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT); + if (target_read_memory (dtv_elem_addr, buf.data (), buf.size ()) != 0) + throw_error (TLS_GENERIC_ERROR, _("Unable to fetch TLS block address")); + const struct builtin_type *builtin = builtin_type (gdbarch); + CORE_ADDR tls_block_addr = gdbarch_pointer_to_address + (gdbarch, builtin->builtin_data_ptr, + buf.data ()); + + /* When the TLS block addr is 0 or -1, this usually indicates that + the TLS storage hasn't been allocated yet. (In GLIBC, some + architectures use 0 while others use -1.) */ + if (tls_block_addr == 0 || tls_block_addr == (CORE_ADDR) -1) + throw_error (TLS_NOT_ALLOCATED_YET_ERROR, _("TLS not allocated yet")); + + /* MUSL (and perhaps other C libraries, though not GLIBC) have + TLS implementations for some architectures which, for some + reason, have DTV entries which must be negatively offset by + DTP_OFFSET in order to obtain the TLS block address. + DTP_OFFSET is a constant in the MUSL sources - these offsets, + when they're non-zero, seem to be either 0x800 or 0x8000, + and are present for riscv[32/64], powerpc[32/64], m68k, and + mips. + + Use the architecture specific get_tls_dtp_offset method, if + present, to obtain this offset. */ + ULONGEST dtp_offset + = gdbarch_data->get_tls_dtp_offset == nullptr + ? 0 + : gdbarch_data->get_tls_dtp_offset (gdbarch, ptid, libc); + + return tls_block_addr - dtp_offset + offset; + } +} + +/* See svr4-tls-tdep.h. */ + +void +svr4_tls_register_tls_methods (struct gdbarch_info info, struct gdbarch *gdbarch, + get_tls_dtv_addr_ftype *get_tls_dtv_addr, + get_tls_dtp_offset_ftype *get_tls_dtp_offset) +{ + gdb_assert (get_tls_dtv_addr != nullptr); + + svr4_tls_gdbarch_data *gdbarch_data = get_svr4_tls_gdbarch_data (gdbarch); + gdbarch_data->get_tls_dtv_addr = get_tls_dtv_addr; + gdbarch_data->get_tls_dtp_offset = get_tls_dtp_offset; +} + +INIT_GDB_FILE (svr4_tls_tdep) +{ + add_setshow_boolean_cmd ("force-internal-tls-address-lookup", class_obscure, + &force_internal_tls_address_lookup, _("\ +Set to force internal TLS address lookup."), _("\ +Show whether GDB is forced to use internal TLS address lookup."), _("\ +When resolving addresses for TLS (Thread Local Storage) variables,\n\ +GDB will attempt to use facilities provided by the thread library (i.e.\n\ +libthread_db). If those facilities aren't available, GDB will fall\n\ +back to using some internal (to GDB), but possibly less accurate\n\ +mechanisms to resolve the addresses for TLS variables. When this flag\n\ +is set, GDB will force use of the fall-back TLS resolution mechanisms.\n\ +This flag is used by some GDB tests to ensure that the internal fallback\n\ +code is exercised and working as expected. The default is to not force\n\ +the internal fall-back mechanisms to be used."), + NULL, NULL, + &maintenance_set_cmdlist, + &maintenance_show_cmdlist); +} diff --git a/gdb/svr4-tls-tdep.h b/gdb/svr4-tls-tdep.h new file mode 100644 index 0000000..73efc02 --- /dev/null +++ b/gdb/svr4-tls-tdep.h @@ -0,0 +1,59 @@ +/* Target-dependent code for GNU/Linux, architecture independent. + + 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/>. */ + +#ifndef GDB_SVR4_TLS_TDEP_H +#define GDB_SVR4_TLS_TDEP_H + +/* C library variants for TLS lookup. */ + +enum svr4_tls_libc +{ + svr4_tls_libc_unknown, + svr4_tls_libc_musl, + svr4_tls_libc_glibc +}; + +/* Function type for "get_tls_dtv_addr" method. */ + +typedef CORE_ADDR (get_tls_dtv_addr_ftype) (struct gdbarch *gdbarch, + ptid_t ptid, + enum svr4_tls_libc libc); + +/* Function type for "get_tls_dtp_offset" method. */ + +typedef CORE_ADDR (get_tls_dtp_offset_ftype) (struct gdbarch *gdbarch, + ptid_t ptid, + enum svr4_tls_libc libc); + +/* Register architecture specific methods for fetching the TLS DTV + and TLS DTP, used by linux_get_thread_local_address. */ + +extern void svr4_tls_register_tls_methods + (struct gdbarch_info info, struct gdbarch *gdbarch, + get_tls_dtv_addr_ftype *get_tls_dtv_addr, + get_tls_dtp_offset_ftype *get_tls_dtp_offset = nullptr); + +/* Used as a gdbarch method for get_thread_local_address when the tdep + file also defines a suitable method for obtaining the TLS DTV. + See linux_init_abi(), above. */ +CORE_ADDR +svr4_tls_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid, + CORE_ADDR lm_addr, CORE_ADDR offset); + +#endif /* GDB_SVR4_TLS_TDEP_H */ diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c index 9c5ce85..a5ff21e 100644 --- a/gdb/symfile-debug.c +++ b/gdb/symfile-debug.c @@ -895,9 +895,7 @@ show_debug_symfile (struct ui_file *file, int from_tty, gdb_printf (file, _("Symfile debugging is %s.\n"), value); } -void _initialize_symfile_debug (); -void -_initialize_symfile_debug () +INIT_GDB_FILE (symfile_debug) { add_setshow_boolean_cmd ("symfile", no_class, &debug_symfile, _("\ Set debugging of the symfile functions."), _("\ diff --git a/gdb/symfile-mem.c b/gdb/symfile-mem.c index 92672d7..274dc0f 100644 --- a/gdb/symfile-mem.c +++ b/gdb/symfile-mem.c @@ -204,9 +204,7 @@ add_vsyscall_page (inferior *inf) } } -void _initialize_symfile_mem (); -void -_initialize_symfile_mem () +INIT_GDB_FILE (symfile_mem) { add_cmd ("add-symbol-file-from-memory", class_files, add_symbol_file_from_memory_command, diff --git a/gdb/symfile.c b/gdb/symfile.c index 9a6322d..0e47f50 100644 --- a/gdb/symfile.c +++ b/gdb/symfile.c @@ -1101,7 +1101,7 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name, no separate debug file. If there is a separate debug file which does not have symbols, we'll have emitted this message for that file, and so printing it twice is just redundant. */ - if (should_print && !objfile_has_symbols (objfile) + if (should_print && !objfile->has_symbols () && objfile->separate_debug_objfile == nullptr) gdb_printf (_("(No debugging symbols found in %ps)\n"), styled_string (file_name_style.style (), name)); @@ -2323,7 +2323,7 @@ add_symbol_file_command (const char *args, int from_tty) objf = symbol_file_add (filename.get (), add_flags, §ion_addrs, flags); - if (!objfile_has_symbols (objf) && objf->per_bfd->minimal_symbol_count <= 0) + if (!objf->has_symbols () && objf->per_bfd->minimal_symbol_count <= 0) warning (_("newly-added symbol file \"%ps\" does not provide any symbols"), styled_string (file_name_style.style (), filename.get ())); @@ -2662,7 +2662,7 @@ reread_symbols (int from_tty) objfile->expand_all_symtabs (); } - if (!objfile_has_symbols (objfile)) + if (!objfile->has_symbols ()) { gdb_stdout->wrap_here (0); gdb_printf (_("(no debugging symbols found)\n")); @@ -3849,9 +3849,7 @@ test_set_ext_lang_command () #endif /* GDB_SELF_TEST */ -void _initialize_symfile (); -void -_initialize_symfile () +INIT_GDB_FILE (symfile) { struct cmd_list_element *c; diff --git a/gdb/symmisc.c b/gdb/symmisc.c index 8a95958..0cb6a1b 100644 --- a/gdb/symmisc.c +++ b/gdb/symmisc.c @@ -1051,9 +1051,7 @@ maintenance_info_line_tables (const char *regexp, int from_tty) /* Do early runtime initializations. */ -void _initialize_symmisc (); -void -_initialize_symmisc () +INIT_GDB_FILE (symmisc) { add_cmd ("symbols", class_maintenance, maintenance_print_symbols, _("\ Print dump of current symbol definitions.\n\ diff --git a/gdb/symtab.c b/gdb/symtab.c index 5147aee..7d1a0b0 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -60,7 +60,6 @@ #include "cp-abi.h" #include "cp-support.h" #include "observable.h" -#include "solist.h" #include "macrotab.h" #include "macroscope.h" @@ -2163,13 +2162,6 @@ lookup_symbol_aux (const char *name, symbol_name_match_type match_type, domain_name (domain).c_str (), language_str (language)); } - /* Make sure we do something sensible with is_a_field_of_this, since - the callers that set this parameter to some non-null value will - certainly use it later. If we don't set it, the contents of - is_a_field_of_this are undefined. */ - if (is_a_field_of_this != NULL) - memset (is_a_field_of_this, 0, sizeof (*is_a_field_of_this)); - langdef = language_def (language); /* Search specified block and its superiors. Don't search @@ -4499,7 +4491,7 @@ info_sources_filter::matches (const char *fullname) const switch (m_match_type) { case match_on::DIRNAME: - dirname = ldirname (fullname); + dirname = gdb_ldirname (fullname); to_match = dirname.c_str (); break; case match_on::BASENAME: @@ -4714,7 +4706,7 @@ info_sources_worker (struct ui_out *uiout, if (uiout->is_mi_like_p ()) { const char *debug_info_state; - if (objfile_has_symbols (objfile)) + if (objfile->has_symbols ()) { if (debug_fully_readin) debug_info_state = "fully-read"; @@ -4730,7 +4722,7 @@ info_sources_worker (struct ui_out *uiout, if (!debug_fully_readin) uiout->text ("(Full debug information has not yet been read " "for this file.)\n"); - if (!objfile_has_symbols (objfile)) + if (!objfile->has_symbols ()) uiout->text ("(Objfile has no debug information.)\n"); uiout->text ("\n"); } @@ -6206,8 +6198,6 @@ default_collect_symbol_completion_matches_break_on if (current_language->macro_expansion () == macro_expansion_c && code == TYPE_CODE_UNDEF) { - gdb::unique_xmalloc_ptr<struct macro_scope> scope; - /* This adds a macro's name to the current completion list. */ auto add_macro_name = [&] (const char *macro_name, const macro_definition *, @@ -6225,10 +6215,9 @@ default_collect_symbol_completion_matches_break_on resulting expression will be evaluated at "file:line" -- but at there does not seem to be a way to detect this at completion time. */ - scope = default_macro_scope (); - if (scope) - macro_for_each_in_scope (scope->file, scope->line, - add_macro_name); + macro_scope scope = default_macro_scope (); + if (scope.is_valid ()) + macro_for_each_in_scope (scope.file, scope.line, add_macro_name); /* User-defined macros are always visible. */ macro_for_each (macro_user_macros, add_macro_name); @@ -7120,9 +7109,7 @@ info_module_var_func_command_completer (struct cmd_list_element *ignore, -void _initialize_symtab (); -void -_initialize_symtab () +INIT_GDB_FILE (symtab) { cmd_list_element *c; diff --git a/gdb/symtab.h b/gdb/symtab.h index e547d10..0a57be5 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2101,17 +2101,17 @@ struct field_of_this_result symbol was not found in 'this'. If non-NULL, then one of the other fields will be non-NULL as well. */ - struct type *type; + struct type *type = nullptr; /* If the symbol was found as an ordinary field of 'this', then this is non-NULL and points to the particular field. */ - struct field *field; + struct field *field = nullptr; /* If the symbol was found as a function field of 'this', then this is non-NULL and points to the particular field. */ - struct fn_fieldlist *fn_field; + struct fn_fieldlist *fn_field = nullptr; }; /* Find the definition for a specified symbol name NAME diff --git a/gdb/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py index 30e52b7..40039bb 100755 --- a/gdb/syscalls/riscv-canonicalize-syscall-gen.py +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py @@ -82,51 +82,59 @@ tail = """\ class Generator: - def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]: - gdb_syscalls: list[str] = [] - with open(gdb_syscalls_path, "r", encoding="UTF-8") as file: - lines = file.readlines() - for line in lines: - match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line) - if match: - gdb_syscalls.append(match.group("name").strip()) - return gdb_syscalls - - def _get_canon_syscalls_lines(self, syscalls_path: _Path, gdb_syscalls: list[str]) -> list[str]: - canon_syscalls: dict[int, str] = {} - with open(syscalls_path, "r", encoding="UTF-8") as file: - lines = file.readlines() - for line in lines: - match = re.match(r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line) - if match: - syscall_name = match.group("name") - syscall_num = int(match.group("number")) - gdb_syscall_name = f"gdb_sys_{syscall_name}" - if gdb_syscall_name in gdb_syscalls: - value = f" case {syscall_num}: return {gdb_syscall_name};\n" - canon_syscalls[syscall_num] = value - # this is a place for corner cases - elif syscall_name == "mmap": - gdb_old_syscall_name = "gdb_old_mmap" - value = f" case {syscall_num}: return {gdb_old_syscall_name};\n" - canon_syscalls[syscall_num] = value - else: - value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n" - canon_syscalls[syscall_num] = value - return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)] - - def generate(self, syscalls_path: _Path) -> None: - repo_path = _Path(__file__).parent.parent.parent - gdb_syscalls_path = repo_path / "gdb" / "linux-record.h" - canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c" - - gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path) - canon_syscalls_lines = self._get_canon_syscalls_lines(syscalls_path, gdb_syscalls) - - with open(canon_syscalls_path, "w", encoding="UTF-8") as file: - file.writelines(head) - file.writelines(canon_syscalls_lines) - file.writelines(tail) + def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]: + gdb_syscalls: list[str] = [] + with open(gdb_syscalls_path, "r", encoding="UTF-8") as file: + lines = file.readlines() + for line in lines: + match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line) + if match: + gdb_syscalls.append(match.group("name").strip()) + return gdb_syscalls + + def _get_canon_syscalls_lines( + self, syscalls_path: _Path, gdb_syscalls: list[str] + ) -> list[str]: + canon_syscalls: dict[int, str] = {} + with open(syscalls_path, "r", encoding="UTF-8") as file: + lines = file.readlines() + for line in lines: + match = re.match( + r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line + ) + if match: + syscall_name = match.group("name") + syscall_num = int(match.group("number")) + gdb_syscall_name = f"gdb_sys_{syscall_name}" + if gdb_syscall_name in gdb_syscalls: + value = f" case {syscall_num}: return {gdb_syscall_name};\n" + canon_syscalls[syscall_num] = value + # this is a place for corner cases + elif syscall_name == "mmap": + gdb_old_syscall_name = "gdb_sys_old_mmap" + value = ( + f" case {syscall_num}: return {gdb_old_syscall_name};\n" + ) + canon_syscalls[syscall_num] = value + else: + value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n" + canon_syscalls[syscall_num] = value + return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)] + + def generate(self, syscalls_path: _Path) -> None: + repo_path = _Path(__file__).parent.parent.parent + gdb_syscalls_path = repo_path / "gdb" / "linux-record.h" + canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c" + + gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path) + canon_syscalls_lines = self._get_canon_syscalls_lines( + syscalls_path, gdb_syscalls + ) + + with open(canon_syscalls_path, "w", encoding="UTF-8") as file: + file.writelines(head) + file.writelines(canon_syscalls_lines) + file.writelines(tail) help_message = """\ @@ -136,28 +144,28 @@ from path to riscv linux syscalls. def setup_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(description=help_message) - parser.add_argument( - "-i", - "--input", - type=_Path, - required=True, - help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)", - ) - return parser + parser = argparse.ArgumentParser(description=help_message) + parser.add_argument( + "-i", + "--input", + type=_Path, + required=True, + help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)", + ) + return parser def main(argv: list[str]) -> int: - try: - parser = setup_parser() - args = parser.parse_args(argv) - generator = Generator() - generator.generate(args.input) - return 0 - except RuntimeError as e: - print(str(e)) - return -1 + try: + parser = setup_parser() + args = parser.parse_args(argv) + generator = Generator() + generator.generate(args.input) + return 0 + except RuntimeError as e: + print(str(e)) + return -1 if __name__ == "__main__": - sys.exit(main(sys.argv[1:])) + sys.exit(main(sys.argv[1:])) diff --git a/gdb/target-connection.c b/gdb/target-connection.c index 60d7732..02eea05 100644 --- a/gdb/target-connection.c +++ b/gdb/target-connection.c @@ -149,10 +149,7 @@ info_connections_command (const char *args, int from_tty) print_connection (current_uiout, args); } -void _initialize_target_connection (); - -void -_initialize_target_connection () +INIT_GDB_FILE (target_connection) { add_info ("connections", info_connections_command, _("\ diff --git a/gdb/target-dcache.c b/gdb/target-dcache.c index 9fcdd1e..6be1224 100644 --- a/gdb/target-dcache.c +++ b/gdb/target-dcache.c @@ -161,9 +161,7 @@ maint_flush_dcache_command (const char *command, int from_tty) gdb_printf (_("The dcache was flushed.\n")); } -void _initialize_target_dcache (); -void -_initialize_target_dcache () +INIT_GDB_FILE (target_dcache) { add_setshow_boolean_cmd ("stack-cache", class_support, &stack_cache_enabled_1, _("\ diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c index 8042d25..dc5fbac 100644 --- a/gdb/target-descriptions.c +++ b/gdb/target-descriptions.c @@ -1880,9 +1880,7 @@ maintenance_check_xml_descriptions (const char *dir, int from_tty) (long) selftests::xml_tdesc.size (), failed); } -void _initialize_target_descriptions (); -void -_initialize_target_descriptions () +INIT_GDB_FILE (target_descriptions) { cmd_list_element *cmd; diff --git a/gdb/target.c b/gdb/target.c index 4a1964e..f7c43f6 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -1250,11 +1250,21 @@ generic_tls_error (void) _("Cannot find thread-local variables on this target")); } -/* Using the objfile specified in OBJFILE, find the address for the - current thread's thread-local storage with offset OFFSET. */ +/* See target.h. */ + CORE_ADDR -target_translate_tls_address (struct objfile *objfile, CORE_ADDR offset) +target_translate_tls_address (struct objfile *objfile, CORE_ADDR offset, + const char *name) { + if (!target_has_registers ()) + { + if (name == nullptr) + error (_("Cannot translate TLS address without registers")); + else + error (_("Cannot find address of TLS symbol `%s' without registers"), + name); + } + volatile CORE_ADDR addr = 0; struct target_ops *target = current_inferior ()->top_target (); gdbarch *gdbarch = current_inferior ()->arch (); @@ -2454,6 +2464,7 @@ target_pre_inferior () if (!gdbarch_has_global_solist (current_inferior ()->arch ())) { no_shared_libraries (current_program_space); + current_program_space->unset_solib_ops (); invalidate_target_mem_regions (); @@ -3194,8 +3205,8 @@ target_ops::fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno) } int -target_ops::fileio_stat (struct inferior *inf, const char *filename, - struct stat *sb, fileio_error *target_errno) +target_ops::fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) { *target_errno = FILEIO_ENOSYS; return -1; @@ -3321,17 +3332,17 @@ target_fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno) /* See target.h. */ int -target_fileio_stat (struct inferior *inf, const char *filename, - struct stat *sb, fileio_error *target_errno) +target_fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) { for (target_ops *t = default_fileio_target (); t != NULL; t = t->beneath ()) { - int ret = t->fileio_stat (inf, filename, sb, target_errno); + int ret = t->fileio_lstat (inf, filename, sb, target_errno); if (ret == -1 && *target_errno == FILEIO_ENOSYS) continue; - target_debug_printf_nofunc ("target_fileio_stat (%s) = %d (%d)", + target_debug_printf_nofunc ("target_fileio_lstat (%s) = %d (%d)", filename, ret, ret != -1 ? 0 : *target_errno); return ret; @@ -4492,10 +4503,7 @@ set_write_memory_registers_permission (const char *args, int from_tty, update_observer_mode (); } -void _initialize_target (); - -void -_initialize_target () +INIT_GDB_FILE (target) { the_debug_target = new debug_target (); diff --git a/gdb/target.h b/gdb/target.h index 004494d..365e894 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -1016,8 +1016,8 @@ struct target_ops filesystem seen by the debugger (GDB or, for remote targets, the remote stub). Return 0 on success, or -1 if an error occurs (and set *TARGET_ERRNO). */ - virtual int fileio_stat (struct inferior *inf, const char *filename, - struct stat *sb, fileio_error *target_errno); + virtual int fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno); /* Close FD on the target. Return 0, or -1 if an error occurs (and set *TARGET_ERRNO). */ @@ -2256,8 +2256,8 @@ extern int target_fileio_fstat (int fd, struct stat *sb, filesystem seen by the debugger (GDB or, for remote targets, the remote stub). Return 0 on success, or -1 if an error occurs (and set *TARGET_ERRNO). */ -extern int target_fileio_stat (struct inferior *inf, const char *filename, - struct stat *sb, fileio_error *target_errno); +extern int target_fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno); /* Close FD on the target. Return 0, or -1 if an error occurs (and set *TARGET_ERRNO). */ @@ -2472,8 +2472,14 @@ extern void target_pre_inferior (); extern void target_preopen (int); +/* Using the objfile specified in OBJFILE, find the address for the + current thread's thread-local storage with offset OFFSET. If it's + provided, NAME might be used to indicate the relevant variable + in an error message. */ + extern CORE_ADDR target_translate_tls_address (struct objfile *objfile, - CORE_ADDR offset); + CORE_ADDR offset, + const char *name = nullptr); /* Return the "section" containing the specified address. */ const struct target_section *target_section_by_addr (struct target_ops *target, 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/analyze-racy-logs.py b/gdb/testsuite/analyze-racy-logs.py index e7c5647..f24bea9 100755 --- a/gdb/testsuite/analyze-racy-logs.py +++ b/gdb/testsuite/analyze-racy-logs.py @@ -62,7 +62,6 @@ sum_matcher = re.compile("^(.?(PASS|FAIL)): (.*)$") def parse_sum_line(line: str, dic: dict[str, set[str]]): """Parse a single LINE from a sumfile, and store the results in the dictionary referenced by DIC.""" - global sum_matcher line = line.rstrip() m = re.match(sum_matcher, line) @@ -96,7 +95,6 @@ def read_sum_files(files: list[str]): """Read the sumfiles (passed as a list in the FILES variable), and process each one, filling the FILES_AND_TESTS global dictionary with information about them.""" - global files_and_tests for x in files: with open(x, "r") as f: @@ -115,7 +113,6 @@ def identify_racy_tests(): This function does that for all sets (PASS, FAIL, KPASS, KFAIL, etc.), and then print a sorted list (without duplicates) of all the tests that were found to be racy.""" - global files_and_tests # First, construct two dictionaries that will hold one set of # testcases for each state (PASS, FAIL, etc.). 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..f8a4363 --- /dev/null +++ b/gdb/testsuite/gdb.ada/dyn-bit-offset.exp @@ -0,0 +1,79 @@ +# 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 +} + +# GCC needs to have fixes: +# - 809b46d2ccc ("Partially lift restriction from loc_list_from_tree_1") +# - d7f24e37d4b ("Fix oversight about big-endian targets in latest change") +set have_xfail [gnat_version_compare <= {16 1}] + +clean_restart ${testfile} + +set bp_location [gdb_get_line_number "STOP" ${testdir}/exam.adb] +runto "exam.adb:$bp_location" + +set re_pass \ + [string_to_regexp \ + " = (discr => 3, array_field => (-5, -6, -7), field => -5, another_field => -6)"] +set re_xfail_le \ + [string_to_regexp \ + " = (discr => 3, array_field => (-5, -6, -7), field => -4, another_field => -4)"] +set re_xfail_be \ + [string_to_regexp \ + " = (discr => 3, array_field => (-5, -6, -7), field => -6, another_field => -6)"] + +gdb_test_multiple "print spr" "" { + -re -wrap $re_pass { + pass $gdb_test_name + } + -re -wrap $re_xfail_le|$re_xfail_be { + if { $have_xfail } { + xfail $gdb_test_name + } else { + fail $gdb_test_name + } + } +} + +set re_pass " = -5" +set re_xfail_le " = -4" +set re_xfail_be " = -6" + +gdb_test_multiple "print spr.field" "" { + -re -wrap $re_pass { + pass $gdb_test_name + } + -re -wrap $re_xfail_le|$re_xfail_be { + if { $have_xfail } { + xfail $gdb_test_name + } else { + fail $gdb_test_name + } + } +} 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..5c7f70b --- /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 => -5, + Another_Field => -6, + Array_Field => (-5, -6, -7)); + +begin + null; -- STOP +end Exam; diff --git a/gdb/testsuite/gdb.ada/finish-var-size.exp b/gdb/testsuite/gdb.ada/finish-var-size.exp index e038ed4..ae30086 100644 --- a/gdb/testsuite/gdb.ada/finish-var-size.exp +++ b/gdb/testsuite/gdb.ada/finish-var-size.exp @@ -22,7 +22,13 @@ require {expr [gcc_major_version] >= 12} standard_ada_testfile p -if {[gdb_compile_ada "${srcfile}" "${binfile}" executable debug] != ""} { +set opts {} +lappend opts debug +if { [have_fvar_tracking] } { + lappend opts additional_flags=-fvar-tracking +} + +if {[gdb_compile_ada "${srcfile}" "${binfile}" executable $opts] != ""} { return -1 } 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/negative-bit-offset.exp b/gdb/testsuite/gdb.ada/negative-bit-offset.exp new file mode 100644 index 0000000..c5fcae1 --- /dev/null +++ b/gdb/testsuite/gdb.ada/negative-bit-offset.exp @@ -0,0 +1,36 @@ +# 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 negative DW_AT_bit_offset. + +load_lib "ada.exp" + +require allow_ada_tests + +standard_ada_testfile prog + +# This particular output is only generated with -gdwarf-4. +if {[gdb_compile_ada "${srcfile}" "${binfile}" executable \ + {debug additional_flags=-gdwarf-4}] != ""} { + return +} + +clean_restart ${testfile} + +set bp_location [gdb_get_line_number "STOP" ${testdir}/prog.adb] +runto "prog.adb:$bp_location" + +gdb_test "print xp" \ + [string_to_regexp "(x => 21, y => (-1, -2, -3, -4, -5, -6, -7, -8, -9, -10))"] diff --git a/gdb/testsuite/gdb.ada/negative-bit-offset/prog.adb b/gdb/testsuite/gdb.ada/negative-bit-offset/prog.adb new file mode 100644 index 0000000..e3c1775 --- /dev/null +++ b/gdb/testsuite/gdb.ada/negative-bit-offset/prog.adb @@ -0,0 +1,36 @@ +-- 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 Prog is + + type Small is range -32 .. 31; + for Small'Size use 6; + + type SomeArray is array (POSITIVE range <>) of Small; + + type SomePackedArray is array (POSITIVE range <>) of Small; + pragma Pack (SomePackedArray); + + type SomePackedRecord is record + X: Small; + Y: SomePackedArray (1 .. 10); + end record; + pragma Pack (SomePackedRecord); + + XP: SomePackedRecord := (21, (-1, -2, -3, -4, -5, -6, -7, -8, -9, -10)); + +begin + null; -- STOP +end; 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.arch/amd64-watchpoint-downgrade.exp b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp index dcee040..5663b0d 100644 --- a/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp +++ b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp @@ -58,7 +58,7 @@ gdb_test "starti" \ [multi_line \ "warning: watchpoint $num downgraded to software watchpoint" \ "" \ - "Program stopped\\." \ + "(Program|Thread \[^\r\n\]) stopped\\." \ ".*"] # Watchpoint should now have downgraded to a s/w watchpoint. diff --git a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c new file mode 100644 index 0000000..f55cee5 --- /dev/null +++ b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c @@ -0,0 +1,27 @@ +/* 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 <alloca.h> + +int +main (int argc, char **argv) +{ + volatile __attribute__ ((__aligned__ (64))) int a; + volatile char *p = (char *) alloca (argc * 12); + p[2] = 'b'; + return 1; +} diff --git a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp index eb93127..06285ce 100644 --- a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp +++ b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp @@ -19,41 +19,65 @@ # This option places an `endbr32`/`endbr64` instruction at the start of # all functions, which can interfere with prologue analysis. -standard_testfile .c -set binfile ${binfile} +standard_testfile .c -stackalign.c require {is_any_target x86_64-*-* i?86-*-*} - require supports_fcf_protection -set opts {debug additional_flags=-fcf-protection=full} +# Tests if breakpoint set on main is placed past main's entry. +proc test_run {} { + # Get start address of function main. + set main_addr [get_integer_valueof &main -1] + gdb_assert {$main_addr != -1} -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $opts] != "" } { - untested "failed to compile" - return -} + set bp_addr -1 -clean_restart ${binfile} + # Put breakpoint on main, get the address where the breakpoint was installed. + gdb_test_multiple "break -q main" "break on main, get address" { + -re -wrap "Breakpoint $::decimal at ($::hex).*" { + set bp_addr $expect_out(1,string) -# Get start address of function main. -set main_addr [get_integer_valueof &main -1] -gdb_assert {$main_addr != -1} + # Convert to decimal. + set bp_addr [expr $bp_addr] -set bp_addr -1 + pass $gdb_test_name + } + } -# Put breakpoint on main, get the address where the breakpoint was installed. -gdb_test_multiple "break -q main" "break on main, get address" { - -re -wrap "Breakpoint $decimal at ($hex).*" { - set bp_addr $expect_out(1,string) + # Make sure some prologue was skipped. + gdb_assert {$bp_addr != -1 && $bp_addr > $main_addr} \ + "breakpoint placed past main's entry" +} - # Convert to decimal. - set bp_addr [expr $bp_addr] +with_test_prefix "skip-cf-protection" { + set opts {debug additional_flags=-fcf-protection=full} - pass $gdb_test_name + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + $opts] != "" } { + untested "failed to compile" + return } + + clean_restart ${binfile} + + test_run } -if { $bp_addr != -1 } { - # Make sure some prologue was skipped. - gdb_assert {$bp_addr > $main_addr} +# Now, make sure that the prologue analysis does not end up at function's entry +# when stack alignment sequence is generated right after 'endbr64'/'endbr32'. +# That could happen if GDB handled those incorrectly - there was a bug that +# checked for those two in incorrect order, which caused such issue. +with_test_prefix "skip-cf-protection-stackalign" { + # gcc is easier to make it produce the sequence of interest. + if { ![is_c_compiler_gcc] } { + unsupported "stackalign test part requires gcc compiler" + return + } + + if { [prepare_for_testing "failed to prepare" "${testfile}-stackalign" \ + $srcfile2 [list optimize=-O0 additional_flags=-fcf-protection=full]] } { + return + } + + test_run } diff --git a/gdb/testsuite/gdb.base/attach-deleted-exec.exp b/gdb/testsuite/gdb.base/attach-deleted-exec.exp index 82a7bb4..45fac0d 100644 --- a/gdb/testsuite/gdb.base/attach-deleted-exec.exp +++ b/gdb/testsuite/gdb.base/attach-deleted-exec.exp @@ -67,5 +67,49 @@ if { [regexp $re_nfs $filename] } { gdb_assert { [string equal $filename /proc/${testpid}/exe] } $test } +# Restart GDB. +clean_restart + +# Setup an empty sysroot. GDB will fail to find the executable within +# the sysroot. Additionally, the presence of a sysroot should prevent +# GDB from trying to load the executable from /proc/PID/exe. +set sysroot [standard_output_file "sysroot"] +gdb_test_no_output "set sysroot $sysroot" \ + "setup sysroot" + +# Attach to the inferior. GDB should complain about failing to find +# the executable. It is the name of the executable that GDB doesn't +# find that we're interesting in here. For native targets GDB should +# be looking for BINFILE, not /proc/PID/exe. +# +# For extended-remote targets things are unfortunately harder. Native +# GDB looks for BINFILE because it understands that GDB will be +# looking in the sysroot. But remote GDB doesn't know if GDB is using +# a sysroot or not. As such, gdbserver will return /proc/PID/exe if +# it knows that the file has been deleted locally. This isn't great +# if GDB then plans to look in a sysroot, but equally, if the remote +# file has been deleted, then the name GDB will return, will have had +# " (deleted" appended, so we're unlikely to get a hit in the sysroot +# either way. +if { [target_info gdb_protocol] == "extended-remote" } { + set filename_re "/proc/$testpid/exe" +} else { + set filename_re "\[^\r\n\]+/${testfile} \\(deleted\\)" +} + +verbose -log "APB: warning: No executable has been specified, and target executable $filename_re could not be found\\. Try using the \"file\" command\\." + +gdb_test "attach $testpid" \ + [multi_line \ + "Attaching to process $decimal" \ + "warning: No executable has been specified, and target executable $filename_re could not be found\\. Try using the \"file\" command\\." \ + ".*"] \ + "attach to inferior" + +# Check GDB hasn't managed to load an executable. +gdb_test "info inferior" \ + "\\*\[^)\]+\\)\\s*" \ + "confirm no executable is loaded." + # Cleanup. kill_wait_spawned_process $test_spawn_id diff --git a/gdb/testsuite/gdb.base/bp-cond-failure.exp b/gdb/testsuite/gdb.base/bp-cond-failure.exp index d645454..4d03e7b 100644 --- a/gdb/testsuite/gdb.base/bp-cond-failure.exp +++ b/gdb/testsuite/gdb.base/bp-cond-failure.exp @@ -75,7 +75,7 @@ proc run_test { cond_eval access_type bpexpr nloc } { "Error in testing condition for breakpoint ${bp_num}.2:" \ "Cannot access memory at address 0x0" \ "" \ - "Breakpoint ${bp_num}.2, foo \\(c=49 ...\\) at \[^\r\n\]+:\[0-9\]+" \ + "(Thread \[^\r\n\]+ hit )?Breakpoint ${bp_num}.2, foo \\(c=49 ...\\) at \[^\r\n\]+:\[0-9\]+" \ "${::decimal}\\s+\[^\r\n\]+ breakpoint here\\. \[^\r\n\]+"] } else { gdb_test "continue" \ @@ -84,7 +84,7 @@ proc run_test { cond_eval access_type bpexpr nloc } { "Error in testing condition for breakpoint ${bp_num}:" \ "Cannot access memory at address 0x0" \ "" \ - "Breakpoint ${bp_num}, bar \\(\\) at \[^\r\n\]+:\[0-9\]+" \ + "(Thread \[^\r\n\]+ hit )?Breakpoint ${bp_num}, bar \\(\\) at \[^\r\n\]+:\[0-9\]+" \ "${::decimal}\\s+\[^\r\n\]+ breakpoint here\\. \[^\r\n\]+"] } } diff --git a/gdb/testsuite/gdb.base/bp-permanent.c b/gdb/testsuite/gdb.base/bp-permanent.c index d586acc..72e5e8a 100644 --- a/gdb/testsuite/gdb.base/bp-permanent.c +++ b/gdb/testsuite/gdb.base/bp-permanent.c @@ -101,7 +101,7 @@ test_signal_no_handler (void) } static void -test_signal_nested_handler () +test_signal_nested_handler (int sig) { test (); } diff --git a/gdb/testsuite/gdb.base/bp-permanent.exp b/gdb/testsuite/gdb.base/bp-permanent.exp index 62ce3f6..c6c6269 100644 --- a/gdb/testsuite/gdb.base/bp-permanent.exp +++ b/gdb/testsuite/gdb.base/bp-permanent.exp @@ -134,7 +134,7 @@ proc test {always_inserted sw_watchpoint} { unsupported "failed to stop at permanent breakpoint" return } - -re "Program received signal SIGTRAP.*$gdb_prompt $" { + -re "received signal SIGTRAP.*$gdb_prompt $" { pass $test } } @@ -174,7 +174,7 @@ proc test {always_inserted sw_watchpoint} { # disabled, it should act as if we hadn't created it in the first # place. IOW, we should get a random signal, and, the breakpoint's # command should not run. - gdb_test "continue" "Program received signal SIGTRAP.*" \ + gdb_test "continue" "received signal SIGTRAP.*" \ "disabled permanent breakpoint doesn't explain stop" gdb_test "info breakpoints" \ diff --git a/gdb/testsuite/gdb.base/break-dbg.cc b/gdb/testsuite/gdb.base/break-dbg.cc new file mode 100644 index 0000000..642ded0 --- /dev/null +++ b/gdb/testsuite/gdb.base/break-dbg.cc @@ -0,0 +1,31 @@ +/* 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/>. */ + +volatile int global_var = 0; + +int +foo () +{ + return global_var; +} + +int +main () +{ + int res = foo (); + return res; +} diff --git a/gdb/testsuite/gdb.base/break-dbg.exp b/gdb/testsuite/gdb.base/break-dbg.exp new file mode 100644 index 0000000..3652b8e --- /dev/null +++ b/gdb/testsuite/gdb.base/break-dbg.exp @@ -0,0 +1,70 @@ +# 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/>. */ + +# Some basic testing of 'set debug breakpoint on'. At one point a bug +# meant that some breakpoints would immediately trigger a segfault if +# GDB tried to run with breakpoint debugging turned on. +# +# Test is compiled as C++ only so 'catch catch/throw/rethrow' have a +# something to do. The original bug would trigger for any 'catch' +# style breakpoint, so C++ isn't really a hard requirement. + +standard_testfile .cc + +require allow_cplus_tests + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + {debug c++}] } { + return +} + +if {![runto_main]} { + return +} + +gdb_test "catch catch" "^Catchpoint $decimal \\(catch\\)" +gdb_test "catch throw" "^Catchpoint $decimal \\(throw\\)" +gdb_test "catch rethrow" "^Catchpoint $decimal \\(rethrow\\)" + +gdb_test "catch exec" "^Catchpoint $decimal \\(exec\\)" +gdb_test "catch fork" "^Catchpoint $decimal \\(fork\\)" +gdb_test "catch vfork" "^Catchpoint $decimal \\(vfork\\)" + +gdb_test "catch load" "^Catchpoint $decimal \\(load\\)" +gdb_test "catch unload" "^Catchpoint $decimal \\(unload\\)" + +gdb_test "catch signal" "^Catchpoint $decimal \\(standard signals\\)" +gdb_test "catch syscall" "^Catchpoint $decimal \\(any syscall\\)" + +gdb_test "watch -l global_var" "\[Ww]atchpoint $decimal: -location global_var" + +gdb_test_no_output "set debug breakpoint on" + +set saw_bp_debug_line false +gdb_test_multiple "step" "" { + -re "^step\r\n" { + exp_continue + } + -re "^\\\[breakpoint\\\] \[^\r\n\]+\r\n" { + set saw_bp_debug_line true + exp_continue + } + -re "^$gdb_prompt $" { + gdb_assert { $saw_bp_debug_line } $gdb_test_name + } + -re "^\[^\r\n\]*\r\n" { + exp_continue + } +} diff --git a/gdb/testsuite/gdb.base/break1.c b/gdb/testsuite/gdb.base/break1.c index 110341c..26c4663 100644 --- a/gdb/testsuite/gdb.base/break1.c +++ b/gdb/testsuite/gdb.base/break1.c @@ -23,7 +23,13 @@ struct some_struct { int a_field; int b_field; - union { int z_field; }; + union + { + struct + { + int z_field; + }; + }; }; struct some_struct values[50]; diff --git a/gdb/testsuite/gdb.base/catch-fork-kill.exp b/gdb/testsuite/gdb.base/catch-fork-kill.exp index 0fd853b..224a8df 100644 --- a/gdb/testsuite/gdb.base/catch-fork-kill.exp +++ b/gdb/testsuite/gdb.base/catch-fork-kill.exp @@ -32,6 +32,8 @@ standard_testfile +require allow_fork_tests + # Build two programs -- one for fork, and another for vfork. set testfile_fork "${testfile}-fork" set testfile_vfork "${testfile}-vfork" diff --git a/gdb/testsuite/gdb.base/catch-fork-static.exp b/gdb/testsuite/gdb.base/catch-fork-static.exp index b171a6d..9d50d5d 100644 --- a/gdb/testsuite/gdb.base/catch-fork-static.exp +++ b/gdb/testsuite/gdb.base/catch-fork-static.exp @@ -21,9 +21,7 @@ # ld.so probes before reaching main, and ptrace flags were set then. But a # static executable would just keep running and never catch the fork. -# Until "catch fork" is implemented on other targets... -# -require {is_any_target "*-*-linux*" "*-*-openbsd*"} +require allow_fork_tests # Reusing foll-fork.c since it's a simple forking program. standard_testfile foll-fork.c diff --git a/gdb/testsuite/gdb.base/catch-signal-fork.exp b/gdb/testsuite/gdb.base/catch-signal-fork.exp index dea0cc4..2a33ee1 100644 --- a/gdb/testsuite/gdb.base/catch-signal-fork.exp +++ b/gdb/testsuite/gdb.base/catch-signal-fork.exp @@ -14,6 +14,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. require {!target_info exists gdb,nosignals} +require allow_fork_tests standard_testfile 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/corefile-shmem-zero-id-lib.c b/gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c new file mode 100644 index 0000000..58fdec6 --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c @@ -0,0 +1,522 @@ +/* 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/>. */ + +/* This file contains a library that can be preloaded into GDB on Linux + using the LD_PRELOAD technique. + + The library intercepts calls to OPEN, CLOSE, READ, and PREAD in order to + fake the inode number of a shared memory mapping. + + When GDB creates a core file (e.g. with the 'gcore' command), then + shared memory mappings should be included in the generated core file. + + The 'id' for the shared memory mapping shares the inode slot in the + /proc/PID/smaps file, which is what GDB consults to decide which + mappings should be included in the core file. + + It is possible for a shared memory mapping to have an 'id' of zero. + + At one point there was a bug in GDB where mappings with an inode of zero + would not be included in the generated core file. This meant that most + shared memory mappings would be included in the generated core file, + but, if a shared memory mapping happened to get an 'id' of zero, then, + because this would appear as a zero inode in the smaps file, this shared + memory mapping would be excluded from the generated core file. + + This preload library spots when GDB opens a /proc/PID/smaps file and + immediately copies the contents of this file into an internal buffer. + The buffer is then scanned looking for a shared memory mapping, and, if + a shared memory mapping is found, its 'id' (in the inode position) is + changed to zero. + + Calls to read/pread are intercepted, and attempts to read from the smaps + file are then served from the modified buffer contents. + + The close calls are monitored and, when the smaps file is closed, the + internal buffer is released. + + This works with GDB (currently) because the requirements for access to + the smaps file are pretty simple. GDB opens the file and grabs the + entire contents with a single pread call and a large buffer. There's no + seeking within the file or anything like that. + + The intention is that this library is preloaded into a GDB session which + is then used to start an inferior and generate a core file. GDB will + then see the zero inode for the shared memory mapping and should, if the + bug is correctly fixed, still add the shared memory mapping to the + generated core file. */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdarg.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <stdbool.h> +#include <assert.h> + +/* Logging. */ + +static void +log_msg (const char *fmt, ...) +{ +#ifdef LOGGING + va_list ap; + + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); +#endif /* LOGGING */ +} + +/* Error handling, message and exit. */ + +static void +error (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + + exit (EXIT_FAILURE); +} + +/* The type of the open() function. */ +typedef int (*open_func_type)(const char *pathname, int flags, ...); + +/* The type of the close() function. */ +typedef int (*close_func_type)(int fd); + +/* The type of the read() function. */ +typedef ssize_t (*read_func_type)(int fd, void *buf, size_t count); + +/* The type of the pread() function. */ +typedef ssize_t (*pread_func_type) (int fd, void *buf, size_t count, off_t offset); + +/* Structure that holds information about a /proc/PID/smaps file that has + been opened. */ +struct interesting_file +{ + /* The file descriptor for the opened file. */ + int fd; + + /* The read offset within the file. Set to zero when the file is + opened. Any 'read' calls will update this offset. */ + size_t offset; + + /* The size of the contents within the buffer. This is not the total + buffer size (which might be larger). Attempts to read beyond SIZE + indicate an attempt to read beyond the end of the file. */ + size_t size; + + /* The (possibly modified) contents of the file. */ + char *content; +}; + +/* We only track a single interesting file. Currently, for the use case + we imagine, GDB will only ever open one /proc/PID/smaps file at once. */ +struct interesting_file the_file = { -1, 0, 0, NULL }; + +/* Update the contents of the global THE_FILE buffer. It is assumed that + the file contents have already been loaded into THE_FILE's content + buffer. + + Look for any lines that represent a shared memory mapping and modify + the inode field (which holds the shared memory id) to be zero. */ +static void +update_file_content_buffer (void) +{ + assert (the_file.content != NULL); + + char *start = the_file.content; + do + { + /* Every line, even the last one, ends with a newline. */ + char *end = strchrnul (start, '\n'); + assert (end != NULL); + assert (*end != '\0'); + + /* Attribute lines start with an uppercase letter. The lines we want + to modify should start with a lower case hex character, + i.e. [0-9a-f]. Also, every line that we want to consider should + be long enough, but just in case, check the longest possible + filename that we care about. */ + if (isxdigit (*start) && (isdigit (*start) || islower (*start)) + && (end - start) > 23) + { + /* There are two possible filenames that we look for: + /SYSV%08x + /SYSV%08x (deleted) + The END pointer is pointing to the first character after the + filename. + + Setup OFFSET to be the offset from END to the start of the + filename. As we check the filename we set OFFSET to 0 if the + filename doesn't match one of the expected patterns. */ + size_t offset; + if (strncmp ((end - 13), "/SYSV", 5) == 0) + offset = 13; + else if (strncmp ((end - 23), "/SYSV", 5) == 0) + { + if (strncmp ((end - 10), " (deleted)", 10) == 0) + offset = 23; + else + offset = 0; + } + else + offset = 0; + + for (int i = 0; i < 8 && offset != 0; ++i) + { + if (!isdigit (*(end - offset + 5 + i))) + offset = 0; + } + + /* If OFFSET is non-zero then the filename on this line looks + like a shared memory mapping, and OFFSET is the offset from + END to the first character of the filename. */ + if (offset != 0) + { + log_msg ("[LD_PRELOAD] shared memory entry: %.*s\n", + offset, (end - offset)); + + /* Set PTR to the first character before the filename. This + should be a white space character. */ + char *ptr = end - offset - 1; + assert (isspace (*ptr)); + + /* Walk backwards until we find the inode field. */ + while (isspace (*ptr)) + --ptr; + + /* Now replace every character in the inode field, except the + first one, with a space character. */ + while (!isspace (*(ptr - 1))) + { + assert (isdigit (*ptr)); + *ptr = ' '; + --ptr; + } + + /* Replace the first character with '0'. */ + assert (isdigit (*ptr)); + *ptr = '0'; + + /* This print is checked for from GDB. */ + printf ("[LD_PRELOAD] updated a shared memory mapping\n"); + } + } + + /* Update START to point to the next line. The last line of the + file will be empty. */ + assert (*end == '\n'); + start = end; + while (*start == '\n') + ++start; + } + while (*start != '\0'); +} + +/* Return true if PATHNAME has for form "/proc/PID/smaps" (without the + quotes). Otherwise, return false. */ + +static bool +is_smaps_file (const char *pathname) +{ + if (strncmp (pathname, "/proc/", 6) == 0) + { + int idx = 6; + while (isdigit (pathname[idx])) + idx++; + if (idx > 6 && strcmp (&pathname[idx], "/smaps") == 0) + return true; + } + + return false; +} + +/* Return true if PATHNAME should be considered interesting. PATHNAME is + interesting if it has the form /proc/PID/smaps, and there is no + interesting file already opened. */ + +static bool +is_interesting_pathname (const char *pathname) +{ + return the_file.fd == -1 && is_smaps_file (pathname); +} + +/* Read the contents of an interesting file from FD (and open file + descriptor) into the global THE_FILE variable, making the file FD the + current interesting file. There should be no already open interesting + file when this function is called. + + The contents of the file FD are read into a memory buffer and updated so + that any shared memory mappings listed within FD (which will be an smaps + file) will have the id zero. */ + +static void +read_interesting_file_contents (int fd) +{ +#define BLOCK_SIZE 1024 + /* Slurp contents into a local buffer. */ + size_t buffer_size = 1024; + size_t offset = 0; + + assert (the_file.size == 0); + assert (the_file.content == NULL); + assert (the_file.fd == -1); + assert (the_file.offset == 0); + + do + { + the_file.content = (char *) realloc (the_file.content, buffer_size); + if (the_file.content == NULL) + error ("[LD_PRELOAD] Failed allocating memory: %s\n", strerror (errno)); + + ssize_t bytes_read = read (fd, the_file.content + offset, BLOCK_SIZE); + if (bytes_read == -1) + error ("[LD_PRELOAD] Failed reading file: %s\n", strerror (errno)); + + the_file.size += bytes_read; + + if (bytes_read < BLOCK_SIZE) + break; + + offset += BLOCK_SIZE; + buffer_size += BLOCK_SIZE; + } + while (true); + + /* Add a null terminator. This makes the update easier. We know + there will be space because we only break out of the loop above + when the last read returns less than BLOCK_SIZE bytes. This means + we allocated an extra BLOCK_SIZE bytes, but didn't fill them all. + This means there must be at least 1 byte available for the null. */ + the_file.content[the_file.size] = '\0'; + + /* Reset the seek pointer. */ + if (lseek (fd, 0, SEEK_SET) == (off_t) -1) + error ("[LD_PRELOAD] Failed to lseek in file: %s\n", strerror (errno)); + + /* Record the file descriptor, this is used in read, pread, and close + in order to spot when we need to intercept the call. */ + the_file.fd = fd; + + update_file_content_buffer (); +#undef BLOCK_SIZE +} + +/* Intercept calls to 'open'. If this is an attempt to open a + /proc/PID/smaps file then intercept it, load the file contents into a + buffer and update the file contents. For all other open requests, just + forward to the real open function. */ +int +open (const char *pathname, int flags, ...) +{ + /* Pointer to the real open function. */ + static open_func_type real_open = NULL; + + /* Mode is only used if the O_CREAT flag is set in FLAGS. */ + mode_t mode = 0; + + /* Set true if this is a /proc/PID/smaps file. */ + bool is_interesting = is_interesting_pathname (pathname); + + /* Check if O_CREAT is in flags. If it is, get the mode. */ + if (flags & O_CREAT) + { + va_list args; + va_start (args, flags); + mode = va_arg (args, mode_t); + va_end (args); + } + + /* Debug. */ + if (is_interesting) + log_msg ("[LD_PRELOAD] Opening file: %s\n", pathname); + + /* Make sure we have a pointer to the real open() function. */ + if (real_open == NULL) + { + /* Get the address of the real open() function. */ + real_open = (open_func_type) dlsym (RTLD_NEXT, "open"); + if (real_open == NULL) + error ("[LD_PRELOAD] dlsym() error for 'open': %s\n", dlerror ()); + } + + /* Call the original open() function with the provided arguments. */ + int res = -1; + if (flags & O_CREAT) + res = real_open (pathname, flags, mode); + else + res = real_open (pathname, flags); + + if (res != -1 && is_interesting) + read_interesting_file_contents (res); + + return res; +} + +/* Like above, but for open64. */ + +int +open64 (const char *pathname, int flags, ...) +{ + /* Pointer to the real open64 function. */ + static open_func_type real_open64 = NULL; + + /* Mode is only used if the O_CREAT flag is set in FLAGS. */ + mode_t mode = 0; + + /* Set true if this is a /proc/PID/smaps file. */ + bool is_interesting = is_interesting_pathname (pathname); + + /* Check if O_CREAT is in flags. If it is, get the mode. */ + if (flags & O_CREAT) + { + va_list args; + va_start (args, flags); + mode = va_arg (args, mode_t); + va_end (args); + } + + /* Debug. */ + if (is_interesting) + log_msg ("[LD_PRELOAD] Opening file: %s\n", pathname); + + /* Make sure we have a pointer to the real open64() function. */ + if (real_open64 == NULL) + { + /* Get the address of the real open64() function. */ + real_open64 = (open_func_type) dlsym (RTLD_NEXT, "open64"); + if (real_open64 == NULL) + error ("[LD_PRELOAD] dlsym() error for 'open64': %s\n", dlerror ()); + } + + /* Call the original open64() function with the provided arguments. */ + int res = -1; + if (flags & O_CREAT) + res = real_open64 (pathname, flags, mode); + else + res = real_open64 (pathname, flags); + + if (res != -1 && is_interesting) + read_interesting_file_contents (res); + + return res; +} + +/* Intercept the 'close' function. If this is a previously opened + interesting file then clean up. Otherwise, forward to the normal close + function. */ +int +close (int fd) +{ + static close_func_type real_close = NULL; + + if (fd == the_file.fd) + { + the_file.fd = -1; + free (the_file.content); + the_file.content = NULL; + the_file.offset = 0; + the_file.size = 0; + log_msg ("[LD_PRELOAD] Closing file.\n"); + } + + /* Make sure we have a pointer to the real open() function. */ + if (real_close == NULL) + { + /* Get the address of the real open() function. */ + real_close = (close_func_type) dlsym (RTLD_NEXT, "close"); + if (real_close == NULL) + error ("[LD_PRELOAD] dlsym() error for 'close': %s\n", dlerror ()); + } + + return real_close (fd); +} + +/* Intercept 'pread' calls. If this is a pread from a previously opened + interesting file, then read from the in memory buffer. Otherwise, + forward to the real pread function. */ +ssize_t +pread (int fd, void *buf, size_t count, off_t offset) +{ + static pread_func_type real_pread = NULL; + + if (fd == the_file.fd) + { + size_t max; + + if (offset > the_file.size) + max = 0; + else + max = the_file.size - offset; + if (count > max) + count = max; + + memcpy (buf, the_file.content + offset, count); + log_msg ("[LD_PRELOAD] Read from file.\n"); + return count; + } + + if (real_pread == NULL) + { + /* Get the address of the real read() function. */ + real_pread = (pread_func_type) dlsym (RTLD_NEXT, "pread"); + if (real_pread == NULL) + error ("[LD_PRELOAD] dlsym() error for 'pread': %s\n", dlerror ()); + } + + return real_pread (fd, buf, count, offset); +} + +/* Intercept 'read' calls. If this is a read from a previously opened + interesting file, then read from the in memory buffer. Otherwise, + forward to the real read function. */ +ssize_t +read (int fd, void *buf, size_t count) +{ + static read_func_type real_read = NULL; + + if (fd == the_file.fd) + { + ssize_t bytes_read = pread (fd, buf, count, the_file.offset); + if (bytes_read > 0) + the_file.offset += bytes_read; + return bytes_read; + } + + if (real_read == NULL) + { + /* Get the address of the real read() function. */ + real_read = (read_func_type) dlsym (RTLD_NEXT, "read"); + if (real_read == NULL) + error ("[LD_PRELOAD] dlsym() error for 'read': %s\n", dlerror ()); + } + + return real_read (fd, buf, count); +} diff --git a/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c new file mode 100644 index 0000000..92d2edf --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c @@ -0,0 +1,63 @@ +/* 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 <sys/ipc.h> +#include <sys/shm.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <time.h> + +void +breakpt (void) +{ + /* Nothing. */ +} + +int +main (void) +{ + /* Create a shared memory mapping. */ + int sid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | IPC_EXCL | 0777); + if (sid == -1) + { + perror ("shmget"); + exit (1); + } + + /* Attach the shared memory mapping. */ + void *addr = shmat (sid, NULL, SHM_RND); + if (addr == (void *) -1L) + { + perror ("shmat"); + exit (1); + } + + breakpt (); + + /* Mark the shared memory mapping as deleted -- once the last user + has finished with it. */ + if (shmctl (sid, IPC_RMID, NULL) != 0) + { + perror ("shmctl"); + exit (1); + } + + return 0; +} diff --git a/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp new file mode 100644 index 0000000..57c665e --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp @@ -0,0 +1,228 @@ +# 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/>. + +# This test script tries to check GDB's ability to create a core file +# (e.g. with 'gcore' command) when there's a shared memory mapping +# with the id zero. +# +# Testing this case is hard. Older kernels don't even seem to give +# out the shared memory id zero. And on new kernels you still cannot +# guarantee to grab the zero id for testing; the id might be in use by +# some other process, or the kernel might just not give out that id +# for some other reason. +# +# To figure out which mappings to include in the core file, GDB reads +# the /proc/PID/smaps file. There is a field in this file which for +# file backed mappings, holds the inode of the file. But for shared +# memory mappings this field holds the shared memory id. The problem +# was that GDB would ignore any entry in /proc/PID/smaps with an inode +# entry of zero, which would catch the shared memory mapping with a +# zero id. +# +# There was an attempt to write a test which spammed out requests for +# shared memory mappings and tried to find the one with id zero, but +# this was still really unreliable. +# +# This test takes a different approach. We compile a library which we +# preload into the GDB process. This library intercepts calls to +# open, close, read, and pread, and watches for an attempt to open the +# /proc/PID/smaps file. +# +# When we see that file being opened, we copy the file contents into a +# memory buffer and modify the buffer so that the inode field for any +# shared memory mappings is set to zero. We then intercept calls to +# read and pread and return results from that in memory buffer. +# +# The test executable itself create a shared memory mapping (which +# might have any id). +# +# GDB, with the pre-load library in place, start the inferior and then +# uses the 'gcore' command to dump a core file. When GDB opens the +# smaps file and reads from it, the preload library ensures that GDB +# sees an inode of zero. +# + +# This test only works on Linux +require isnative +require {!is_remote host} +require {!is_remote target} +require {istarget *-linux*} +require gcore_cmd_available + +standard_testfile .c -lib.c + +set libfile ${testfile}-lib +set libobj [standard_output_file ${libfile}.so] + +# Compile the preload library. We only get away with this as we +# limit this test to running when ISNATIVE is true. +if { [build_executable "build preload lib" $libobj $srcfile2 \ + {debug shlib libs=-ldl}] == -1 } { + return +} + +# Now compile the inferior executable. +if {[build_executable "build executable" $testfile $srcfile] == -1} { + return +} + +# Spawn GDB with LIBOBJ preloaded using LD_PRELOAD. +save_vars { env(LD_PRELOAD) env(ASAN_OPTIONS) } { + if { ![info exists env(LD_PRELOAD) ] + || $env(LD_PRELOAD) == "" } { + set env(LD_PRELOAD) "$libobj" + } else { + append env(LD_PRELOAD) ":$libobj" + } + + # Prevent address sanitizer error: + # ASan runtime does not come first in initial library list; you should + # either link runtime to your application or manually preload it with + # LD_PRELOAD. + append_environment_default ASAN_OPTIONS verify_asan_link_order 0 + + clean_restart $binfile + + # Start GDB with the modified environment, this means that, when + # using remote targets, gdbserver will also use the preload + # library. + if {![runto_main]} { + return + } +} + +gdb_breakpoint breakpt +gdb_continue_to_breakpoint "run to breakpt" + +# Check the /proc/PID/smaps file itself. The call to 'cat' should +# inherit the preload library, so should see the modified file +# contents. Check that the shared memory mapping line has an id of +# zero. This confirms that the preload library is working. If the +# preload library breaks then we'll start seeing non-zero shared +# memory ids, which always worked, so we'd never know that this test +# is broken! +# +# This check ensures the test is working as expected. +set shmem_line_count 0 +set fixup_line_count 0 +set inf_pid [get_inferior_pid] +gdb_test_multiple "shell cat /proc/${inf_pid}/smaps" "check smaps" { + -re "^\\\[LD_PRELOAD\\\] updated a shared memory mapping\r\n" { + incr fixup_line_count + exp_continue + } + -re "^\[^\r\n\]+($decimal)\\s+/SYSV\[0-9\]{8}(?: \\(deleted\\))?\r\n" { + set id $expect_out(1,string) + if { $id == 0 } { + incr shmem_line_count + } + exp_continue + } + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $shmem_line_count == 1 } \ + "single shared memory mapping found" + gdb_assert { $fixup_line_count == 1 } \ + "single fixup line found" + } + } + -re "^\[^\r\n\]+\r\n" { + exp_continue + } +} + +# Now generate a core file. This will use the preload library to read +# the smaps file. The code below is copied from 'proc gdb_gcore_cmd', +# but we don't use that as we also look for a message that is printed +# by the LD_PRELOAD library. This is an extra level of check that the +# preload library is triggering when needed. +set corefile [standard_output_file ${testfile}.core] +set saw_ld_preload_msg false +set saw_saved_msg false +with_timeout_factor 3 { + gdb_test_multiple "gcore $corefile" "save core file" { + -re "^\\\[LD_PRELOAD\\\] updated a shared memory mapping\r\n" { + # GDB actually reads the smaps file multiple times when + # creating a core file, so we'll see multiple of these + # fixup lines. + set saw_ld_preload_msg true + exp_continue + } + -re "^Saved corefile \[^\r\n\]+\r\n" { + set saw_saved_msg true + exp_continue + } + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $saw_saved_msg } \ + "saw 'Saved corefile' message" + + # If we're using a remote target then the message from + # the preload library will go to gdbservers stdout, + # not GDB's, so don't check for it. + if { [gdb_protocol_is_native] } { + gdb_assert { $saw_ld_preload_msg } \ + "saw LD_PRELOAD message from library" + } + } + } + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } +} + +# Restart GDB. This time we are _not_ using the preload library. We +# no longer need it as we are only analysing the core file now. +clean_restart $binfile + +# Load the core file. +gdb_test "core-file $corefile" \ + "Program terminated with signal SIGTRAP, Trace/breakpoint trap\\..*" \ + "load core file" + +# Look through the mappings. We _should_ see the shared memory +# mapping. We _should_not_ see any of the special '[blah]' style +# mappings, e.g. [vdso], [vstack], [vsyscalls], etc. +set saw_special_mapping false +set saw_shmem_mapping false +gdb_test_multiple "info proc mappings" "" { + -re "\r\nStart Addr\[^\r\n\]+File\\s*\r\n" { + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\\s+\\\[\\S+\\\]\\s*\r\n" { + set saw_special_mapping true + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\\s+/SYSV\[0-9\]+ \\(deleted\\)\\s*\r\n" { + set saw_shmem_mapping true + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\[^\r\n\]*\r\n" { + exp_continue + } + + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $saw_shmem_mapping } \ + "check shared memory mapping exists" + gdb_assert { !$saw_special_mapping } \ + "check no special mappings added" + } + } +} diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index 3abd049..01e3cc1 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -253,10 +253,39 @@ gdb_test "h" "List of classes of commands:(\[^\r\n\]*\[\r\n\])+aliases -- User-d gdb_test "help" "List of classes of commands:(\[^\r\n\]*\[\r\n\])+aliases -- User-defined aliases of other commands(\[^\r\n\]*\[\r\n\])+breakpoints -- Making program stop at certain points(\[^\r\n\]*\[\r\n\])+data -- Examining data(\[^\r\n\]*\[\r\n\])+files -- Specifying and examining files(\[^\r\n\]*\[\r\n\])+obscure -- Obscure features(\[^\r\n\]*\[\r\n\])+running -- Running the program(\[^\r\n\]*\[\r\n\])+stack -- Examining the stack(\[^\r\n\]*\[\r\n\])+status -- Status inquiries(\[^\r\n\]*\[\r\n\])+support -- Support facilities(\[^\r\n\]*\[\r\n\])+user-defined -- User-defined commands(\[^\r\n\]*\[\r\n\])+Type \"help\" followed by a class name for a list of commands in that class.(\[^\r\n\]*\[\r\n\])+Type \"help\" followed by command name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." #test handle gdb_test "handle" "Argument required .signal to handle.*" -#test info "i" abbreviation -gdb_test "i" "List of \"info\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help info\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "info \"i\" abbreviation" + +proc test_info_command { command message } { + set saw_info_header 0 + set saw_help_info 0 + set saw_command_abbrev 0 + gdb_test_multiple $command $message -lbl { + -re "\r\nList of \"info\" subcommands:" { + verbose "Info header displayed" + set saw_info_header 1 + exp_continue + } + -re "Type \"help info\" followed by subcommand name for full documentation\\." { + verbose "Help info displayed" + set saw_help_info 1 + exp_continue + } + -re "\r\nCommand name abbreviations are allowed if unambiguous\\." { + verbose "Command name abbreviations displayed" + set saw_command_abbrev 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_info_header && $saw_help_info + && $saw_command_abbrev } $gdb_test_name + } + } +} + +#test info "i" abbreviation +test_info_command "i" "info \"i\" abbreviation" #test info -gdb_test "info" "List of \"info\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help info\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." +test_info_command "info" "info" + #test ignore gdb_test "ignore" "Argument required .a breakpoint number.*" #test info address @@ -379,38 +408,52 @@ gdb_test "info registers" "The program has no registers now." gdb_test "info s" "No stack." "info stack \"s\" abbreviation" #test info stack gdb_test "info stack" "No stack." -#test info set -# Test improved to check three parts: -# 1) confirm -# 2) prompt -# 3) write -# And only succeed if all three are matched. -# This should fix an old problem on native solaris 2.8, -# where this test fails due to this line: + +#test "info set" and "show" commands +# The test needs to match the "prompt: ..." part to fix an old problem on native +# Solaris 2.8, where this test fails due to this line: # prompt: Gdb's prompt is "(gdb) ".^M -set set_confirm_seen 0 -set set_prompt_seen 0 -gdb_test_multiple "info set" "info set" { - -re "confirm: Whether to confirm potentially dangerous operations is o\[a-z\]*.(\[^\r\n\]*\[\r\n\])+history filename: The filename in which to record the command history is (\[^\r\n\]*\[\r\n\])+listsize: Number of source lines gdb will list by default is 10" { - verbose "Confirm dislayed" - set set_confirm_seen 1 - exp_continue - } - -re "Gdb's prompt is \"$gdb_prompt \"" { - verbose "GDB prompt displayed" - set set_prompt_seen 1 - exp_continue - } - -re "Writing into executable.*$gdb_prompt $" { - verbose "write displayed" - if { $set_prompt_seen && $set_confirm_seen } { - pass "info set" - } else { - verbose "prompt $set_prompt_seen confirm $set_confirm_seen" - fail "info set (incomplete output)" +proc test_info_set_show { command } { + set set_confirm_seen 0 + set set_history_filename_seen 0 + set set_listsize_seen 0 + set set_prompt_seen 0 + set set_write_seen 0 + gdb_test_multiple $command $command -lbl { + -re "\r\nconfirm: Whether to confirm potentially dangerous operations is o\[a-z\]+\\." { + verbose "Confirm displayed" + set set_confirm_seen 1 + exp_continue + } + -re "\r\nhistory filename: The filename in which to record the command history is \[^\r\n\]+\\." { + verbose "History filename displayed" + set set_history_filename_seen 1 + exp_continue + } + -re "\r\nlistsize: Number of source lines gdb will list by default is 10\\." { + verbose "Listsize displayed" + set set_listsize_seen 1 + exp_continue + } + -re "\r\nprompt: Gdb's prompt is \"$::gdb_prompt \"" { + verbose "GDB prompt displayed" + set set_prompt_seen 1 + exp_continue + } + -re "write: Writing into executable and core files is o\[a-z\]+\\." { + verbose "Write displayed" + set set_write_seen 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $set_confirm_seen && $set_history_filename_seen + && $set_listsize_seen && $set_prompt_seen + && $set_write_seen } $gdb_test_name } } } +test_info_set_show "info set" + gdb_test "info symbol" "Argument required .address.." #test info source gdb_test "info source" "No current source file..*" @@ -591,12 +634,41 @@ gdb_test "set history" "List of \"set history\" subcommands:(\[^\r\n\]*\[\r\n\]) gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, asm, c, c.., d, fortran, go, minimal, modula-2, objective-c, opencl, pascal, rust." #test set listsize gdb_test "set listsize" "Argument required .integer to set it to.*" + +proc test_set_print { command message } { + set saw_info_header 0 + set saw_help_info 0 + set saw_command_abbrev 0 + gdb_test_multiple $command $message -lbl { + -re "\r\nList of \"set print\" subcommands:" { + verbose "Info header displayed" + set saw_info_header 1 + exp_continue + } + -re "Type \"help set print\" followed by subcommand name for full documentation\\." { + verbose "Help info displayed" + set saw_help_info 1 + exp_continue + } + -re "\r\nCommand name abbreviations are allowed if unambiguous\\." { + verbose "Command name abbreviations displayed" + set saw_command_abbrev 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_info_header && $saw_help_info + && $saw_command_abbrev } $gdb_test_name + } + } +} + #test set print "p" abbreviation -gdb_test "set p" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set print \"p\" abbreviation" +test_set_print "set p" "set print \"p\" abbreviation" #test set print "pr" abbreviation -gdb_test "set pr" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set print \"pr\" abbreviation" +test_set_print "set pr" "set print \"pr\" abbreviation" #test set print -gdb_test "set print" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." +test_set_print "set print" "set print" + #test set print address gdb_test_no_output "set print address" "set print address" #test set print array @@ -700,7 +772,7 @@ set show_conv_list \ {$_shell_exitsignal = void} \ {$_shell_exitcode = 0} \ {$_active_linker_namespaces = 1} \ - {$_current_linker_namespace = <error: No registers.>}\ + {$_linker_namespace = <error: No registers.>}\ } if [allow_python_tests] { append show_conv_list \ @@ -741,12 +813,47 @@ gdb_test "show history" "history expansion: *History expansion on command input gdb_test "show language" "The current source language is \"auto; currently c\"." #test show listsize gdb_test "show listsize" "Number of source lines gdb will list by default is 10." + +proc test_show_print { command } { + set saw_print_address 0 + set saw_print_frame_args 0 + set saw_print_symbol 0 + set saw_print_vtbl 0 + gdb_test_multiple $command $command -lbl { + -re "\r\nprint address: Printing of addresses is o\[a-z\]+\\." { + verbose "Print address displayed" + set saw_print_address 1 + exp_continue + } + -re "\r\nprint frame-arguments: Printing of non-scalar frame arguments is \[^\r\n\]+\\." { + verbose "Print frame-arguments displayed" + set saw_print_frame_args 1 + exp_continue + } + -re "\r\nprint symbol: Printing of symbols when printing pointers is o\[a-z\]+\\." { + verbose "Print symbol displayed" + set saw_print_symbol 1 + exp_continue + } + -re "\r\nprint vtbl: Printing of C\\+\\+ virtual function tables is o\[a-z\]+\\." { + verbose "Print vtbl displayed" + set saw_print_vtbl 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_print_address && $saw_print_frame_args + && $saw_print_symbol && $saw_print_vtbl } $gdb_test_name + } + } +} + #test show print "p" abbreviation -gdb_test "show p" ".*" +test_show_print "show p" #test show print "pr" abbreviation -gdb_test "show pr" ".*" +test_show_print "show pr" #test show print -gdb_test "show print" ".*" +test_show_print "show print" + #test show paths gdb_test "show paths" "Executable and object file path:.*" #test show print address @@ -792,30 +899,10 @@ gdb_test "show width" "Number of characters gdb thinks are in a line is.*" #test show write # This is only supported on targets which use exec.o. gdb_test "show write" "Writing into executable and core files is o.*" + #test show -set show_confirm_seen 0 -set show_prompt_seen 0 -gdb_test_multiple "show" "show" { - -re "confirm: *Whether to confirm potentially dangerous operations is on.(\[^\r\n\]*\[\r\n\])+history filename: *The filename in which to record the command history is (\[^\r\n\]*\[\r\n\])+history save: *Saving of the history record on exit is on.(\[^\r\n\]*\[\r\n\])+history size: *The size of the command history is(\[^\r\n\]*\[\r\n\])+listsize: *Number of source lines gdb will list by default is 10(\[^\r\n]*\[\r\n\])+print elements: *Limit on string chars or array elements to print is 200." { - verbose "Confirm displayed" - set show_confirm_seen 1 - exp_continue - } - -re "Gdb's prompt is \"$gdb_prompt \"" { - verbose "GDB prompt displayed" - set show_prompt_seen 1 - exp_continue - } - -re "Writing into executable.*$gdb_prompt $" { - verbose "write displayed" - if { $show_prompt_seen && $show_confirm_seen } { - pass "show" - } else { - verbose "prompt $show_prompt_seen confirm $show_confirm_seen" - fail "show (incomplete output)" - } - } -} +test_info_set_show "show" + #history saving should stay disabled gdb_test_no_output "set history save off" "set history save off" #test stepi "si" abbreviation diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp index 8f52199..4d3e8eb 100644 --- a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp @@ -112,7 +112,7 @@ proc_with_prefix test_conv_vars {} { gdb_test "print \$_active_linker_namespaces" "1" \ "1 namespace before starting inferior" - gdb_test "print \$_current_linker_namespace" "No registers." \ + gdb_test "print \$_linker_namespace" "No registers." \ "No current namespace before starting inferior" if { ![runto_main] } { @@ -121,7 +121,7 @@ proc_with_prefix test_conv_vars {} { gdb_test "print \$_active_linker_namespaces" "1" \ "Before activating namespaces" - gdb_test "print \$_current_linker_namespace" ".*\"\\\[\\\[0\\\]\\\]\"" \ + gdb_test "print \$_linker_namespace" ".* = 0" \ "Still in the default namespace" gdb_breakpoint "inc" allow-pending @@ -130,10 +130,16 @@ proc_with_prefix test_conv_vars {} { foreach_with_prefix dl {3 2 1} { gdb_continue_to_breakpoint "inc" - gdb_test "print \$_current_linker_namespace" ".*\"\\\[\\\[$dl\\\]\\\]\"" \ + gdb_test "print \$_linker_namespace" ".* = $dl" \ "Verify we're in namespace $dl" } + # Check that we display the namespace of the selected + # frame, not the lowermost one. + gdb_test "up" "\#1.*in main.*" + gdb_test "print \$_linker_namespace" ".* = 0" \ + "print namespace of selected frame" + gdb_continue_to_breakpoint "first dlclose" gdb_test "print \$_active_linker_namespaces" "4" "all SOs loaded" @@ -154,7 +160,7 @@ proc_with_prefix test_conv_vars {} { # breakpoints and pending breakpoints at the same time with # gdb_breakpoint. gdb_test "next" ".*assert.*" "load the first SO" - gdb_breakpoint "inc if \$_streq(\$_current_linker_namespace, \"\[\[2\]\]\")" + gdb_breakpoint "inc if \$_linker_namespace == 2" gdb_continue_to_breakpoint "inc" gdb_continue_to_end "" continue 1 } @@ -163,6 +169,12 @@ proc_with_prefix test_conv_vars {} { proc test_info_linker_namespaces {} { clean_restart $::binfile + # Check that "info linker-namespaces" while the inferior is not running + # doesn't crash. + gdb_test "info linker-namespaces" \ + "Current inferior does not support linker namespaces\\. Use \"info sharedlibrary\" instead\\." \ + "info linker-namespaces before running" + if { ![runto_main] } { return } 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/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index cb3fc90..c10941c 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -385,7 +385,7 @@ proc run_quoting_and_escaping_tests { root } { remove-symbol-file \ "target core" "target exec" "target tfile" \ "maint print c-tdesc" "save gdb-index" - "save gdb-index -dwarf-5" } + "save gdb-index -dwarf-5" "shell ls"} if { [allow_compile_tests] } { lappend all_cmds "compile file" } @@ -408,6 +408,31 @@ proc run_quoting_and_escaping_tests { root } { run_quoting_and_escaping_tests_1 $root $cmd } } + + # Some additional testing of shell command. Test 'shell' and '!' + # when there are multiple filenames on the command line. This + # focuses on completion of the final filename. There is also some + # testing of the shell command above, this tests completion within + # the line. + foreach_with_prefix shell_cmd { "shell " "!" "pipe print 1 | " } { + foreach suffix { "aaa/aa bb" "bb2/dir 1/unique file" } { + set dir $root/$suffix + + regsub -all " " "$dir" "\\ " dir_with_backslash + + with_test_prefix "suffix='$suffix'" { + with_test_prefix "with_backslash" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls $dir_with_backslash" + } + with_test_prefix "with double quotes" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls \"$dir\"" + } + with_test_prefix "with single quotes" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls '$dir'" + } + } + } + } } # Helper for run_unquoted_tests. ROOT is the root directory as setup diff --git a/gdb/testsuite/gdb.base/foll-exec-c++.exp b/gdb/testsuite/gdb.base/foll-exec-c++.exp new file mode 100644 index 0000000..d96310b --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-exec-c++.exp @@ -0,0 +1,24 @@ +# 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/>. + +# This file is part of the gdb testsuite + +# See foll-exec.exp.tcl for test details. This file runs the test +# using the C++ compiler. + +require allow_cplus_tests +set lang c++ + +source $srcdir/$subdir/foll-exec.exp.tcl diff --git a/gdb/testsuite/gdb.base/foll-exec-c.exp b/gdb/testsuite/gdb.base/foll-exec-c.exp new file mode 100644 index 0000000..67f62cc --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-exec-c.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/>. + +# This file is part of the gdb testsuite + +# See foll-exec.exp.tcl for test details. This file runs the test +# using the C compiler. + +set lang c + +source $srcdir/$subdir/foll-exec.exp.tcl diff --git a/gdb/testsuite/gdb.base/foll-exec.c b/gdb/testsuite/gdb.base/foll-exec.c index a1c9b70..291f803 100644 --- a/gdb/testsuite/gdb.base/foll-exec.c +++ b/gdb/testsuite/gdb.base/foll-exec.c @@ -19,25 +19,38 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> - +#include <libgen.h> +#include <assert.h> #include <limits.h> int global_i = 100; +#ifndef EXECD_PROG +#define EXECD_PROG "execd-prog" +#endif + int main (int argc, char ** argv) { int local_j = global_i + 1; int local_k = local_j + 1; char prog[PATH_MAX]; - int len; + size_t len = PATH_MAX - 1; + + printf ("foll-exec is about to execlp(%s)...\n", EXECD_PROG); + + prog [len] = '\0'; + + strncpy (prog, dirname (argv[0]), len); + len -= strlen (prog); + assert (len > 0); - printf ("foll-exec is about to execlp(execd-prog)...\n"); + strncat (prog, "/", len); + len -= 1; + assert (len > 0); - strcpy (prog, argv[0]); - len = strlen (prog); - /* Replace "foll-exec" with "execd-prog". */ - memcpy (prog + len - 9, "execd-prog", 10); - prog[len + 1] = 0; + strncat (prog, EXECD_PROG, len); + len -= strlen (EXECD_PROG); + assert (len > 0); /* In the following function call, maximum line length exceed the limit 80. This is intentional and required for clang compiler such that complete @@ -45,7 +58,7 @@ int main (int argc, char ** argv) multi-line. */ execlp (prog, /* tbreak-execlp */ prog, "execlp arg1 from foll-exec", (char *) 0); - printf ("foll-exec is about to execl(execd-prog)...\n"); + printf ("foll-exec is about to execl(%s)...\n", EXECD_PROG); /* In the following function call, maximum line length exceed the limit 80. This is intentional and required for clang compiler such that complete @@ -61,7 +74,7 @@ int main (int argc, char ** argv) argv[0] = prog; - printf ("foll-exec is about to execv(execd-prog)...\n"); + printf ("foll-exec is about to execv(%s)...\n", EXECD_PROG); execv (prog, argv); /* tbreak-execv */ } diff --git a/gdb/testsuite/gdb.base/foll-exec.exp b/gdb/testsuite/gdb.base/foll-exec.exp.tcl index ad4c3516..8f96a55 100644 --- a/gdb/testsuite/gdb.base/foll-exec.exp +++ b/gdb/testsuite/gdb.base/foll-exec.exp.tcl @@ -22,33 +22,55 @@ require {istarget "*-linux*"} standard_testfile foll-exec.c -set testfile2 "execd-prog" -set srcfile2 ${testfile2}.c -set binfile2 [standard_output_file ${testfile2}] +# Compile a program that performs an exec as EXECER_LANG, and a +# program that will be exec'd as EXECEE_LANG. Either language can be +# 'c' or 'c++'. Then run various test associated with 'catch exec' +# using the compiled programs. +proc do_exec_tests { execer_lang execee_lang } { + global srcfile testfile + global gdb_prompt -set compile_options debug + # First compile the program to be exec'd, the execee. + set execee_base_filename "execd-prog" + set srcfile2 ${execee_base_filename}.c + set execee_testfile "execd-prog-${execee_lang}" + set execee_testfile_re [string_to_regexp $execee_testfile] + set execee_binfile [standard_output_file $execee_testfile] -# build the first test case -if { [gdb_compile "${srcdir}/${subdir}/${srcfile2}" "${binfile2}" executable $compile_options] != "" } { - untested "failed to compile" - return -1 -} + set execee_flags debug + if { $execee_lang == "c++" } { + lappend execee_flags "c++" + } -if { [is_remote target] } { - gdb_remote_download target $binfile2 -} + if { [build_executable "failed to build $execee_testfile" $execee_testfile \ + $srcfile2 $execee_flags] == -1 } { + return + } -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $compile_options] != "" } { - untested "failed to compile" - return -1 -} + if { [is_remote target] } { + gdb_remote_download target $execee_binfile + } -proc do_exec_tests {} { - global binfile srcfile srcfile2 testfile testfile2 - global gdb_prompt + + # Now compile the program to do the exec, the execer. + set execer_testfile "$testfile-${execee_lang}" + set execer_binfile [standard_output_file $execer_testfile] + + set execer_flags debug + if { $execer_lang == "c++" } { + lappend execer_flags "c++" + } + lappend execer_flags "additional_flags=-DEXECD_PROG=\"${execee_testfile}\"" + + if { [build_executable "failed to build $execer_testfile" $execer_testfile \ + $srcfile $execer_flags] == -1 } { + return + } + + # Now we can start running the tests. + clean_restart $execer_binfile # Start the program running, and stop at main. - # if {![runto_main]} { return } @@ -71,7 +93,7 @@ proc do_exec_tests {} { return } - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -120,7 +142,7 @@ proc do_exec_tests {} { set execd_line [gdb_get_line_number "after-exec" $srcfile2] send_gdb "next\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execlp call"} -re "$gdb_prompt $" {fail "step through execlp call"} timeout {fail "(timeout) step through execlp call"} @@ -160,7 +182,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -193,7 +215,7 @@ proc do_exec_tests {} { send_gdb "continue\n" gdb_expect { - -re ".*xecuting new program:.*${testfile2}.*Catchpoint .*(exec\'d .*${testfile2}).*$gdb_prompt $"\ + -re ".*xecuting new program:.*${execee_testfile_re}.*Catchpoint .*(exec\'d .*${execee_testfile_re}).*$gdb_prompt $"\ {pass "hit catch exec"} -re "$gdb_prompt $" {fail "hit catch exec"} timeout {fail "(timeout) hit catch exec"} @@ -210,7 +232,7 @@ proc do_exec_tests {} { # set msg "info shows catchpoint exec pathname" gdb_test_multiple "info breakpoints" $msg { - -re ".*catchpoint.*keep y.*exec, program \".*${testfile2}\".*$gdb_prompt $" { + -re ".*catchpoint.*keep y.*exec, program \".*${execee_testfile_re}\".*$gdb_prompt $" { pass $msg } } @@ -228,7 +250,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -269,7 +291,7 @@ proc do_exec_tests {} { # send_gdb "next 2\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execl call"} -re "$gdb_prompt $" {fail "step through execl call"} timeout {fail "(timeout) step through execl call"} @@ -295,7 +317,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -330,7 +352,7 @@ proc do_exec_tests {} { } send_gdb "next\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execv call"} -re "$gdb_prompt $" {fail "step through execv call"} timeout {fail "(timeout) step through execv call"} @@ -356,7 +378,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -370,13 +392,13 @@ proc do_exec_tests {} { # send_gdb "continue\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "continue through exec"} -re "$gdb_prompt $" {fail "continue through exec"} timeout {fail "(timeout) continue through exec"} } } -clean_restart $binfile - -do_exec_tests +foreach_with_prefix execee_lang { c c++ } { + do_exec_tests $lang $execee_lang +} 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..21ef334 --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-fork-syscall.exp @@ -0,0 +1,143 @@ +# 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-*-*"} +require allow_fork_tests + +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/foll-fork.exp b/gdb/testsuite/gdb.base/foll-fork.exp index 94755c6..12db516 100644 --- a/gdb/testsuite/gdb.base/foll-fork.exp +++ b/gdb/testsuite/gdb.base/foll-fork.exp @@ -17,6 +17,8 @@ # enabled as it will be redirected to the log. require !gdb_debug_enabled +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile debug]} { diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp index 266df46..6ca7711 100644 --- a/gdb/testsuite/gdb.base/foll-vfork.exp +++ b/gdb/testsuite/gdb.base/foll-vfork.exp @@ -18,12 +18,7 @@ # either execs or exits --- since those events take somewhat different # code paths in GDB, both variants are exercised. -# Until "set follow-fork-mode" and "catch vfork" are implemented on -# other targets... -# -if {![istarget "*-linux*"]} { - continue -} +require allow_fork_tests standard_testfile .c -exit.c vforked-prog.c diff --git a/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp b/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp index 311d7ba..2d47d5d 100644 --- a/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp +++ b/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp @@ -23,6 +23,7 @@ # in the source of the shlib, and "list" should display the source where # the program stopped. +require allow_fork_tests require allow_shlib_tests standard_testfile .c -shlib.c diff --git a/gdb/testsuite/gdb.base/fork-print-inferior-events.exp b/gdb/testsuite/gdb.base/fork-print-inferior-events.exp index 26ed2f9..19ace00 100644 --- a/gdb/testsuite/gdb.base/fork-print-inferior-events.exp +++ b/gdb/testsuite/gdb.base/fork-print-inferior-events.exp @@ -19,6 +19,8 @@ # inferior-events [on,off]', 'set follow-fork-mode [child,parent]' and # 'set detach-on-fork [on,off]' are the correct ones. +require allow_fork_tests + # This test relies on "run", so it cannot run on target remote stubs. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.base/fork-running-state.exp b/gdb/testsuite/gdb.base/fork-running-state.exp index 4b810a6..c446800 100644 --- a/gdb/testsuite/gdb.base/fork-running-state.exp +++ b/gdb/testsuite/gdb.base/fork-running-state.exp @@ -17,6 +17,8 @@ # in non-stop). GDB used to miss updating the parent/child running # states after a fork. +require allow_fork_tests + standard_testfile # The test proper. 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/infcall-failure.exp b/gdb/testsuite/gdb.base/infcall-failure.exp index 66bccd1..e7aeac1 100644 --- a/gdb/testsuite/gdb.base/infcall-failure.exp +++ b/gdb/testsuite/gdb.base/infcall-failure.exp @@ -131,7 +131,13 @@ proc_with_prefix run_cond_hits_segfault_test { async_p non_stop_p } { [multi_line \ "Continuing\\." \ "" \ - "Program received signal SIGSEGV, Segmentation fault\\." \ + [string cat \ + [string_to_regexp \ + "Program received signal SIGSEGV, Segmentation fault."] \ + "("] \ + [string cat \ + [string_to_regexp "Address not mapped to object."] \ + ")?"] \ "${::hex} in func_segfault \\(\\) at \[^\r\n\]+:${::segv_line}" \ "${::decimal}\\s+\[^\r\n\]+Segfault here\[^\r\n\]+" \ "Error in testing condition for breakpoint ${bp_1_num}:" \ @@ -161,7 +167,13 @@ proc_with_prefix run_call_hits_segfault_test { async_p non_stop_p } { gdb_test "call func_segfault ()" \ [multi_line \ "" \ - "Program received signal SIGSEGV, Segmentation fault\\." \ + [string cat \ + [string_to_regexp \ + "Program received signal SIGSEGV, Segmentation fault."] \ + "("] \ + [string cat \ + [string_to_regexp "Address not mapped to object."] \ + ")?"] \ "${::hex} in func_segfault \\(\\) at \[^\r\n\]+:${::segv_line}" \ "${::decimal}\\s+\[^\r\n\]+Segfault here\[^\r\n\]+" \ "The program being debugged was signaled while in a function called from GDB\\." \ diff --git a/gdb/testsuite/gdb.base/inferior-died.exp b/gdb/testsuite/gdb.base/inferior-died.exp index 3992561..764a88d 100644 --- a/gdb/testsuite/gdb.base/inferior-died.exp +++ b/gdb/testsuite/gdb.base/inferior-died.exp @@ -13,10 +13,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/>. -# Until "set follow-fork-mode" and "catch fork" are implemented on -# other targets... -# -require {istarget "*-*-linux*"} +require allow_fork_tests require support_displaced_stepping diff --git a/gdb/testsuite/gdb.base/info-shared.exp b/gdb/testsuite/gdb.base/info-shared.exp index 6f1b2d6..e81b28e 100644 --- a/gdb/testsuite/gdb.base/info-shared.exp +++ b/gdb/testsuite/gdb.base/info-shared.exp @@ -79,6 +79,9 @@ proc check_info_shared { test expect1 expect2 } { } } +# Check that "info shared" before running doesn't crash. +check_info_shared "info sharedlibrary before running" 0 0 + # Start the inferior, and check neither of the libraries are loaded at # the start. if ![runto_main] { diff --git a/gdb/testsuite/gdb.base/interrupt-daemon.exp b/gdb/testsuite/gdb.base/interrupt-daemon.exp index 161f854..8b8c61d 100644 --- a/gdb/testsuite/gdb.base/interrupt-daemon.exp +++ b/gdb/testsuite/gdb.base/interrupt-daemon.exp @@ -16,6 +16,8 @@ # Make sure that we can interrupt an inferior that forks and moves to # its own session. +require allow_fork_tests + standard_testfile if {[build_executable "failed to build" $testfile $srcfile {debug}]} { 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-fork.exp b/gdb/testsuite/gdb.base/jit-elf-fork.exp index 81d3350..c1fa428 100644 --- a/gdb/testsuite/gdb.base/jit-elf-fork.exp +++ b/gdb/testsuite/gdb.base/jit-elf-fork.exp @@ -15,6 +15,7 @@ # Test fork handling of an inferior that has JIT-ed objfiles. +require allow_fork_tests require allow_shlib_tests load_lib jit-elf-helpers.exp 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/kill-detach-inferiors-cmd.exp b/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp index ef4bb88..57ec330 100644 --- a/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp +++ b/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp @@ -19,6 +19,7 @@ # commands. require can_spawn_for_attach +require allow_multi_inferior_tests standard_testfile set executable $testfile diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp index e006d55..7936e53 100644 --- a/gdb/testsuite/gdb.base/maint.exp +++ b/gdb/testsuite/gdb.base/maint.exp @@ -52,6 +52,40 @@ if {[prepare_for_testing "failed to prepare" $testfile \ return -1 } +# Check "maint set per-command" warnings. We do this early so that +# the following tests don't need to expect them, as GDB only warns +# once. + +with_test_prefix "warnings" { + # Potential warning given by "maint set per-command time". + set maybe_per_command_warning \ + "(?:warning: per-thread run time information not available on this platform)?" + + # This one should not issue the "per-command time" warning. + with_test_prefix "per-command space" { + gdb_test_no_output "mt set per-command space on" + gdb_test_no_output "mt set per-command space off" + } + + # These might warn. "per-command on" enables all sub commands, so + # might trigger the "per-command time" warning. + foreach cmd {"per-command" "per-command time"} { + with_test_prefix $cmd { + # GDB only warns once, so restart between commands. + clean_restart $binfile + gdb_test "mt set $cmd on" "$maybe_per_command_warning" + gdb_test "mt set $cmd off" "command started" + gdb_test_no_output "mt set $cmd on" \ + "mt set $cmd on, again" + gdb_test "mt set $cmd off" "command started" \ + "mt set $cmd off, again" + } + } + + # We've already warned once above, so the following tests don't + # need to expect the warning. +} + set readnow_p [readnow] # The commands we test here produce many lines of output; disable "press @@ -205,8 +239,8 @@ set re \ "( Number of \"partial\" symbols read: $decimal" \ ")?( Number of psym tables \\(not yet expanded\\): $decimal" \ ")?( Total memory used for psymbol cache: $decimal" \ - ")?( Number of read CUs: $decimal" \ - " Number of unread CUs: $decimal" \ + ")?( Number of read units: $decimal" \ + " Number of unread units: $decimal" \ ")? Total memory used for objfile obstack: $decimal" \ " Total memory used for BFD obstack: $decimal" \ " Total memory used for string cache: $decimal" \ @@ -513,4 +547,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/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp index 61a240f..3facccb 100644 --- a/gdb/testsuite/gdb.base/multi-forks.exp +++ b/gdb/testsuite/gdb.base/multi-forks.exp @@ -13,11 +13,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/>. -# Until "set follow-fork-mode" and "catch fork" are implemented on -# other targets... -# -require {istarget "*-*-linux*"} - +require allow_fork_tests standard_testfile .c 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/pie-fork.exp b/gdb/testsuite/gdb.base/pie-fork.exp index 48c01d9..86407b4 100644 --- a/gdb/testsuite/gdb.base/pie-fork.exp +++ b/gdb/testsuite/gdb.base/pie-fork.exp @@ -16,6 +16,8 @@ # Test that we can follow forks properly when the executable is # position-independent. +require allow_fork_tests + standard_testfile set opts [list debug pie] 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/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp index 22913ca..eaee010 100644 --- a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp +++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp @@ -49,6 +49,11 @@ if {[build_executable "failed to prepare" $testfile $srcfile]} { # - run: use the run command # - attach: start a process outside of GDB and attach it proc do_test { action1 action2 } { + + if {$action1 == "add" && ![allow_multi_inferior_tests]} { + return + } + save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"maintenance set target-non-stop on\"" clean_restart $::binfile diff --git a/gdb/testsuite/gdb.base/sigall.exp b/gdb/testsuite/gdb.base/sigall.exp index b23e3c5..461a92b 100644 --- a/gdb/testsuite/gdb.base/sigall.exp +++ b/gdb/testsuite/gdb.base/sigall.exp @@ -41,13 +41,14 @@ proc test_one_sig {nextsig} { setup_xfail "i*86-pc-linuxoldld-gnu" "i*86-pc-linuxaout-gnu" } # On Linux SPARC64 systems SIGLOST==SIGPWR and gdb identifies - # the raised signal as PWR. - if {$thissig == "LOST" && [istarget "sparc64-*-linux*"]} { + # the raised signal as PWR. Same for Cygwin. + if {$thissig == "LOST" + && ([istarget "sparc64-*-linux*"] || [istarget "*-*-cygwin*"])} { set esig "PWR" } gdb_test "continue" \ - "Continuing.*Program received signal SIG$esig.*" \ + "Continuing.* received signal SIG$esig.*" \ "get signal $esig" } @@ -177,7 +178,7 @@ gdb_test "handle SIGTERM stop print" \ "SIGTERM\[ \t\]*Yes\[ \t\]*Yes\[ \t\]*Yes.*" gdb_test "b handle_TERM" "Breakpoint \[0-9\]+ .*" gdb_test "continue" \ - "Continuing.*Program received signal SIGTERM.*" \ + "Continuing.* received signal SIGTERM.*" \ "get signal TERM" gdb_test "continue" "Breakpoint.*handle_TERM.*" "send signal TERM" gdb_continue_to_end "continue to sigall exit" 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/step-over-exit.exp b/gdb/testsuite/gdb.base/step-over-exit.exp index 2370f97..6dfa7bb 100644 --- a/gdb/testsuite/gdb.base/step-over-exit.exp +++ b/gdb/testsuite/gdb.base/step-over-exit.exp @@ -13,11 +13,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -standard_testfile - # Test a thread is doing step-over a syscall instruction which is exit, # and GDBserver should cleanup its state of step-over properly. +# The testcase relies on follow-fork-mode child. +require allow_fork_tests + +standard_testfile + set syscall_insn "" # Define the syscall instruction for each target. diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index 59c93ee..503671b 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" } } @@ -739,6 +751,280 @@ proc test_enable_styling_warning { } { } } +# Run an 'apropos' command. Each line of output starts with a +# non-default style (command style). Ensure that pagination triggers +# during the 'apropos' output such that, at the point pagination kicks +# in, a non-default style is in effect. +# +# Then, at the pagination prompt, quit the command. +# +# Next, run a command which switches to a different style, and then +# back to the current style. +# +# At one point, a bug in the pagination code would leave the +# non-default style from the 'apropos' command recorded as the current +# style, such that the second command would switch back to the earlier +# style. +proc test_pagination_cmd_after_quit_styling {} { + with_ansi_styling_terminal { + clean_restart + } + + # We're going to use 'apropos time'. Check that with a height of + # 12 lines, each line starts with a non-default style, and that we + # do see the pagination prompt. This means that there are more + # than 12 lines for this command. + with_test_prefix "validate apropos output" { + gdb_test_no_output "set height 12" + + set saw_pagination_prompt false + gdb_test_multiple "apropos time" "" { + -re "^apropos time\r\n" { + exp_continue + } + -re "^\033\\\[39;49;1;27m\[^\r\n\]+\r\n" { + exp_continue + } + -re "^$::pagination_prompt$" { + set saw_pagination_prompt true + send_gdb "q\n" + exp_continue + } + -re "^q\r\n" { + exp_continue + } + -re "^Quit\r\n" { + exp_continue + } + -re "^$::gdb_prompt $" { + gdb_assert { $saw_pagination_prompt } $gdb_test_name + } + -re "^\[^\r\n\]+\r\n" { + exp_continue + } + } + } + + # Now reduce the height to 10 and re-run 'apropos time'. Based on + # the previous check, we know that this is going to present the + # pagination prompt when a non-default style is in use. + gdb_test_no_output "set height 10" + + set saw_pagination_prompt false + gdb_test_multiple "apropos time" "" { + -re "$::pagination_prompt" { + set saw_pagination_prompt true + send_gdb "q\n" + exp_continue + } + -re "\r\n$::gdb_prompt $" { + gdb_assert { $saw_pagination_prompt } $gdb_test_name + } + } + + # The help output for this maintenance command switches to a + # different style, and then back to the default. If the + # pagination bug still exists, then this would switch back to the + # non-default style that was in use when pagination kicked in + # above. + gdb_test "maintenance time" \ + "^\"\033\\\[39;49;1;27mmaintenance time\033\\\[m\" takes a numeric argument\\." +} + +# Helper for test_pagination_prompt_styling. Return false if STR, a +# line that appears immediately before a pagination prompt, matches +# the pattern for needing a style reset at the end, but does not have +# the style reset. +# +# In all other cases, return true. So lines that don't match the +# known pattern for neededing a style reset will always return true, +# as will lines that match the pattern, and do have the style reset. +proc previous_line_is_ok { str } { + + # Create a copy of STR with all the '\033' characters removed. + # Then compare string lengths to get a count of the '\033' + # charactes present in STR. + regsub -all "\033" $str {} stripped + set count [expr [string length $str] - [string length $stripped]] + + # If STR switched styles, then it _must_ switch back again, + # otherwise the pagination prompt will be in the wrong style. + # This means that there _must_ be an even number of '\033' + # characters in STR. If there is not then we switched style, but + # failed to switch back. + if { [expr $count % 2] != 0 } { + return false + } + + # For lines that don't match this pattern, we cannot comment on + # where the style reset should occur, so lets just claim the line + # is fine. + if { ![regexp "\\s+$::hex - $::hex is \[^\r\n\]+ in " $str] } { + return true + } + + # This line did match the above pattern, so we know that a style + # reset _must_ occur at the end of the line. If it doesn't then + # this line is not OK. + if { ![regexp "\033\\\[m$" $str] } { + return false + } + + # All tests passed, this line looks OK. + return true +} + +# Test that the pagination prompt is displayed unstyled. This is done +# by looking at the 'info files' output and selecting a width that +# will mean we should get a pagination prompt part way through a +# styled filename. +# +# Then, re-run 'info files' and check that for every pagination +# prompt, the previous line disables styling as expected. +proc test_pagination_prompt_styling {} { + with_ansi_styling_terminal { + clean_restart $::binfile + } + + if {![runto_main]} { + return + } + + # Set height so we actually get a pagination prompt. + gdb_test_no_output "set height 3" + + # Scan the 'info files' output and set DESIRED_WIDTH such that it + # will trigger pagination part-way through a styled filename. + set desired_width 0 + gdb_test_multiple "info files" "find good test width" { + -re "^info files\r\n" { + exp_continue + } + + -re "^$::pagination_prompt$" { + send_gdb "\n" + exp_continue + } + + -re "^$::gdb_prompt $" { + } + + -re "^((\\s+$::hex - $::hex is \[^\r\n\]+ in )\[^\r\n\]+)\r\n" { + if { $desired_width == 0 } { + set full_line $expect_out(1,string) + set inner_line $expect_out(2,string) + set desired_width [expr [string length $inner_line] + ([string length $full_line] - [string length $inner_line]) / 2] + } + exp_continue + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Now setup the screen width. + gdb_test_no_output "set width $desired_width" + + # Re-run 'info files'. Check that the content before any + # pagination prompt correctly disables styling. + set saw_bad_line false + set prev_line "" + gdb_test_multiple "info files" "check pagination prompt styling" { + -re "^info files\r\n" { + exp_continue + } + + -re "^$::pagination_prompt$" { + if { ![previous_line_is_ok $prev_line] } { + set saw_bad_line true + } + send_gdb "\n" + exp_continue + } + + -re "^(\[^\r\n\]+)$::pagination_prompt$" { + set prev_line $expect_out(1,string) + if { ![previous_line_is_ok $prev_line] } { + set saw_bad_line true + } + send_gdb "\n" + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_line } $gdb_test_name + } + + -re "^(\[^\r\n\]*)\r\n" { + set prev_line $expect_out(1,string) + exp_continue + } + } +} + +# Test that GDB can correctly restore the current style after a +# pagination prompt. +# +# Set the logging file to a garbage string based on LENGTH (is +# actually 2x LENGTH), then 'show logging file'. Press return at the +# pagination prompt, and check that the reset of the filename is +# styled correctly, and that GDB correctly switches back to the +# default style once the logging file has finished. +proc test_pagination_continue_styling_1 { length } { + with_ansi_styling_terminal { + clean_restart $::binfile + } + + set filename [string repeat "ax" $length] + + gdb_test_no_output "set logging file $filename" + + gdb_test_no_output "set height 3" + gdb_test_no_output "set width 80" + + set saw_bad_styling false + gdb_test_multiple "show logging file" "" { + -re "^show logging file\r\n" { + exp_continue + } + + -re "^The current logfile is \"\033\\\[32;49;22;27m(?:ax)+\033\\\[m" { + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+\033\\\[m(?=--)" { + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+(?=--)" { + set saw_bad_styling true + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+\033\\\[m\"\\.\r\n" { + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_styling } $gdb_test_name + } + + -re "^$::pagination_prompt$$" { + send_gdb "\n" + exp_continue + } + } +} + +# Wrapper around test_pagination_continue_styling_1, calls that +# function with different lengths. +proc test_pagination_continue_styling { } { + foreach_with_prefix length { 80 160 } { + test_pagination_continue_styling_1 $length + } +} + # Check to see if the Python styling of disassembler output is # expected or not, this styling requires Python support in GDB, and # the Python pygments module to be available. @@ -781,3 +1067,6 @@ test_colorsupport_truecolor test_colorsupport_truecolor_only test_enable_styling_warning +test_pagination_cmd_after_quit_styling +test_pagination_prompt_styling +test_pagination_continue_styling diff --git a/gdb/testsuite/gdb.base/tls-common.exp.tcl b/gdb/testsuite/gdb.base/tls-common.exp.tcl new file mode 100644 index 0000000..7aa7f46 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-common.exp.tcl @@ -0,0 +1,50 @@ +# Copyright 2024 Free Software Foundation, Inc. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# Require statement, variables and procs used by tls-nothreads.exp, +# tls-multiobj.exp, and tls-dlobj.exp. + +# The tests listed above are known to work for the targets listed on +# the 'require' line, below. +# +# At the moment, only the Linux target is listed, but, ideally, these +# tests should be run on other targets too. E.g, testing on FreeBSD +# shows many failures which should be addressed in some fashion before +# enabling it for that target. + +require {is_any_target "*-*-linux*"} + +# These are the targets which have support for internal TLS lookup: + +set internal_tls_linux_targets {"x86_64-*-linux*" "aarch64-*-linux*" + "riscv*-*-linux*" "powerpc64*-*-linux*" + "s390x*-*-linux*"} + +# The "maint set force-internal-tls-address-lookup" command is only +# available for certain Linux architectures. Don't attempt to force +# use of internal TLS support for architectures which don't support +# it. + +if [is_any_target {*}$internal_tls_linux_targets] { + set internal_tls_iters { false true } +} else { + set internal_tls_iters { false } +} + +# Set up a kfail with message KFAIL_MSG when KFAIL_COND holds, then +# issue gdb_test with command CMD and regular expression RE. + +proc gdb_test_with_kfail {cmd re kfail_cond kfail_msg} { + if [uplevel 1 [list expr $kfail_cond]] { + setup_kfail $kfail_msg *-*-* + } + gdb_test $cmd $re +} diff --git a/gdb/testsuite/gdb.base/tls-dlobj-lib.c b/gdb/testsuite/gdb.base/tls-dlobj-lib.c new file mode 100644 index 0000000..c69bab7 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-dlobj-lib.c @@ -0,0 +1,87 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* This program needs to be compiled with preprocessor symbol set to + a small integer, e.g. "gcc -DN=1 ..." With N defined, the CONCAT2 + and CONCAT3 macros will construct suitable names for the global + variables and functions. */ + +#define CONCAT2(a,b) CONCAT2_(a,b) +#define CONCAT2_(a,b) a ## b + +#define CONCAT3(a,b,c) CONCAT3_(a,b,c) +#define CONCAT3_(a,b,c) a ## b ## c + +/* For N=1, this ends up being... + __thread int tls_lib1_tbss_1; + __thread int tls_lib1_tbss_2; + __thread int tls_lib1_tdata_1 = 196; + __thread int tls_lib1_tdata_2 = 197; */ + +__thread int CONCAT3(tls_lib, N, _tbss_1); +__thread int CONCAT3(tls_lib, N, _tbss_2); +__thread int CONCAT3(tls_lib, N, _tdata_1) = CONCAT2(N, 96); +__thread int CONCAT3(tls_lib, N, _tdata_2) = CONCAT2(N, 97); + +/* Substituting for N, define function: + + int get_tls_libN_var (int which) . */ + +int +CONCAT3(get_tls_lib, N, _var) (int which) +{ + switch (which) + { + case 0: + return -1; + case 1: + return CONCAT3(tls_lib, N, _tbss_1); + case 2: + return CONCAT3(tls_lib, N, _tbss_2); + case 3: + return CONCAT3(tls_lib, N, _tdata_1); + case 4: + return CONCAT3(tls_lib, N, _tdata_2); + } + return -1; +} + +/* Substituting for N, define function: + + void set_tls_libN_var (int which, int val) . */ + +void +CONCAT3(set_tls_lib, N, _var) (int which, int val) +{ + switch (which) + { + case 0: + break; + case 1: + CONCAT3(tls_lib, N, _tbss_1) = val; + break; + case 2: + CONCAT3(tls_lib, N, _tbss_2) = val; + break; + case 3: + CONCAT3(tls_lib, N, _tdata_1) = val; + break; + case 4: + CONCAT3(tls_lib, N, _tdata_2) = val; + break; + } +} diff --git a/gdb/testsuite/gdb.base/tls-dlobj.c b/gdb/testsuite/gdb.base/tls-dlobj.c new file mode 100644 index 0000000..322bdda --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-dlobj.c @@ -0,0 +1,311 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +typedef void (*setter_ftype) (int which, int val); + +__thread int tls_main_tbss_1; +__thread int tls_main_tbss_2; +__thread int tls_main_tdata_1 = 96; +__thread int tls_main_tdata_2 = 97; + +extern void set_tls_lib10_var (int which, int val); +extern void set_tls_lib11_var (int which, int val); + +volatile int data; + +static void +set_tls_main_var (int which, int val) +{ + switch (which) + { + case 1: + tls_main_tbss_1 = val; + break; + case 2: + tls_main_tbss_2 = val; + break; + case 3: + tls_main_tdata_1 = val; + break; + case 4: + tls_main_tdata_2 = val; + break; + } +} + +void +use_it (int a) +{ + data = a; +} + +static void * +load_dso (char *dso_name, int n, setter_ftype *setterp) +{ + char buf[80]; + void *sym; + void *handle = dlopen (dso_name, RTLD_NOW | RTLD_GLOBAL); + if (handle == NULL) + { + fprintf (stderr, "dlopen of DSO '%s' failed: %s\n", dso_name, dlerror ()); + exit (1); + } + sprintf (buf, "set_tls_lib%d_var", n); + sym = dlsym (handle, buf); + assert (sym != NULL); + *setterp = sym; + + /* Some libc implementations (for some architectures) refuse to + initialize TLS data structures (specifically, the DTV) without + first calling dlsym on one of the TLS symbols. */ + sprintf (buf, "tls_lib%d_tdata_1", n); + assert (dlsym (handle, buf) != NULL); + + return handle; +} + +int +main (int argc, char **argv) +{ + int i, status; + setter_ftype s0, s1, s2, s3, s4, s10, s11; + void *h1 = load_dso (OBJ1, 1, &s1); + void *h2 = load_dso (OBJ2, 2, &s2); + void *h3 = load_dso (OBJ3, 3, &s3); + void *h4 = load_dso (OBJ4, 4, &s4); + s0 = set_tls_main_var; + s10 = set_tls_lib10_var; + s11 = set_tls_lib11_var; + + use_it (0); /* main-breakpoint-1 */ + + /* Set TLS variables in main program and all libraries. */ + for (i = 1; i <= 4; i++) + s0 (i, 10 + i); + for (i = 1; i <= 4; i++) + s1 (i, 110 + i); + for (i = 1; i <= 4; i++) + s2 (i, 210 + i); + for (i = 1; i <= 4; i++) + s3 (i, 310 + i); + for (i = 1; i <= 4; i++) + s4 (i, 410 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1010 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1110 + i); + + use_it (0); /* main-breakpoint-2 */ + + /* Unload lib2 and lib3. */ + status = dlclose (h2); + assert (status == 0); + status = dlclose (h3); + assert (status == 0); + + /* Set TLS variables in main program and in libraries which are still + loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 20 + i); + for (i = 1; i <= 4; i++) + s1 (i, 120 + i); + for (i = 1; i <= 4; i++) + s4 (i, 420 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1020 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1120 + i); + + use_it (0); /* main-breakpoint-3 */ + + /* Load lib3. */ + h3 = load_dso (OBJ3, 3, &s3); + + /* Set TLS vars again; currently, only lib2 is not loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 30 + i); + for (i = 1; i <= 4; i++) + s1 (i, 130 + i); + for (i = 1; i <= 4; i++) + s3 (i, 330 + i); + for (i = 1; i <= 4; i++) + s4 (i, 430 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1030 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1130 + i); + + use_it (0); /* main-breakpoint-4 */ + + /* Unload lib1 and lib4; load lib2. */ + status = dlclose (h1); + assert (status == 0); + status = dlclose (h4); + assert (status == 0); + h2 = load_dso (OBJ2, 2, &s2); + + /* Set TLS vars; currently, lib2 and lib3 are loaded, + lib1 and lib4 are not. */ + for (i = 1; i <= 4; i++) + s0 (i, 40 + i); + for (i = 1; i <= 4; i++) + s2 (i, 240 + i); + for (i = 1; i <= 4; i++) + s3 (i, 340 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1040 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1140 + i); + + use_it (0); /* main-breakpoint-5 */ + + /* Load lib4 and lib1. Unload lib2. */ + h4 = load_dso (OBJ4, 4, &s4); + h1 = load_dso (OBJ1, 1, &s1); + status = dlclose (h2); + assert (status == 0); + + /* Set TLS vars; currently, lib1, lib3, and lib4 are loaded; + lib2 is not loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 50 + i); + for (i = 1; i <= 4; i++) + s1 (i, 150 + i); + for (i = 1; i <= 4; i++) + s3 (i, 350 + i); + for (i = 1; i <= 4; i++) + s4 (i, 450 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1050 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1150 + i); + + use_it (0); /* main-breakpoint-6 */ + + /* Load lib2, unload lib1, lib3, and lib4; then load lib3 again. */ + h2 = load_dso (OBJ2, 2, &s2); + status = dlclose (h1); + assert (status == 0); + status = dlclose (h3); + assert (status == 0); + status = dlclose (h4); + assert (status == 0); + h3 = load_dso (OBJ3, 3, &s3); + + /* Set TLS vars; currently, lib2 and lib3 are loaded; + lib1 and lib4 are not loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 60 + i); + for (i = 1; i <= 4; i++) + s2 (i, 260 + i); + for (i = 1; i <= 4; i++) + s3 (i, 360 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1060 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1160 + i); + + use_it (0); /* main-breakpoint-7 */ + + /* Unload lib3 and lib2, then (re)load lib4, lib3, lib2, and lib1, + in that order. */ + status = dlclose (h3); + assert (status == 0); + status = dlclose (h2); + assert (status == 0); + h4 = load_dso (OBJ4, 4, &s4); + h3 = load_dso (OBJ3, 3, &s3); + h2 = load_dso (OBJ2, 2, &s2); + h1 = load_dso (OBJ1, 1, &s1); + + /* Set TLS vars; currently, lib1, lib2, lib3, and lib4 are all + loaded. */ + for (i = 1; i <= 4; i++) + s0 (i, 70 + i); + for (i = 1; i <= 4; i++) + s1 (i, 170 + i); + for (i = 1; i <= 4; i++) + s2 (i, 270 + i); + for (i = 1; i <= 4; i++) + s3 (i, 370 + i); + for (i = 1; i <= 4; i++) + s4 (i, 470 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1070 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1170 + i); + + use_it (0); /* main-breakpoint-8 */ + + /* Unload lib3, lib1, and lib4. */ + status = dlclose (h3); + assert (status == 0); + status = dlclose (h1); + assert (status == 0); + status = dlclose (h4); + assert (status == 0); + + /* Set TLS vars; currently, lib2 is loaded; lib1, lib3, and lib4 are + not. */ + for (i = 1; i <= 4; i++) + s0 (i, 80 + i); + for (i = 1; i <= 4; i++) + s2 (i, 280 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1080 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1180 + i); + + use_it (0); /* main-breakpoint-9 */ + + /* Load lib3, unload lib2, load lib4. */ + h3 = load_dso (OBJ3, 3, &s3); + status = dlclose (h2); + assert (status == 0); + h4 = load_dso (OBJ4, 4, &s4); + + /* Set TLS vars; currently, lib3 and lib4 are loaded; lib1 and lib2 + are not. */ + for (i = 1; i <= 4; i++) + s0 (i, 90 + i); + for (i = 1; i <= 4; i++) + s3 (i, 390 + i); + for (i = 1; i <= 4; i++) + s4 (i, 490 + i); + for (i = 1; i <= 4; i++) + s10 (i, 1090 + i); + for (i = 1; i <= 4; i++) + s11 (i, 1190 + i); + + use_it (0); /* main-breakpoint-10 */ + + /* Attempt to keep variables in the main program from being optimized + away. */ + use_it (tls_main_tbss_1); + use_it (tls_main_tbss_2); + use_it (tls_main_tdata_1); + use_it (tls_main_tdata_2); + + use_it (100); /* main-breakpoint-last */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/tls-dlobj.exp b/gdb/testsuite/gdb.base/tls-dlobj.exp new file mode 100644 index 0000000..02f2ff8 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-dlobj.exp @@ -0,0 +1,378 @@ +# Copyright 2024 Free Software Foundation, Inc. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# Test that the GDB-internal TLS link map to module id mapping code +# works correctly when debugging a program which is linked against +# shared objects and which also loads and unloads other shared objects +# in different orders. For targets which have GDB-internal TLS +# support, it'll check both GDB-internal TLS support as well as that +# provided by a helper library such as libthread_db. + +source $srcdir/$subdir/tls-common.exp.tcl + +require allow_shlib_tests + +standard_testfile + +set libsrc "${srcdir}/${subdir}/${testfile}-lib.c" + +# These will be dlopen'd: +set lib1obj [standard_output_file "${testfile}1-lib.so"] +set lib2obj [standard_output_file "${testfile}2-lib.so"] +set lib3obj [standard_output_file "${testfile}3-lib.so"] +set lib4obj [standard_output_file "${testfile}4-lib.so"] + +# These will be dynamically linked with the main program: +set lib10obj [standard_output_file "${testfile}10-lib.so"] +set lib11obj [standard_output_file "${testfile}11-lib.so"] + +# Due to problems with some versions of glibc, we expect some tests to +# fail due to TLS storage not being allocated/initialized. Test +# command CMD using regular expression RE, and use XFAIL instead of +# FAIL when the relevant RE is matched and COND is true when evaluated +# in the upper level. + +proc gdb_test_with_xfail { cmd re cond} { + gdb_test_multiple $cmd $cmd { + -re -wrap $re { + pass $gdb_test_name + } + -re -wrap "The inferior has not yet allocated storage for thread-local variables.*" { + if [ uplevel 1 [list expr $cond]] { + xfail $gdb_test_name + } else { + fail $gdb_test_name + } + } + } +} + +proc do_tests {force_internal_tls} { + clean_restart $::binfile + if ![runto_main] { + return + } + + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"] + gdb_continue_to_breakpoint "main-breakpoint-1" + + with_test_prefix "before assignments" { + gdb_test "print tls_main_tbss_1" ".* = 0" + gdb_test "print tls_main_tbss_2" ".* = 0" + gdb_test "print tls_main_tdata_1" ".* = 96" + gdb_test "print tls_main_tdata_2" ".* = 97" + + # For these tests, where we're attempting to access TLS vars + # in a dlopen'd library, but before assignment to any of the + # vars, so it could happen that storage hasn't been allocated + # yet. But it might also work. (When testing against MUSL, + # things just work; GLIBC ends to produce the TLS error.) So + # accept either the right answer or a TLS error message. + + set tlserr "The inferior has not yet allocated storage for thread-local variables.*" + foreach n {1 2 3 4} { + gdb_test "print tls_lib${n}_tbss_1" \ + "0|${tlserr}" + gdb_test "print tls_lib${n}_tbss_2" \ + "0|${tlserr}" + gdb_test "print tls_lib${n}_tdata_1" \ + "96|${tlserr}" + gdb_test "print tls_lib${n}_tdata_2" \ + "97|${tlserr}" + } + foreach n {10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = 0" + gdb_test "print tls_lib${n}_tbss_2" ".* = 0" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}96" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}97" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"] + gdb_continue_to_breakpoint "main-breakpoint-2" + + with_test_prefix "at main-breakpoint-2" { + gdb_test "print tls_main_tbss_1" ".* = 11" + gdb_test "print tls_main_tbss_2" ".* = 12" + gdb_test "print tls_main_tdata_1" ".* = 13" + gdb_test "print tls_main_tdata_2" ".* = 14" + + foreach n {1 2 3 4 10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}11" + gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}12" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}13" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}14" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-3"] + gdb_continue_to_breakpoint "main-breakpoint-3" + + # At this point lib2 and lib3 have been unloaded. Also, TLS vars + # in remaining libraries have been changed. + + with_test_prefix "at main-breakpoint-3" { + gdb_test "print tls_main_tbss_1" ".* = 21" + gdb_test "print tls_main_tbss_2" ".* = 22" + gdb_test "print tls_main_tdata_1" ".* = 23" + gdb_test "print tls_main_tdata_2" ".* = 24" + + foreach n {1 4 10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}21" + gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}22" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}23" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}24" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-4"] + gdb_continue_to_breakpoint "main-breakpoint-4" + + # lib3 has been loaded again; lib2 is the only one not loaded. + + with_test_prefix "at main-breakpoint-4" { + gdb_test "print tls_main_tbss_1" ".* = 31" + gdb_test "print tls_main_tbss_2" ".* = 32" + gdb_test "print tls_main_tdata_1" ".* = 33" + gdb_test "print tls_main_tdata_2" ".* = 34" + + set cond { $n == 3 } + foreach n {1 3 4 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}31" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}32" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}33" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}34" $cond + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-5"] + gdb_continue_to_breakpoint "main-breakpoint-5" + + # lib2 and lib3 are loaded; lib1 and lib4 are not. + + with_test_prefix "at main-breakpoint-5" { + gdb_test "print tls_main_tbss_1" ".* = 41" + gdb_test "print tls_main_tbss_2" ".* = 42" + gdb_test "print tls_main_tdata_1" ".* = 43" + gdb_test "print tls_main_tdata_2" ".* = 44" + + set cond { $n == 2 || $n == 3 } + foreach n {2 3 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}41" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}42" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}43" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}44" $cond + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-6"] + gdb_continue_to_breakpoint "main-breakpoint-6" + + # lib1, lib3 and lib4 are loaded; lib2 is not loaded. + + with_test_prefix "at main-breakpoint-6" { + gdb_test "print tls_main_tbss_1" ".* = 51" + gdb_test "print tls_main_tbss_2" ".* = 52" + gdb_test "print tls_main_tdata_1" ".* = 53" + gdb_test "print tls_main_tdata_2" ".* = 54" + + set cond { $n == 1 || $n == 3 || $n == 4} + foreach n {1 3 4 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}51" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}52" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}53" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}54" $cond + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-7"] + gdb_continue_to_breakpoint "main-breakpoint-7" + + # lib2 and lib3 are loaded; lib1 and lib4 are not. + + with_test_prefix "at main-breakpoint-7" { + gdb_test "print tls_main_tbss_1" ".* = 61" + gdb_test "print tls_main_tbss_2" ".* = 62" + gdb_test "print tls_main_tdata_1" ".* = 63" + gdb_test "print tls_main_tdata_2" ".* = 64" + + set cond { $n == 2 || $n == 3 } + foreach n {2 3 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}61" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}62" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}63" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}64" $cond + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-8"] + gdb_continue_to_breakpoint "main-breakpoint-8" + + # lib1, lib2, lib3, and lib4 are all loaded. + + with_test_prefix "at main-breakpoint-8" { + gdb_test "print tls_main_tbss_1" ".* = 71" + gdb_test "print tls_main_tbss_2" ".* = 72" + gdb_test "print tls_main_tdata_1" ".* = 73" + gdb_test "print tls_main_tdata_2" ".* = 74" + + foreach n {1 2 3 4 10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}71" + gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}72" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}73" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}74" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-9"] + gdb_continue_to_breakpoint "main-breakpoint-9" + + # lib2 is loaded; lib1, lib3, and lib4 are not. + + with_test_prefix "at main-breakpoint-9" { + gdb_test "print tls_main_tbss_1" ".* = 81" + gdb_test "print tls_main_tbss_2" ".* = 82" + gdb_test "print tls_main_tdata_1" ".* = 83" + gdb_test "print tls_main_tdata_2" ".* = 84" + + foreach n {2 10 11} { + gdb_test "print tls_lib${n}_tbss_1" ".* = ${n}81" + gdb_test "print tls_lib${n}_tbss_2" ".* = ${n}82" + gdb_test "print tls_lib${n}_tdata_1" ".* = ${n}83" + gdb_test "print tls_lib${n}_tdata_2" ".* = ${n}84" + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-10"] + gdb_continue_to_breakpoint "main-breakpoint-10" + + # lib3 and lib4 are loaded; lib1 and lib2 are not. + + with_test_prefix "at main-breakpoint-10" { + gdb_test "print tls_main_tbss_1" ".* = 91" + gdb_test "print tls_main_tbss_2" ".* = 92" + gdb_test "print tls_main_tdata_1" ".* = 93" + gdb_test "print tls_main_tdata_2" ".* = 94" + + set cond { $n == 3 || $n == 4 } + foreach n {3 4 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}91" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}92" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}93" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}94" $cond + } + } + + # gdb_interact + + set corefile ${::binfile}.core + set core_supported 0 + if { ![is_remote host] } { + set core_supported [gdb_gcore_cmd $corefile "save corefile"] + } + + # Finish test early if no core file was made. + if !$core_supported { + return + } + + clean_restart $::binfile + + set core_loaded [gdb_core_cmd $corefile "load corefile"] + if { $core_loaded == -1 } { + return + } + + with_test_prefix "core file" { + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + gdb_test "print tls_main_tbss_1" ".* = 91" + gdb_test "print tls_main_tbss_2" ".* = 92" + gdb_test "print tls_main_tdata_1" ".* = 93" + gdb_test "print tls_main_tdata_2" ".* = 94" + + set cond { $n == 3 || $n == 4 } + foreach n {3 4 10 11} { + gdb_test_with_xfail "print tls_lib${n}_tbss_1" ".* = ${n}91" $cond + gdb_test_with_xfail "print tls_lib${n}_tbss_2" ".* = ${n}92" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_1" ".* = ${n}93" $cond + gdb_test_with_xfail "print tls_lib${n}_tdata_2" ".* = ${n}94" $cond + } + } +} + +# Build shared objects for dlopen: +if { [gdb_compile_shlib $libsrc $lib1obj [list debug additional_flags=-DN=1]] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $libsrc $lib2obj [list debug additional_flags=-DN=2]] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $libsrc $lib3obj [list debug additional_flags=-DN=3]] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $libsrc $lib4obj [list debug additional_flags=-DN=4]] != "" } { + untested "failed to compile shared object" + return -1 +} + +# Build shared objects to link against main program: +if { [gdb_compile_shlib $libsrc $lib10obj [list debug additional_flags=-DN=10]] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $libsrc $lib11obj [list debug additional_flags=-DN=11]] != "" } { + untested "failed to compile shared object" + return -1 +} + +# Use gdb_compile_pthreads to build and link the main program for +# testing. It's also possible to run the tests using plain old +# gdb_compile, but this adds complexity with setting up additional +# KFAILs. (When run using GLIBC versions earlier than 2.34, a program +# that's not dynamically linked against libpthread will lack a working +# libthread_db, and, therefore, won't be able to access thread local +# storage without GDB-internal TLS support. Additional complications +# arise from when testing on x86_64 with -m32, which tends to work +# okay on GLIBC 2.34 and newer, but not older versions. It gets messy +# to properly sort out all of these cases.) +# +# This test was originally written to do it both ways, i.e. with both +# both gdb_compile and gdb_compile_pthreads, but the point of this +# test is to check that the link map address to TLS module id mapping +# code works correctly in programs which use lots of dlopen and +# dlclose calls in various orders - and that can be done using just +# gdb_compile_pthreads. + +if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + [list debug shlib_load \ + shlib=${lib10obj} \ + shlib=${lib11obj} \ + additional_flags=-DOBJ1=\"${lib1obj}\" \ + additional_flags=-DOBJ2=\"${lib2obj}\" \ + additional_flags=-DOBJ3=\"${lib3obj}\" \ + additional_flags=-DOBJ4=\"${lib4obj}\" \ + ]] != "" } { + untested "failed to compile" +} else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls + } +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj.c b/gdb/testsuite/gdb.base/tls-multiobj.c new file mode 100644 index 0000000..10e67da --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj.c @@ -0,0 +1,89 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +__thread int tls_main_tbss_1; +__thread int tls_main_tbss_2; +__thread int tls_main_tdata_1 = 96; +__thread int tls_main_tdata_2 = 97; + +extern __thread int tls_lib1_tbss_1; +extern __thread int tls_lib1_tbss_2; +extern __thread int tls_lib1_tdata_1; +extern __thread int tls_lib1_tdata_2; + +extern __thread int tls_lib2_tbss_1; +extern __thread int tls_lib2_tbss_2; +extern __thread int tls_lib2_tdata_1; +extern __thread int tls_lib2_tdata_2; + +extern __thread int tls_lib3_tbss_1; +extern __thread int tls_lib3_tbss_2; +extern __thread int tls_lib3_tdata_1; +extern __thread int tls_lib3_tdata_2; + +extern void lib1_func (); +extern void lib2_func (); +extern void lib3_func (); + +volatile int data; + +void +use_it (int a) +{ + data = a; +} + +int +main (int argc, char **argv) +{ + use_it (-1); + + tls_main_tbss_1 = 51; /* main-breakpoint-1 */ + tls_main_tbss_2 = 52; + tls_main_tdata_1 = 53; + tls_main_tdata_2 = 54; + + tls_lib1_tbss_1 = 151; + tls_lib1_tbss_2 = 152; + tls_lib1_tdata_1 = 153; + tls_lib1_tdata_2 = 154; + + tls_lib2_tbss_1 = 251; + tls_lib2_tbss_2 = 252; + tls_lib2_tdata_1 = 253; + tls_lib2_tdata_2 = 254; + + tls_lib3_tbss_1 = 351; + tls_lib3_tbss_2 = 352; + tls_lib3_tdata_1 = 353; + tls_lib3_tdata_2 = 354; + + lib1_func (); + lib2_func (); + lib3_func (); + + /* Attempt to keep variables in the main program from being optimized + away. */ + use_it (tls_main_tbss_1); + use_it (tls_main_tbss_2); + use_it (tls_main_tdata_1); + use_it (tls_main_tdata_2); + + use_it (100); /* main-breakpoint-2 */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj.exp b/gdb/testsuite/gdb.base/tls-multiobj.exp new file mode 100644 index 0000000..97acb33 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj.exp @@ -0,0 +1,230 @@ +# Copyright 2024 Free Software Foundation, Inc. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# Using different compilation/linking scenarios, attempt to access +# thread-local variables in a non-threaded program using multiple +# shared objects. + +source $srcdir/$subdir/tls-common.exp.tcl + +standard_testfile + +set lib1src "${srcdir}/${subdir}/${testfile}1.c" +set lib2src "${srcdir}/${subdir}/${testfile}2.c" +set lib3src "${srcdir}/${subdir}/${testfile}3.c" + +set lib1obj [standard_output_file "${testfile}1-lib.so"] +set lib2obj [standard_output_file "${testfile}2-lib.so"] +set lib3obj [standard_output_file "${testfile}3-lib.so"] + +proc do_tests {force_internal_tls {do_kfail_tls_access 0}} { + clean_restart $::binfile + if ![runto_main] { + return + } + + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + if { $do_kfail_tls_access && [istarget "*-*-linux*"] } { + # Turn off do_kfail_tls_access when libthread_db is loaded. + # This can happen for the default case when testing x86_64 + # w/ -m32 using glibc versions 2.34 or newer. + gdb_test_multiple "maint check libthread-db" "Check for loaded libthread_db" { + -re -wrap "libthread_db integrity checks passed." { + set do_kfail_tls_access 0 + pass $gdb_test_name + } + -re -wrap "No libthread_db loaded" { + pass $gdb_test_name + } + } + # Also turn off do_kfail_tls_access when connected to a + # gdbserver and we observe that accessing a TLS variable + # works. + if [target_is_gdbserver] { + gdb_test_multiple "print tls_main_tbss_1" \ + "Check TLS accessibility when connected to a gdbserver" { + -re -wrap "= 0" { + set do_kfail_tls_access 0 + pass $gdb_test_name + } + -re -wrap "Remote target failed to process qGetTLSAddr request" { + pass $gdb_test_name + } + } + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"] + gdb_continue_to_breakpoint "main-breakpoint-1" + + set t $do_kfail_tls_access + set m "tls not available" + with_test_prefix "before assignments" { + gdb_test_with_kfail "print tls_main_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_main_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_main_tdata_1" ".* = 96" $t $m + gdb_test_with_kfail "print tls_main_tdata_2" ".* = 97" $t $m + + gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 196" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 197" $t $m + + gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 296" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 297" $t $m + + gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 396" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 397" $t $m + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"] + gdb_continue_to_breakpoint "main-breakpoint-2" + + with_test_prefix "after assignments" { + gdb_test_with_kfail "print tls_main_tbss_1" ".* = 51" $t $m + gdb_test_with_kfail "print tls_main_tbss_2" ".* = 52" $t $m + gdb_test_with_kfail "print tls_main_tdata_1" ".* = 53" $t $m + gdb_test_with_kfail "print tls_main_tdata_2" ".* = 54" $t $m + + gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 151" $t $m + gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 152" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 153" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 154" $t $m + + gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 251" $t $m + gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 252" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 253" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 254" $t $m + + gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 351" $t $m + gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 352" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 353" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 354" $t $m + } + + set corefile ${::binfile}.core + set core_supported 0 + if { ![is_remote host] } { + set core_supported [gdb_gcore_cmd $corefile "save corefile"] + } + + # Finish test early if no core file was made. + if !$core_supported { + return + } + + clean_restart $::binfile + + set core_loaded [gdb_core_cmd $corefile "load corefile"] + if { $core_loaded == -1 } { + return + } + + with_test_prefix "core file" { + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + gdb_test_with_kfail "print tls_main_tbss_1" ".* = 51" $t $m + gdb_test_with_kfail "print tls_main_tbss_2" ".* = 52" $t $m + gdb_test_with_kfail "print tls_main_tdata_1" ".* = 53" $t $m + gdb_test_with_kfail "print tls_main_tdata_2" ".* = 54" $t $m + + gdb_test_with_kfail "print tls_lib1_tbss_1" ".* = 151" $t $m + gdb_test_with_kfail "print tls_lib1_tbss_2" ".* = 152" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_1" ".* = 153" $t $m + gdb_test_with_kfail "print tls_lib1_tdata_2" ".* = 154" $t $m + + gdb_test_with_kfail "print tls_lib2_tbss_1" ".* = 251" $t $m + gdb_test_with_kfail "print tls_lib2_tbss_2" ".* = 252" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_1" ".* = 253" $t $m + gdb_test_with_kfail "print tls_lib2_tdata_2" ".* = 254" $t $m + + gdb_test_with_kfail "print tls_lib3_tbss_1" ".* = 351" $t $m + gdb_test_with_kfail "print tls_lib3_tbss_2" ".* = 352" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_1" ".* = 353" $t $m + gdb_test_with_kfail "print tls_lib3_tdata_2" ".* = 354" $t $m + } +} + +if { [gdb_compile_shlib $lib1src $lib1obj {debug}] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $lib2src $lib2obj {debug}] != "" } { + untested "failed to compile shared object" + return -1 +} +if { [gdb_compile_shlib $lib3src $lib3obj {debug}] != "" } { + untested "failed to compile shared object" + return -1 +} + +# Certain linux target architectures implement support for internal +# TLS lookup which is used when thread stratum support (via +# libthread_db) is missing or when the linux-only GDB maintenance +# setting 'force-internal-tls-address-lookup' is 'on'. Thus for some +# of the testing scenarios, such as statically linked executables, +# this internal support will be used. Set 'do_kfail_tls_access' to 1 +# for those architectures which don't implement internal tls support. +if {[istarget *-*-linux*] + && ![is_any_target {*}$internal_tls_linux_targets]} { + set do_kfail_tls_access 1 +} elseif {[istarget *-*-linux*] && [is_x86_like_target]} { + # This covers the case of x86_64 with -m32: + set do_kfail_tls_access 1 +} else { + set do_kfail_tls_access 0 +} + +set binprefix $binfile + +with_test_prefix "default" { + set binfile $binprefix-default + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + [list debug shlib=${lib1obj} \ + shlib=${lib2obj} \ + shlib=${lib3obj}]] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + # Depending on glibc version, it might not be appropriate + # for do_kfail_tls_access to be set here. That will be + # handled in 'do_tests', disabling it if necessary. + # + # Specifically, glibc versions 2.34 and later have the + # thread library (and libthread_db availability) in + # programs not linked against libpthread.so + do_tests $force_internal_tls $do_kfail_tls_access + } + } +} + +with_test_prefix "pthreads" { + set binfile $binprefix-pthreads + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + [list debug shlib=${lib1obj} \ + shlib=${lib2obj} \ + shlib=${lib3obj}]] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls + } + } +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj1.c b/gdb/testsuite/gdb.base/tls-multiobj1.c new file mode 100644 index 0000000..86e7222 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj1.c @@ -0,0 +1,26 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +__thread int tls_lib1_tbss_1; +__thread int tls_lib1_tbss_2; +__thread int tls_lib1_tdata_1 = 196; +__thread int tls_lib1_tdata_2 = 197; + +void +lib1_func () +{ +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj2.c b/gdb/testsuite/gdb.base/tls-multiobj2.c new file mode 100644 index 0000000..cea0709 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj2.c @@ -0,0 +1,26 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +__thread int tls_lib2_tbss_1; +__thread int tls_lib2_tbss_2; +__thread int tls_lib2_tdata_1 = 296; +__thread int tls_lib2_tdata_2 = 297; + +void +lib2_func () +{ +} diff --git a/gdb/testsuite/gdb.base/tls-multiobj3.c b/gdb/testsuite/gdb.base/tls-multiobj3.c new file mode 100644 index 0000000..bb0f239 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-multiobj3.c @@ -0,0 +1,26 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +__thread int tls_lib3_tbss_1; +__thread int tls_lib3_tbss_2; +__thread int tls_lib3_tdata_1 = 396; +__thread int tls_lib3_tdata_2 = 397; + +void +lib3_func () +{ +} diff --git a/gdb/testsuite/gdb.base/tls-nothreads.c b/gdb/testsuite/gdb.base/tls-nothreads.c new file mode 100644 index 0000000..b3aaa33 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-nothreads.c @@ -0,0 +1,57 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +__thread int tls_tbss_1; +__thread int tls_tbss_2; +__thread int tls_tbss_3; + +__thread int tls_tdata_1 = 21; +__thread int tls_tdata_2 = 22; +__thread int tls_tdata_3 = 23; + +volatile int data; + +void +use_it (int a) +{ + data = a; +} + +int +main (int argc, char **argv) +{ + use_it (-1); + + tls_tbss_1 = 24; /* main-breakpoint-1 */ + tls_tbss_2 = 25; + tls_tbss_3 = 26; + + tls_tdata_1 = 42; + tls_tdata_2 = 43; + tls_tdata_3 = 44; + + use_it (tls_tbss_1); + use_it (tls_tbss_2); + use_it (tls_tbss_3); + use_it (tls_tdata_1); + use_it (tls_tdata_2); + use_it (tls_tdata_3); + + use_it (100); /* main-breakpoint-2 */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/tls-nothreads.exp b/gdb/testsuite/gdb.base/tls-nothreads.exp new file mode 100644 index 0000000..92a5cd9 --- /dev/null +++ b/gdb/testsuite/gdb.base/tls-nothreads.exp @@ -0,0 +1,248 @@ +# Copyright 2024 Free Software Foundation, Inc. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# Using different compilation/linking scenarios, attempt to access +# thread-local variables in a non-threaded program. Also test that +# GDB internal TLS lookup works correctly. + +source $srcdir/$subdir/tls-common.exp.tcl + +standard_testfile + +proc do_tests {force_internal_tls {do_kfail_tls_access 0}} { + clean_restart $::binfile + if ![runto_main] { + return + } + + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + if { $do_kfail_tls_access && [istarget "*-*-linux*"] } { + # Turn off do_kfail_tls_access when libthread_db is loaded. + # This can happen for the default case when testing x86_64 + # w/ -m32 using glibc versions 2.34 or newer. + gdb_test_multiple "maint check libthread-db" "Check for loaded libthread_db" { + -re -wrap "libthread_db integrity checks passed." { + set do_kfail_tls_access 0 + pass $gdb_test_name + } + -re -wrap "No libthread_db loaded" { + pass $gdb_test_name + } + } + # Also turn off do_kfail_tls_access when connected to a + # gdbserver and we observe that accessing a TLS variable + # works. + if [target_is_gdbserver] { + gdb_test_multiple "print tls_tbss_1" "Check TLS accessibility when connected to a gdbserver" { + -re -wrap "= 0" { + set do_kfail_tls_access 0 + pass $gdb_test_name + } + -re -wrap "Remote target failed to process qGetTLSAddr request" { + pass $gdb_test_name + } + } + } + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-1"] + gdb_continue_to_breakpoint "main-breakpoint-1" + + set t $do_kfail_tls_access + set m "tls not available" + with_test_prefix "before assignments" { + gdb_test_with_kfail "print tls_tbss_1" ".* = 0" $t $m + gdb_test_with_kfail "print tls_tbss_2" ".* = 0" $t $m + gdb_test_with_kfail "print tls_tbss_3" ".* = 0" $t $m + + gdb_test_with_kfail "print tls_tdata_1" ".* = 21" $t $m + gdb_test_with_kfail "print tls_tdata_2" ".* = 22" $t $m + gdb_test_with_kfail "print tls_tdata_3" ".* = 23" $t $m + } + + gdb_breakpoint [gdb_get_line_number "main-breakpoint-2"] + gdb_continue_to_breakpoint "main-breakpoint-2" + + with_test_prefix "after assignments" { + gdb_test_with_kfail "print tls_tbss_1" ".* = 24" $t $m + gdb_test_with_kfail "print tls_tbss_2" ".* = 25" $t $m + gdb_test_with_kfail "print tls_tbss_3" ".* = 26" $t $m + + gdb_test_with_kfail "print tls_tdata_1" ".* = 42" $t $m + gdb_test_with_kfail "print tls_tdata_2" ".* = 43" $t $m + gdb_test_with_kfail "print tls_tdata_3" ".* = 44" $t $m + } + + # Make a core file now, but save testing using it until the end + # in case core files are not supported. + set corefile ${::binfile}.core + set core_supported 0 + if { ![is_remote host] } { + set core_supported [gdb_gcore_cmd $corefile "save corefile"] + } + + # Now continue to end and see what happens when attempting to + # access a TLS variable when the program is no longer running. + gdb_continue_to_end + with_test_prefix "after exit" { + gdb_test "print tls_tbss_1" \ + "Cannot (?:read|find address of TLS symbol) `tls_tbss_1' without registers" + } + + with_test_prefix "stripped" { + set binfile_stripped "${::binfile}.stripped" + set objcopy [gdb_find_objcopy] + set cmd "$objcopy --strip-debug ${::binfile} $binfile_stripped" + if ![catch "exec $cmd" cmd_output] { + clean_restart $binfile_stripped + if ![runto_main] { + return + } + + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + # While there are no debug (e.g. DWARF) symbols, there + # are minimal symbols, so we should be able to place a + # breakpoint in use_it and continue to it. Continuing + # twice should put us past the assignments, at which point + # we can see if the TLS variables are still accessible. + gdb_test "break use_it" "Breakpoint 2 at $::hex" + gdb_test "continue" "Breakpoint 2, $::hex in use_it.*" + gdb_test "continue" "Breakpoint 2, $::hex in use_it.*" "continue 2" + + # Note that a cast has been added in order to avoid the + # "...has unknown type; cast it to its declared type" + # problem. + gdb_test_with_kfail "print (int) tls_tbss_1" ".* = 24" $t $m + gdb_test_with_kfail "print (int) tls_tbss_2" ".* = 25" $t $m + gdb_test_with_kfail "print (int) tls_tbss_3" ".* = 26" $t $m + + gdb_test_with_kfail "print (int) tls_tdata_1" ".* = 42" $t $m + gdb_test_with_kfail "print (int) tls_tdata_2" ".* = 43" $t $m + gdb_test_with_kfail "print (int) tls_tdata_3" ".* = 44" $t $m + + # Get rid of the "use_it" breakpoint + gdb_test_no_output "del 2" + + # Continue to program exit + gdb_continue_to_end + + # TLS variables should not be accessible after program exit + # (This case initially caused GDB to crash during development + # of GDB-internal TLS lookup support.) + with_test_prefix "after exit" { + gdb_test "print (int) tls_tbss_1" \ + "Cannot find address of TLS symbol `tls_tbss_1' without registers" + } + } + } + + # Finish test early if no core file was made. + if !$core_supported { + return + } + + clean_restart $::binfile + + set core_loaded [gdb_core_cmd $corefile "load corefile"] + if { $core_loaded == -1 } { + return + } + + with_test_prefix "core file" { + if $force_internal_tls { + gdb_test_no_output "maint set force-internal-tls-address-lookup on" + } + + gdb_test_with_kfail "print tls_tbss_1" ".* = 24" $t $m + gdb_test_with_kfail "print tls_tbss_2" ".* = 25" $t $m + gdb_test_with_kfail "print tls_tbss_3" ".* = 26" $t $m + + gdb_test_with_kfail "print tls_tdata_1" ".* = 42" $t $m + gdb_test_with_kfail "print tls_tdata_2" ".* = 43" $t $m + gdb_test_with_kfail "print tls_tdata_3" ".* = 44" $t $m + } +} + +# Certain linux target architectures implement support for internal +# TLS lookup which is used when thread stratum support (via +# libthread_db) is missing or when the linux-only GDB maintenance +# setting 'force-internal-tls-address-lookup' is 'on'. Thus for some +# of the testing scenarios, such as statically linked executables, +# this internal support will be used. Set 'do_kfail_tls_access' to 1 +# for those architectures which don't implement internal TLS support. +if {[istarget *-*-linux*] + && ![is_any_target {*}$internal_tls_linux_targets]} { + set do_kfail_tls_access 1 +} elseif {[istarget *-*-linux*] && [is_x86_like_target]} { + # This covers the case of x86_64 with -m32: + set do_kfail_tls_access 1 +} else { + set do_kfail_tls_access 0 +} + +set binprefix $binfile + +with_test_prefix "default" { + set binfile $binprefix-default + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + # Depending on glibc version, it might not be appropriate + # for do_kfail_tls_access to be set here. That will be + # handled in 'do_tests', disabling it if necessary. + # + # Specifically, glibc versions 2.34 and later have the + # thread library (and libthread_db availability) in + # programs not linked against libpthread.so + do_tests $force_internal_tls $do_kfail_tls_access + } + } +} + +with_test_prefix "static" { + set binfile $binprefix-static + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls $do_kfail_tls_access + } + } +} + +with_test_prefix "pthreads" { + set binfile $binprefix-pthreads + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls + } + } +} + +with_test_prefix "pthreads-static" { + set binfile $binprefix-pthreads-static + if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug "additional_flags=-static"}] != "" } { + untested "failed to compile" + } else { + foreach_with_prefix force_internal_tls $internal_tls_iters { + do_tests $force_internal_tls $do_kfail_tls_access + } + } +} diff --git a/gdb/testsuite/gdb.base/user-namespace-attach.c b/gdb/testsuite/gdb.base/user-namespace-attach.c new file mode 100644 index 0000000..684ce1c --- /dev/null +++ b/gdb/testsuite/gdb.base/user-namespace-attach.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 <sys/types.h> +#include <unistd.h> +#include <stdio.h> + +volatile int spin_p = 1; + +int +main () +{ + alarm (60); + + printf ("pid = %lld\n", ((long long) getpid ())); + + while (spin_p) + sleep (1); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/user-namespace-attach.exp b/gdb/testsuite/gdb.base/user-namespace-attach.exp new file mode 100644 index 0000000..741093c --- /dev/null +++ b/gdb/testsuite/gdb.base/user-namespace-attach.exp @@ -0,0 +1,148 @@ +# 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 GDB can attach to a process started using 'unshare'. The +# inferior is started in a separate mnt namespace. + +require can_spawn_for_attach + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile] == -1} { + return +} + +# This test relies (at least in some parts) on the sysroot being +# 'target:'. Grab the current sysroot now so we can skip those tests +# if the board file has changed the sysroot. +set sysroot "" +set test "show sysroot" +gdb_test_multiple $test $test { + -re -wrap "The current system root is \"(.*)\"\\." { + set sysroot $expect_out(1,string) + } +} + +# Start a process using 'unshare FLAGS', then attach to the process +# from GDB. Check that the attach worked as expected. +proc run_test { flags } { + + # If FLAGS contains '--mount' then a separate mnt namespace will + # be created, in which case the executable will have been read + # from the 'target:'. Otherwise, the executable will have been + # read from the local filesystem, and there will be no prefix. + # + # Of course, this only applies if the sysroot is 'target:', some + # boards change this, so skip these tests on those boards. + if { [lsearch -exact [split $flags " "] "--mount"] != -1 } { + if { $::sysroot ne "target:" } { + return + } + + set prefix "target:" + } else { + set prefix "" + } + + set unshare_cmd "unshare $flags" + + # Run '/bin/true' using UNSHARE_CMD. If the flags in UNSHARE_CMD + # aren't supported then this will fail, this means we shouldn't + # spawn the command with our test executable and try attaching. + # + # This will also fail if /bin/true isn't present, or doesn't work + # as we expect. But this should be fine for many targets. + set res [remote_exec target "$unshare_cmd /bin/true"] + if { [lindex $res 0] != 0 } { + unsupported "unshare flags not supported" + return + } + + set inferior_spawn_id \ + [spawn_wait_for_attach [list "$unshare_cmd $::binfile"]] + if { $inferior_spawn_id == -1 } { + unsupported "failed to spawn for attach" + return + } + + set inferior_pid [spawn_id_get_pid $inferior_spawn_id] + + clean_restart + + set saw_bad_warning false + gdb_test_multiple "attach $inferior_pid" "attach to inferior" { + -re "^attach $::decimal\r\n" { + exp_continue + } + + -re "^warning: \[^\r\n\]+: could not open as an executable file: \[^\r\n\]+\r\n" { + set saw_bad_warning true + exp_continue + } + + -re "^warning: \[^\r\n\]+: can't open to read symbols: \[^\r\n\]+\r\n" { + set saw_bad_warning true + exp_continue + } + + -re "^warning: Could not load vsyscall page because no executable was specified\r\n" { + # This warning is a secondary consequence of the above bad + # warnings, so don't count this as a bad warnings, ignore + # it instead. + exp_continue + } + + -re "^warning:\\s+$::decimal\\s*\[^\r\n\]+: No such file or directory\r\n" { + # This unrelated warning is seen when GDB stops in libc, + # and the source code for libc is not available. + exp_continue + } + + -re "^warning: \[^\r\n\]+\r\n" { + # If we ignore "other" warnings then, should the above + # warnings strings change we'll start ignoring the bad + # warnings, and the test will appear to pass. + # + # If you are seeing a warning here that really has nothing + # to do with the test failing, then the correct solution + # is to add a new regexp to specifically match _that_ + # warning, and ignore it. + set saw_bad_warning true + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_warning } $gdb_test_name + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Ensure GDB could access the executable. + set binfile_re [string_to_regexp $::binfile] + gdb_test "info inferiors" \ + "\r\n\\*\\s+$::decimal\\s+\[^\r\n\]+\\s+${prefix}${binfile_re}\\s*" +} + +set test_flags [list \ + "--mount --map-root-user" \ + "--user" \ + "--user --map-root-user"] + +foreach_with_prefix flags $test_flags { + run_test $flags +} diff --git a/gdb/testsuite/gdb.base/vfork-follow-parent.exp b/gdb/testsuite/gdb.base/vfork-follow-parent.exp index fca2993..8cb785d 100644 --- a/gdb/testsuite/gdb.base/vfork-follow-parent.exp +++ b/gdb/testsuite/gdb.base/vfork-follow-parent.exp @@ -19,6 +19,8 @@ # schedule-multiple on" or "set detach-on-fork on". Test these two resolution # methods. +require allow_fork_tests + standard_testfile .c vforked-prog.c set binfile ${testfile}-exit diff --git a/gdb/testsuite/gdb.base/watch-before-fork.exp b/gdb/testsuite/gdb.base/watch-before-fork.exp index 074cfbd..509561e 100644 --- a/gdb/testsuite/gdb.base/watch-before-fork.exp +++ b/gdb/testsuite/gdb.base/watch-before-fork.exp @@ -20,6 +20,8 @@ # This test uses "awatch". require allow_hw_watchpoint_access_tests +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile debug]} { diff --git a/gdb/testsuite/gdb.base/watch-vfork.exp b/gdb/testsuite/gdb.base/watch-vfork.exp index 1bc61bc..503727d 100644 --- a/gdb/testsuite/gdb.base/watch-vfork.exp +++ b/gdb/testsuite/gdb.base/watch-vfork.exp @@ -17,6 +17,8 @@ standard_testfile .c +require allow_fork_tests + if { [build_executable ${testfile}.exp ${testfile} $srcfile {debug}] } { untested "failed to compile" return -1 diff --git a/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp b/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp index b3892f3..fa63edb 100644 --- a/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp +++ b/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp @@ -26,29 +26,18 @@ if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} { return -1 } -if ![runto_main] { - return -1 -} +set test_spawn_id [spawn_wait_for_attach $binfile] +set testpid [spawn_id_get_pid $test_spawn_id] -# Run to the point where mypid in the test program has been -# populated. -gdb_breakpoint [gdb_get_line_number "pidacquired"] -gdb_continue_to_breakpoint "pidacquired" - -# Get the PID of the test process. -set testpid [get_integer_valueof "mypid" 0] +gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach once" gdb_test "detach" "Detaching from program: .*, process $testpid\r\n\\\[Inferior $decimal \\(process $testpid\\) detached\\\]" -if {$testpid == ""} { - return -1 -} - # A clean restart is needed to force the hardware watchpoint setup # logic to run post attach rather than post inferior launch. clean_restart $binfile -gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach" +gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach twice" # Ensure the test program is in the top frame so the required # variables are in scope. @@ -62,3 +51,5 @@ gdb_test "watch watched_variable" \ gdb_test "continue" \ "continue.*Continuing.*\.Hardware watchpoint $decimal: watched_variable.*Old value = 0.*New value = 4.*watched_variable\\);" + +kill_wait_spawned_process $test_spawn_id diff --git a/gdb/testsuite/gdb.btrace/multi-inferior.exp b/gdb/testsuite/gdb.btrace/multi-inferior.exp index ed2acb2..d87a941 100644 --- a/gdb/testsuite/gdb.btrace/multi-inferior.exp +++ b/gdb/testsuite/gdb.btrace/multi-inferior.exp @@ -24,6 +24,8 @@ require allow_btrace_tests +require allow_multi_inferior_tests + require !use_gdb_stub standard_testfile diff --git a/gdb/testsuite/gdb.cp/chained-calls.cc b/gdb/testsuite/gdb.cp/chained-calls.cc index 9d12c98..9358c71 100644 --- a/gdb/testsuite/gdb.cp/chained-calls.cc +++ b/gdb/testsuite/gdb.cp/chained-calls.cc @@ -23,6 +23,8 @@ public: S operator+ (const S &s); + int get (); + int a; }; @@ -41,6 +43,12 @@ S::operator+ (const S &s) return res; } +int +S::get () +{ + return a; +} + S f (int i) { @@ -162,6 +170,8 @@ public: U (type t); type get_type (); + int get (); + int a; char c; type tp[2]; @@ -191,6 +201,12 @@ U::get_type () } int +U::get () +{ + return a; +} + +int main () { int i = g(f(0)); @@ -198,6 +214,7 @@ main () B b = makeb (); C c; + int z = f (42).get (); return i + getb(b, 0); /* Break here */ } diff --git a/gdb/testsuite/gdb.cp/chained-calls.exp b/gdb/testsuite/gdb.cp/chained-calls.exp index 4f0597a..d34162c 100644 --- a/gdb/testsuite/gdb.cp/chained-calls.exp +++ b/gdb/testsuite/gdb.cp/chained-calls.exp @@ -42,3 +42,6 @@ gdb_test "p *c" ".* = {a = 5678}" "*c" gdb_test "p *c + *c" ".* = {a = 11356}" "*c + *c" gdb_test "p q(*c + *c)" ".* = {a = 11356}" "q(*c + *c)" gdb_test "p make_int().get_type ()" ".* = INT" "make_int().get_type ()" +gdb_test "p f(42).get()" " = 42" "f().get()" +gdb_test "ptype f(42).get()" "type = int" "ptype f().get()" +gdb_test "ptype make_int().get()" "type = int" "make_int().get()" diff --git a/gdb/testsuite/gdb.cp/cpexprs.exp.tcl b/gdb/testsuite/gdb.cp/cpexprs.exp.tcl index 1ac35af..5c3dfd6 100644 --- a/gdb/testsuite/gdb.cp/cpexprs.exp.tcl +++ b/gdb/testsuite/gdb.cp/cpexprs.exp.tcl @@ -28,17 +28,33 @@ proc test_breakpoint {func} { delete_breakpoints if { ! [gdb_breakpoint test_function] } { fail "set test_function breakpoint for $func" - } elseif { [gdb_test "continue" \ - "Continuing.\r\n\r\nBreakpoint $DEC+,.*test_function.*" \ - "continue to test_function for $func"] != 0 } { - } else { - gdb_breakpoint "$func" - set i [expr {[string last : $func] + 1}] - set efunc [string_to_regexp [string range $func $i end]] - gdb_test "continue" \ - "Continuing.\r\n\r\nBreakpoint $DEC+,.*$efunc.*" \ - "continue to $func" + return + } + + # Accept any input between "Continuing" and the breakpoint hit, as + # on Cygwin, we may see a "New Thread" notification. This is the + # Cygwin runtime spawning its own internal threads. + if { [gdb_test "continue" \ + "Continuing.\r\n.*Breakpoint $DEC+,.*test_function.*" \ + "continue to test_function for $func"] != 0 } { + return } + + # On some systems, the in-charge and not-in-charge dtors of a + # class may end up with the same address, so setting a breakpoint + # at a dtor like base::~base only finds one location. On other + # systems (e.g. Cygwin), the two dtors for the same class may have + # different addresses, so we find two locations for the + # breakpoint. Thus, expect that the breakpoint hit may or may not + # report a location number. + set bp_re "$DEC+(\.$DEC+)?" + + gdb_breakpoint "$func" + set i [expr {[string last : $func] + 1}] + set efunc [string_to_regexp [string range $func $i end]] + gdb_test "continue" \ + "Continuing.\r\n.*Breakpoint $bp_re,.*$efunc.*" \ + "continue to $func" } # Add a function to the list of tested functions 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.dap/attach.exp b/gdb/testsuite/gdb.dap/attach.exp index 37e867c..5e1f634 100644 --- a/gdb/testsuite/gdb.dap/attach.exp +++ b/gdb/testsuite/gdb.dap/attach.exp @@ -33,11 +33,11 @@ set attach_id [dap_attach $testpid $binfile] dap_check_request_and_response "configurationDone" configurationDone +dap_check_response "attach response" attach $attach_id + dap_wait_for_event_and_check "stopped" stopped \ "body reason" attach -dap_check_response "attach response" attach $attach_id - dap_shutdown true kill_wait_spawned_process $test_spawn_id diff --git a/gdb/testsuite/gdb.dap/log-message.exp b/gdb/testsuite/gdb.dap/log-message.exp index 421df14..cce367d 100644 --- a/gdb/testsuite/gdb.dap/log-message.exp +++ b/gdb/testsuite/gdb.dap/log-message.exp @@ -40,6 +40,15 @@ set obj [dap_check_request_and_response "set breakpoint" \ [list s $srcfile] $line]] set fn_bpno [dap_get_breakpoint_number $obj] +set eol {\n} +dap_wait_for_event_and_check "set breakpoint output, part 1" output \ + {body category} stdout \ + {body output} "No source file named log-message.c.$eol" + +dap_wait_for_event_and_check "set breakpoint output, part 2" output \ + {body category} stdout \ + {body output} "Breakpoint 1 (-source log-message.c -line $line) pending.$eol" + dap_check_request_and_response "configurationDone" configurationDone dap_check_response "launch response" launch $launch_id diff --git a/gdb/testsuite/gdb.dap/threads.c b/gdb/testsuite/gdb.dap/threads.c new file mode 100644 index 0000000..168f044 --- /dev/null +++ b/gdb/testsuite/gdb.dap/threads.c @@ -0,0 +1,67 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2019-2025 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (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 2 + +static pthread_barrier_t threads_started_barrier; + +static void * +thread_function (void *arg) +{ + pthread_barrier_wait (&threads_started_barrier); + + while (1) + sleep (1); + + pthread_exit (NULL); +} + +static void +all_started (void) +{ +} + +int +main () +{ + pthread_t threads[NUM]; + long i; + + pthread_barrier_init (&threads_started_barrier, NULL, NUM + 1); + + for (i = 1; i <= NUM; i++) + { + int res; + + res = pthread_create (&threads[i - 1], NULL, thread_function, NULL); + } + + pthread_barrier_wait (&threads_started_barrier); + + all_started (); + + printf ("sleeping\n"); + fflush (stdout); + sleep (180); + + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.dap/threads.exp b/gdb/testsuite/gdb.dap/threads.exp new file mode 100644 index 0000000..c91d107 --- /dev/null +++ b/gdb/testsuite/gdb.dap/threads.exp @@ -0,0 +1,81 @@ +# 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 DAP "threads" request. + +require allow_shlib_tests allow_dap_tests + +load_lib dap-support.exp + +standard_testfile + +set libname $testfile-solib +set srcfile_lib $srcdir/$subdir/$libname.c +set binfile_lib [standard_output_file $libname.so] + +if {[build_executable "failed to prepare" $testfile $srcfile \ + {debug pthreads}] == -1} { + return +} + +if {[dap_initialize] == ""} { + return +} + +set launch_id [dap_launch $testfile] + +set obj [dap_check_request_and_response "set breakpoint on all_started function" \ + setFunctionBreakpoints \ + {o breakpoints [a [o name [s all_started]]]}] +set fn_bpno [dap_get_breakpoint_number $obj] + +dap_check_request_and_response "configurationDone" configurationDone + +dap_check_response "launch response" launch $launch_id + +lassign [dap_wait_for_event_and_check "stopped at function breakpoint" \ + stopped \ + "body reason" breakpoint \ + "body hitBreakpointIds" $fn_bpno] \ + ignore \ + all_events + +# Verify that we saw the correct number of thread events. +set count 0 +foreach event $all_events { + if {[dict get $event type] == "event" + && [dict get $event event] == "thread" + && [dict get $event body reason] == "started"} { + incr count + } +} +gdb_assert {$count == 3} "correct number of thread events" + +dap_check_request_and_response "continue" continue \ + {o threadId [i 1]} + +# Make sure that the inferior has really re-started -- note that there +# is no "continue" event, because the "continue" request suppresses +# those. +dap_wait_for_event_and_check "output from inferior" output \ + {body output} "sleeping\\n" + +lassign [dap_check_request_and_response "threads request" threads] \ + response ignore + +gdb_assert {[llength [dict get $response body threads]] == 3} \ + "correct number of threads" + +dap_shutdown true diff --git a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp index 93f8f92..35bb401 100644 --- a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp +++ b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp @@ -46,7 +46,7 @@ set build_id_debug_file \ [standard_output_file [build_id_debug_filename_get $binfile]] # Get the BINFILE.debug filename. This is the file we should be -# moving to the BUILD_ID_DEBUG_FILE location, but we wont, we're going +# moving to the BUILD_ID_DEBUG_FILE location, but we won't, we're going # to move something else there instead. set debugfile [standard_output_file "${binfile}.debug"] diff --git a/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp index 78fa252..dac4e6c 100644 --- a/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp +++ b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp @@ -49,7 +49,7 @@ # # This obviously needs fixing, but is a separate problem from the one being # tested here, so this test deliberately checks the mapping using a file that -# is mmaped rather than loaded as a shared library, as such the file is in the +# is mmapped rather than loaded as a shared library, as such the file is in the # core-files list of mapped files, but is not in the shared library list. # # Despite this test living in the gdb.debuginfod/ directory, only the last @@ -260,7 +260,7 @@ proc load_core_file { testname { line_re "" } } { # We expect RES to be 2 (TCL_RETURN) or 1 (TCL_ERROR). If we get # here then somehow the 'catch' above finished without hitting # either of those cases, which is .... weird. - perror "unexepcted return value, code = $res, value = $string" + perror "unexpected return value, code = $res, value = $string" return -1 } } diff --git a/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp b/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp index 9f1842c..1008e46 100644 --- a/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp +++ b/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp @@ -89,7 +89,7 @@ if {[lindex $status 0] != 0} { } # Build the executable. This links against libfoo.so, which is -# poining at libfoo_1.so. Just to confuse things even more, this +# pointing at libfoo_1.so. Just to confuse things even more, this # executable uses dlopen to load libfoo_2.so. Weird! if { [build_executable "build executable" ${binfile} ${srcfile2} \ [list debug shlib=${library_filename} shlib_load]] == -1 } { diff --git a/gdb/testsuite/gdb.dwarf2/ada-array-bound.c b/gdb/testsuite/gdb.dwarf2/ada-array-bound.c new file mode 100644 index 0000000..5a7d397 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/ada-array-bound.c @@ -0,0 +1,29 @@ +/* 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/>. */ + +/* The data used for the structure. */ + +unsigned char our_data[] = { 3, 7, 11, 13 }; + +/* Dummy main function. */ + +int +main() +{ + asm ("main_label: .globl main_label"); + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp b/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp new file mode 100644 index 0000000..f48df7b --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp @@ -0,0 +1,89 @@ +# 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 handling of an array type whose bound comes from the field of a +# structure. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile .c -debug.S + +# Set up the DWARF for the test. + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_Ada95} + {DW_AT_name $srcfile} + } { + declare_labels byte array disc struct + + byte: DW_TAG_base_type { + {DW_AT_byte_size 1 DW_FORM_sdata} + {DW_AT_encoding @DW_ATE_unsigned} + {DW_AT_name byte} + } + + array: DW_TAG_array_type { + {DW_AT_name array_type} + {DW_AT_type :$byte} + } { + DW_TAG_subrange_type { + {DW_AT_type :$byte} + {DW_AT_upper_bound :$disc} + } + } + + struct: DW_TAG_structure_type { + {DW_AT_name discriminated} + {DW_AT_byte_size 4 DW_FORM_sdata} + } { + disc: DW_TAG_member { + {DW_AT_name disc} + {DW_AT_type :$byte} + {DW_AT_data_member_location 0 DW_FORM_sdata} + } + DW_TAG_member { + {DW_AT_name nums} + {DW_AT_type :$array} + {DW_AT_data_member_location 1 DW_FORM_sdata} + } + } + + DW_TAG_variable { + {DW_AT_name "value"} + {DW_AT_type :$struct} + {DW_AT_external 1 DW_FORM_flag} + {DW_AT_location {DW_OP_addr [gdb_target_symbol "our_data"]} + SPECIAL_expr} + } + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 +} + +gdb_test_no_output "set language ada" +gdb_test "print value" \ + [string_to_regexp " = (disc => 3, nums => (7, 11, 13))"] 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..cb24b19 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx-out-of-bounds.exp @@ -0,0 +1,41 @@ +# 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 + +# With readnow, the dwarf error is printed during the file command, so skip +# the test. +require !readnow + +set prepare_for_testing_done 0 +source $srcdir/$subdir/dw-form-strx.exp.tcl +require {expr $prepare_for_testing_done == 1} + +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..3f739c4 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp @@ -0,0 +1,25 @@ +# 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 + +set prepare_for_testing_done 0 +source $srcdir/$subdir/dw-form-strx.exp.tcl +require {expr $prepare_for_testing_done == 1} + +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..bc5a654 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp.tcl @@ -0,0 +1,64 @@ +# 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_cu1 + + debug_str_offsets { base_offset base_offset_cu1 } int + + cu { + version 5 + } { + DW_TAG_compile_unit { + {DW_AT_str_offsets_base $base_offset_cu1 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 +} + +# Let includers know prepare_for_testing was done, without having to check +# source return status. +set prepare_for_testing_done 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/dw2-modula2-self-type.S b/gdb/testsuite/gdb.dwarf2/dw2-modula2-self-type.S index c09c6db..06a93ac 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-modula2-self-type.S +++ b/gdb/testsuite/gdb.dwarf2/dw2-modula2-self-type.S @@ -114,7 +114,11 @@ die221: .byte 0x0 - .section .debug_str +#ifdef __arm__ + .section .debug_str,"MS",%progbits,1 +#else + .section .debug_str,"MS",@progbits,1 +#endif .LASF1: .string "2.mod" .LASF0: diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-psym-warning.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-psym-warning.exp index ea0fc03..6120878 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-psym-warning.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-psym-warning.exp @@ -117,7 +117,7 @@ if ![runto_main] { # the hole is there in the symbol table, but not the partial symbol table, # we run into: # (gdb) bt -# warning: (Internal error: pc 0x555555554619 in read in psymtab, \ +# warning: (Internal error: pc 0x555555554619 in read in psymtab, # but not in symtab.) # ... # (gdb) diff --git a/gdb/testsuite/gdb.dwarf2/dw2-simple-locdesc.S b/gdb/testsuite/gdb.dwarf2/dw2-simple-locdesc.S index cd999f4..551dda7 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-simple-locdesc.S +++ b/gdb/testsuite/gdb.dwarf2/dw2-simple-locdesc.S @@ -160,7 +160,11 @@ d: .byte 0 .byte 0 .byte 0 - .section .debug_str +#ifdef __arm__ + .section .debug_str,"MS",%progbits,1 +#else + .section .debug_str,"MS",@progbits,1 +#endif .LASF2: .string "GNU C 4.7.0 20110727 (experimental)" .LASF0: diff --git a/gdb/testsuite/gdb.dwarf2/dw2-strp.S b/gdb/testsuite/gdb.dwarf2/dw2-strp.S index c7ede95..db3e64f 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-strp.S +++ b/gdb/testsuite/gdb.dwarf2/dw2-strp.S @@ -163,7 +163,11 @@ .byte 0x0 /* Terminator */ /* String table */ - .section .debug_str +#ifdef __arm__ + .section .debug_str,"MS",%progbits,1 +#else + .section .debug_str,"MS",@progbits,1 +#endif .Lproducer: .string "GNU C 3.3.3" .Lchar_str: diff --git a/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp b/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp new file mode 100644 index 0000000..f4e02da --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp @@ -0,0 +1,95 @@ +# 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 DW_AT_data_bit_offset with an expression. This is a DWARF +# extension, but expected to be in DWARF 6. See +# https://dwarfstd.org/issues/250501.1.html + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile ada-array-bound.c -debug.S + +# Set up the DWARF for the test. + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_Ada95} + {DW_AT_name $srcfile} + } { + declare_labels byte array struct + + byte: DW_TAG_base_type { + {DW_AT_byte_size 1 DW_FORM_sdata} + {DW_AT_encoding @DW_ATE_unsigned} + {DW_AT_name byte} + } + + array: DW_TAG_array_type { + {DW_AT_name array_type} + {DW_AT_type :$byte} + } { + DW_TAG_subrange_type { + {DW_AT_type :$byte} + {DW_AT_upper_bound 3 DW_FORM_sdata} + } + } + + struct: DW_TAG_structure_type { + {DW_AT_name discriminated} + {DW_AT_byte_size 4 DW_FORM_sdata} + } { + DW_TAG_member { + {DW_AT_name disc} + {DW_AT_type :$byte} + {DW_AT_data_member_location 0 DW_FORM_sdata} + } + + # We know this is always at offset 1 but use an + # expression just to test this code path. This is a + # DWARF extension. See + # https://dwarfstd.org/issues/250501.1.html. + DW_TAG_member { + {DW_AT_name nums} + {DW_AT_type :$array} + {DW_AT_data_bit_offset {DW_OP_lit8} SPECIAL_expr} + } + } + + DW_TAG_variable { + {DW_AT_name "value"} + {DW_AT_type :$struct} + {DW_AT_external 1 DW_FORM_flag} + {DW_AT_location {DW_OP_addr [gdb_target_symbol "our_data"]} + SPECIAL_expr} + } + } + } +} + +if {[prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}]} { + return -1 +} + +gdb_test_no_output "set language ada" +gdb_test "print value" \ + [string_to_regexp " = (disc => 3, nums => (7, 11, 13))"] diff --git a/gdb/testsuite/gdb.dwarf2/fission-dw-form-strx.exp b/gdb/testsuite/gdb.dwarf2/fission-dw-form-strx.exp new file mode 100644 index 0000000..4f5867c --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/fission-dw-form-strx.exp @@ -0,0 +1,88 @@ +# 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 support for a DW_FORM_strx attribute in a dwo file. + +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 -dwo.S + +set main_asm_file [standard_output_file $srcfile2] +set dwo_asm_file [standard_output_file $srcfile3] + +# Debug info in the main file. +Dwarf::assemble $main_asm_file { + cu { + version 5 + dwo_id 0xF00D + } { + compile_unit { + {DW_AT_dwo_name ${::gdb_test_file_name}.dwo DW_FORM_strp} + } {} + } +} + +# Debug info in the DWO file. +Dwarf::assemble $dwo_asm_file { + debug_str_offsets { dwo 1 } int + + cu { + fission 1 + version 5 + dwo_id 0xF00D + } { + compile_unit {} { + 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 0 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} + } + } + } +} + +# Build main file. +if { [build_executable "${testfile}.exp" $binfile \ + [list ${srcfile} ${main_asm_file}] {nodebug}] } { + return +} + +# Build DWO file. +set dwo_file [standard_output_file ${testfile}.dwo] +if { [gdb_compile_shlib $dwo_asm_file $dwo_file nodebug] != "" } { + return +} + +if { [is_remote host] } { + gdb_remote_download host $dwo_file +} + +clean_restart $binfile + +gdb_test "ptype global_var" "type = int" 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.dwarf2/pr11465.S b/gdb/testsuite/gdb.dwarf2/pr11465.S index fed98bc..f3f2c57 100644 --- a/gdb/testsuite/gdb.dwarf2/pr11465.S +++ b/gdb/testsuite/gdb.dwarf2/pr11465.S @@ -344,7 +344,11 @@ die149: .uleb128 0x16 /* DW_TAG_variable */ .byte 0x0 .byte 0x0 .byte 0x0 - .section .debug_str +#ifdef __arm__ + .section .debug_str,"MS",%progbits,1 +#else + .section .debug_str,"MS",@progbits,1 +#endif .LASF0: .string "_ZN1N1fE" .LASF7: diff --git a/gdb/testsuite/gdb.guile/scm-cmd.exp b/gdb/testsuite/gdb.guile/scm-cmd.exp index 9caca24..3709cb1 100644 --- a/gdb/testsuite/gdb.guile/scm-cmd.exp +++ b/gdb/testsuite/gdb.guile/scm-cmd.exp @@ -71,6 +71,65 @@ gdb_test_multiline "input subcommand" \ gdb_test "prefix-cmd subcmd ugh" "subcmd output, arg = ugh" "call subcmd" +# Create a sub-command using a partial, but still unique, prefix. + +gdb_test_multiline "sub-command using partial prefix" \ + "guile" "" \ + "(register-command! (make-command \"prefix subcmd2\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display (format #f \"subcmd2 output, arg = ~a\\n\" arg)))))" "" \ + "end" "" + +gdb_test "prefix-cmd subcmd2 ugh" "subcmd2 output, arg = ugh" "call subcmd2" + +# Now create a second prefix, similar to the first. + +gdb_test_multiline "create prefix-xxx prefix command" \ + "guile" "" \ + "(register-command! (make-command \"prefix-xxx\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:completer-class COMPLETE_NONE" "" \ + " #:prefix? #t))" "" \ + "end" "" + +# Now create a sub-command using an ambiguous prefix. + +gdb_test_multiline "sub-command using ambiguous partial prefix" \ + "guile" "" \ + "(register-command! (make-command \"prefix subcmd3\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display (format #f \"subcmd3 output, arg = ~a\\n\" arg)))))" "" \ + "end" \ + [multi_line \ + "Out of range: could not find command prefix 'prefix' in position 1: \"prefix subcmd3\"" \ + "Error while executing Scheme code\\."] + +# Check for errors when creating a command with an unknown prefix. + +gdb_test_multiline "try to create 'unknown-prefix subcmd'" \ + "guile" "" \ + "(register-command! (make-command \"unknown-prefix subcmd\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display \"called unknown-prefix subcmd\"))))" "" \ + "end" \ + [multi_line \ + "Out of range: could not find command prefix 'unknown-prefix' in position 1: \"unknown-prefix subcmd\"" \ + "Error while executing Scheme code\\."] + +gdb_test_multiline "try to create 'prefix-cmd unknown-prefix subcmd'" \ + "guile" "" \ + "(register-command! (make-command \"prefix-cmd unknown-prefix subcmd\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display \"called prefix-cmd unknown-prefix subcmd\"))))" "" \ + "end" \ + [multi_line \ + "Out of range: could not find command prefix 'prefix-cmd unknown-prefix' in position 1: \"prefix-cmd unknown-prefix subcmd\"" \ + "Error while executing Scheme code\\."] + # Test a subcommand in an existing GDB prefix. gdb_test_multiline "input new subcommand" \ 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.guile/scm-frame.exp b/gdb/testsuite/gdb.guile/scm-frame.exp index b5ec73f..9a27c42 100644 --- a/gdb/testsuite/gdb.guile/scm-frame.exp +++ b/gdb/testsuite/gdb.guile/scm-frame.exp @@ -52,7 +52,7 @@ gdb_test "guile (print (frame-read-var bf1 \"b\"))" \ "\"bar\"" "test b" # Test the read-var function in another block other than the current -# block (in this case, the super block). Test thar read-var is reading +# block (in this case, the super block). Test that read-var is reading # the correct variables of i and f but they are the correct value and type. gdb_scm_test_silent_cmd "guile (define sb (block-superblock (frame-block bf1)))" \ "get superblock" diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp index 8ab5d93..e35428a 100644 --- a/gdb/testsuite/gdb.guile/scm-parameter.exp +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp @@ -67,9 +67,19 @@ with_test_prefix "test-param" { gdb_test_no_output "set print test-param off" gdb_test "show print test-param" "The state of the Test Parameter is off." "show parameter off" gdb_test "guile (print (parameter-value test-param))" "= #f" "parameter value, false" - gdb_test "help show print test-param" "Show the state of the boolean test-param.*" "show help" - gdb_test "help set print test-param" "Set the state of the boolean test-param.*" "set help" - gdb_test "help set print" "set print test-param -- Set the state of the boolean test-param.*" "general help" + gdb_test "help show print test-param" \ + [multi_line \ + "^Show the state of the boolean test-param\\." \ + "When enabled, test param does something useful\\. When disabled, does nothing\\."] \ + "show help" + gdb_test "help set print test-param" \ + [multi_line \ + "^Set the state of the boolean test-param\\." \ + "When enabled, test param does something useful\\. When disabled, does nothing\\."] \ + "set help" + gdb_test "help set print" \ + "set print test-param -- Set the state of the boolean test-param.*" \ + "general help" gdb_test "guile (print (parameter? test-param))" "= #t" gdb_test "guile (print (parameter? 42))" "= #f" @@ -314,9 +324,17 @@ with_test_prefix "test-undocumented-param" { gdb_test "show print test-undoc-param" "The state of the Test Parameter is on." "show parameter on" gdb_test_no_output "set print test-undoc-param off" gdb_test "show print test-undoc-param" "The state of the Test Parameter is off." "show parameter off" - gdb_test "help show print test-undoc-param" "This command is not documented." "show help" - gdb_test "help set print test-undoc-param" "This command is not documented." "set help" - gdb_test "help set print" "set print test-undoc-param -- This command is not documented.*" "general help" + gdb_test "help show print test-undoc-param" \ + [multi_line \ + "^Show the current value of 'print test-undoc-param'\\." \ + "This command is not documented\\."] \ + "show help" + gdb_test "help set print test-undoc-param" \ + [multi_line \ + "Set the current value of 'print test-undoc-param'\\." \ + "This command is not documented\\."] \ + "set help" + gdb_test "help set print" "set print test-undoc-param -- Set the current value of 'print test-undoc-param'\\..*" "general help" } # Test a parameter with a restricted range, where we need to notify the user @@ -379,13 +397,168 @@ gdb_test_no_output "guile (register-parameter! prev-ambig)" with_test_prefix "previously-ambiguous" { gdb_test "guile (print (parameter-value prev-ambig))" "= #f" "parameter value, false" - gdb_test "show print s" "Command is not documented is off." "show parameter off" + gdb_test "show print s" \ + "The current value of 'print s' is off\\." "show parameter off" gdb_test_no_output "set print s on" - gdb_test "show print s" "Command is not documented is on." "show parameter on" + gdb_test "show print s" \ + "The current value of 'print s' is on\\." "show parameter on" gdb_test "guile (print (parameter-value prev-ambig))" "= #t" "parameter value, true" - gdb_test "help show print s" "This command is not documented." "show help" - gdb_test "help set print s" "This command is not documented." "set help" - gdb_test "help set print" "set print s -- This command is not documented.*" "general help" + gdb_test "help show print s" \ + [multi_line \ + "^Show the current value of 'print s'\\." \ + "This command is not documented\\."] \ + "show help" + gdb_test "help set print s" \ + [multi_line \ + "Set the current value of 'print s'\\." \ + "This command is not documented\\."] \ + "set help" + gdb_test "help set print" \ + "set print s -- Set the current value of 'print s'\\..*" \ + "general help" +} + +gdb_test_multiline "create set/show foo1 prefix commands" \ + "guile" "" \ + "(register-command! (make-command \"set foo1\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "(register-command! (make-command \"show foo1\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "end" + +gdb_test_multiline "create set/show foo1 baz1 prefix commands" \ + "guile" "" \ + "(register-command! (make-command \"set foo1 baz1\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "(register-command! (make-command \"show foo1 baz1\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "end" + +gdb_test_multiline "create 'foo bar' parameter" \ + "guile" "" \ + "(register-parameter! (make-parameter \"foo bar\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:parameter-type PARAM_BOOLEAN" "" \ + " #:show-func (lambda (self value)" "" \ + " (format #f \"The state of 'foo bar' is ~a.\" value))" "" \ + " #:initial-value #t))" "" \ + "end" + +gdb_test "show foo1 bar" "^The state of 'foo bar' is on\\." "show parameter 'foo bar'" + +gdb_test_multiline "create set/show foo2 prefix commands" \ + "guile" "" \ + "(register-command! (make-command \"set foo2\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "(register-command! (make-command \"show foo2\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "end" "" + +gdb_test_multiline "create ambiguous 'foo baz' parameter" \ + "guile" "" \ + "(register-parameter! (make-parameter \"foo baz\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:parameter-type PARAM_BOOLEAN" "" \ + " #:show-func (lambda (self value)" "" \ + " (format #f \"The state of 'foo baz' is ~a.\" value))" "" \ + " #:initial-value #t))" "" \ + "end" \ + [multi_line \ + "Out of range: could not find command prefix 'foo' in position 1: \"foo baz\"" \ + "Error while executing Scheme code."] + +with_test_prefix "empty doc string" { + gdb_test_multiline "empty doc string parameter" \ + "guile" "" \ + "(register-parameter! (make-parameter \"empty-doc-string\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_ZINTEGER" "" \ + " #:doc \"\"" "" \ + " #:set-doc \"Set doc string.\"" "" \ + " #:show-doc \"Show doc string.\"))" "" \ + "end" + + gdb_test "help set empty-doc-string" "^Set doc string\\." + gdb_test "help show empty-doc-string" "^Show doc string\\." +} + +with_test_prefix "set/show parameter" { + # This first set/show prefix command doesn't have an invoke + # method. As such, GDB installs the default invoke behaviour; set + # prints the full list of sub-commands, and show prints all the + # sub-command values. + gdb_test_multiline "Setup set/show parameter prefix with no invoke" \ + "guile" "" \ + "(register-command! (make-command \"set test-prefix\"" "" \ + " #:prefix? #t" "" \ + " #:command-class COMMAND_NONE))" ""\ + "(register-command! (make-command \"show test-prefix\"" "" \ + " #:prefix? #t" "" \ + " #:command-class COMMAND_NONE))" ""\ + "(register-parameter! (make-parameter \"test-prefix param-1\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_BOOLEAN))" "" \ + "(register-parameter! (make-parameter \"test-prefix param-2\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_UINTEGER))" "" \ + "(register-parameter! (make-parameter \"test-prefix param-3\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_STRING))" "" \ + "end" "" + + gdb_test "set test-prefix" \ + [multi_line \ + "List of \"set test-prefix\" subcommands:" \ + "" \ + "set test-prefix param-1 -- Set the current value of 'test-prefix param-1'." \ + "set test-prefix param-2 -- Set the current value of 'test-prefix param-2'." \ + "set test-prefix param-3 -- Set the current value of 'test-prefix param-3'." \ + "" \ + "Type \"help set test-prefix\" followed by subcommand name for full documentation\\." \ + "Type \"apropos word\" to search for commands related to \"word\"\\." \ + "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \ + "Command name abbreviations are allowed if unambiguous\\."] + + gdb_test "show test-prefix" \ + [multi_line \ + "test-prefix param-1: The current value of 'test-prefix param-1' is off\\." \ + "test-prefix param-2: The current value of 'test-prefix param-2' is 0\\." \ + "test-prefix param-3: The current value of 'test-prefix param-3' is \"\"\\."] + + # This next set/show prefix has an invoke method, which will be + # called instead of the default behaviour tested above. + gdb_test_multiline "Setup set/show parameter prefix with invoke" \ + "guile" "" \ + "(register-command! (make-command \"set test-prefix-2\"" "" \ + " #:prefix? #t" "" \ + " #:command-class COMMAND_NONE" ""\ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display \"invoke -- set\\n\"))))" "" \ + "(register-command! (make-command \"show test-prefix-2\"" "" \ + " #:prefix? #t" "" \ + " #:command-class COMMAND_NONE" ""\ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display \"invoke -- show\\n\"))))" "" \ + "(register-parameter! (make-parameter \"test-prefix-2 param-1\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_BOOLEAN))" "" \ + "(register-parameter! (make-parameter \"test-prefix-2 param-2\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_UINTEGER))" "" \ + "(register-parameter! (make-parameter \"test-prefix-2 param-3\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_STRING))" "" \ + "end" "" + + gdb_test "set test-prefix-2" "^invoke -- set" + + gdb_test "show test-prefix-2" "^invoke -- show" } rename scm_param_test_maybe_no_output "" diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp index 69744dd..86b55bb 100644 --- a/gdb/testsuite/gdb.linespec/linespec.exp +++ b/gdb/testsuite/gdb.linespec/linespec.exp @@ -194,6 +194,12 @@ gdb_test "break lspec.h:$line" \ "Breakpoint \[0-9\]+ at $hex: file .*lspec.h, line $line." \ "set breakpoint in f1" +# This should only have a single location -- in no_multi_locs. +set line [gdb_get_line_number no_multi_locs] +gdb_test "break $line" \ + "Breakpoint \[0-9\]+ at $hex: file .*$srcfile, line $line." \ + "set breakpoint at no_multi_locs" + # # Multi-inferior tests. # diff --git a/gdb/testsuite/gdb.linespec/lspec.cc b/gdb/testsuite/gdb.linespec/lspec.cc index bb660fb..ab0a193 100644 --- a/gdb/testsuite/gdb.linespec/lspec.cc +++ b/gdb/testsuite/gdb.linespec/lspec.cc @@ -13,6 +13,8 @@ int body_elsewhere() #include "body.h" } +void no_multi_locs () { {int var = 0;} } + int main() { return dupname(0) + m(0) + n(0) + f1() + f2() + body_elsewhere(); diff --git a/gdb/testsuite/gdb.mi/interrupt-thread-group.exp b/gdb/testsuite/gdb.mi/interrupt-thread-group.exp index ff35109..869fb1c 100644 --- a/gdb/testsuite/gdb.mi/interrupt-thread-group.exp +++ b/gdb/testsuite/gdb.mi/interrupt-thread-group.exp @@ -54,7 +54,7 @@ mi_send_resuming_command "exec-continue --thread-group i1" \ # We can't run a second inferior on stub targets. We can still test with one # inferior and ensure that the command has the desired effect. -set use_second_inferior [expr {![use_gdb_stub]}] +set use_second_inferior [expr {![use_gdb_stub] && [allow_multi_inferior_tests]}] if { $use_second_inferior } { mi_gdb_test "-add-inferior" \ diff --git a/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp b/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp index 9897b2b..0a89a8a 100644 --- a/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp +++ b/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp @@ -16,7 +16,7 @@ # Check that when GDB fails to evaluate the condition of a conditional # breakpoint we only get one *stopped notification. In this test case # the breakpoint condition fails due to throwing an uncaught C++ -# excpetion. +# exception. require allow_cplus_tests diff --git a/gdb/testsuite/gdb.mi/mi-multi-commands.exp b/gdb/testsuite/gdb.mi/mi-multi-commands.exp index 20b8d46..3bc63eb 100644 --- a/gdb/testsuite/gdb.mi/mi-multi-commands.exp +++ b/gdb/testsuite/gdb.mi/mi-multi-commands.exp @@ -90,7 +90,7 @@ proc run_test { args } { # looking for. However, due to the unpredictable # intermingling, it's much easier if we drop the ^ anchor. # However, with this gone dejagnu would sometimes match the - # second comand output before the first commands output. + # second command output before the first commands output. # # This approach just looks for the first command output, then, # once that has been found, we start looking for the second diff --git a/gdb/testsuite/gdb.mi/mi-var-display.exp b/gdb/testsuite/gdb.mi/mi-var-display.exp index 61b3894..5535368 100644 --- a/gdb/testsuite/gdb.mi/mi-var-display.exp +++ b/gdb/testsuite/gdb.mi/mi-var-display.exp @@ -96,7 +96,7 @@ mi_gdb_test "-var-evaluate-expression bar" \ # Desc: change value of bar mi_gdb_test "-var-assign bar 3" \ "\\^done,value=\"0x3\"" \ - "assing to variable bar" + "assign to variable bar" mi_gdb_test "-var-set-format bar decimal" \ "\\^done,format=\"decimal\",value=\"3\"" \ @@ -152,7 +152,7 @@ mi_gdb_test "-var-evaluate-expression foo" \ # Desc: change value of foo mi_gdb_test "-var-assign foo 3" \ "\\^done,value=\"03\"" \ - "assing to variable foo" + "assign to variable foo" mi_gdb_test "-var-set-format foo decimal" \ "\\^done,format=\"decimal\",value=\"3\"" \ diff --git a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp index 9198cfb..f85e108 100644 --- a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp +++ b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp @@ -40,6 +40,8 @@ standard_testfile # gdbserver modes are supported. require !use_gdb_stub +require allow_multi_inferior_tests + set compile_options "debug pthreads" if {[build_executable $testfile.exp $testfile ${srcfile} ${compile_options}] == -1} { untested "failed to compile" @@ -985,7 +987,7 @@ proc_with_prefix test_mi_stack_select_frame { mode } { # Now use the '-stack-select-frame' command with the --frame # option, this verifies that even when the frame GDB would - # swith to is the same as the frame specified with --frame, an + # switch to is the same as the frame specified with --frame, an # event is still sent to the CLI. set cli_re [make_cli_re $mode -1 -1 0] diff --git a/gdb/testsuite/gdb.multi/attach-no-multi-process.exp b/gdb/testsuite/gdb.multi/attach-no-multi-process.exp index 28aabaf..502f309 100644 --- a/gdb/testsuite/gdb.multi/attach-no-multi-process.exp +++ b/gdb/testsuite/gdb.multi/attach-no-multi-process.exp @@ -59,7 +59,10 @@ proc test {target_non_stop} { "switch to inferior 2" set res [gdbserver_start "--multi" ""] set gdbserver_gdbport [lindex $res 1] - gdb_target_cmd "extended-remote" $gdbserver_gdbport + if { [gdb_target_cmd_ext "extended-remote" $gdbserver_gdbport] == 2 } { + unsupported "non-stop RSP" + return + } # Start a program, then attach to it. set spawn_id_list [spawn_wait_for_attach [list $binfile]] diff --git a/gdb/testsuite/gdb.multi/attach-while-running.exp b/gdb/testsuite/gdb.multi/attach-while-running.exp index 5b63030..723ebb2 100644 --- a/gdb/testsuite/gdb.multi/attach-while-running.exp +++ b/gdb/testsuite/gdb.multi/attach-while-running.exp @@ -37,6 +37,7 @@ standard_testfile require can_spawn_for_attach +require allow_multi_inferior_tests if { [build_executable "failed to prepare" ${testfile} ${srcfile}] } { return @@ -49,7 +50,7 @@ proc do_test {} { } gdb_test -no-prompt-anchor "run &" - gdb_test "add-inferior" "Added inferior 2 on connection 1 .*" + gdb_test -no-prompt-anchor "add-inferior" "Added inferior 2 on connection 1 .*" gdb_test "inferior 2" "Switching to inferior 2 .*" set spawn_id [spawn_wait_for_attach $::binfile] diff --git a/gdb/testsuite/gdb.multi/bp-thread-specific.exp b/gdb/testsuite/gdb.multi/bp-thread-specific.exp index 32b7602..3fe4c20 100644 --- a/gdb/testsuite/gdb.multi/bp-thread-specific.exp +++ b/gdb/testsuite/gdb.multi/bp-thread-specific.exp @@ -19,6 +19,8 @@ # Also check that the correct thread-ids are used in the saved # breakpoints file. +require allow_multi_inferior_tests + # The plain remote target can't do multiple inferiors. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.multi/dummy-frame-restore.exp b/gdb/testsuite/gdb.multi/dummy-frame-restore.exp index 1a9d413..4119e3f 100644 --- a/gdb/testsuite/gdb.multi/dummy-frame-restore.exp +++ b/gdb/testsuite/gdb.multi/dummy-frame-restore.exp @@ -19,6 +19,8 @@ set executable ${testfile} # The plain remote target can't do multiple inferiors. require !use_gdb_stub +require allow_multi_inferior_tests + if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} { return -1 } diff --git a/gdb/testsuite/gdb.multi/multi-arch.exp b/gdb/testsuite/gdb.multi/multi-arch.exp index d0ae511..1d41ba5 100644 --- a/gdb/testsuite/gdb.multi/multi-arch.exp +++ b/gdb/testsuite/gdb.multi/multi-arch.exp @@ -18,6 +18,8 @@ set testfile "multi-arch" +require allow_multi_inferior_tests + # The plain remote target can't do multiple inferiors. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.multi/multi-attach.exp b/gdb/testsuite/gdb.multi/multi-attach.exp index 2d702ee..210c8ca 100644 --- a/gdb/testsuite/gdb.multi/multi-attach.exp +++ b/gdb/testsuite/gdb.multi/multi-attach.exp @@ -17,6 +17,8 @@ # Test attaching to multiple threaded programs. +require allow_multi_inferior_tests + standard_testfile require can_spawn_for_attach diff --git a/gdb/testsuite/gdb.multi/multi-exit.exp b/gdb/testsuite/gdb.multi/multi-exit.exp index 71236c1..8393067 100644 --- a/gdb/testsuite/gdb.multi/multi-exit.exp +++ b/gdb/testsuite/gdb.multi/multi-exit.exp @@ -24,6 +24,8 @@ standard_testfile +require allow_multi_inferior_tests + require !use_gdb_stub if {[build_executable "failed to prepare" $testfile $srcfile]} { diff --git a/gdb/testsuite/gdb.multi/multi-kill.exp b/gdb/testsuite/gdb.multi/multi-kill.exp index 48a2534..7d66ab0 100644 --- a/gdb/testsuite/gdb.multi/multi-kill.exp +++ b/gdb/testsuite/gdb.multi/multi-kill.exp @@ -24,6 +24,8 @@ standard_testfile +require allow_multi_inferior_tests + require !use_gdb_stub if {[build_executable "failed to prepare" $testfile $srcfile {debug}]} { diff --git a/gdb/testsuite/gdb.multi/multi-re-run.exp b/gdb/testsuite/gdb.multi/multi-re-run.exp index 002de57..4caadea 100644 --- a/gdb/testsuite/gdb.multi/multi-re-run.exp +++ b/gdb/testsuite/gdb.multi/multi-re-run.exp @@ -20,6 +20,8 @@ # misbehave, including failing to load libthread_db.so. See PR # gdb/25410. +require allow_multi_inferior_tests + # Build two executables, with different symbols. set exec1 "multi-re-run-1" diff --git a/gdb/testsuite/gdb.multi/multi-target.exp.tcl b/gdb/testsuite/gdb.multi/multi-target.exp.tcl index 8c24602..dc88ca4 100644 --- a/gdb/testsuite/gdb.multi/multi-target.exp.tcl +++ b/gdb/testsuite/gdb.multi/multi-target.exp.tcl @@ -175,6 +175,10 @@ proc multi_target_prepare {} { return 0 } + if {![allow_multi_inferior_tests]} { + return 0 + } + # The plain remote target can't do multiple inferiors. if {[target_info gdb_protocol] != ""} { return 0 diff --git a/gdb/testsuite/gdb.multi/multi-term-settings.exp b/gdb/testsuite/gdb.multi/multi-term-settings.exp index 2f8b3b8..38322be 100644 --- a/gdb/testsuite/gdb.multi/multi-term-settings.exp +++ b/gdb/testsuite/gdb.multi/multi-term-settings.exp @@ -25,6 +25,8 @@ standard_testfile +require allow_multi_inferior_tests + require can_spawn_for_attach if [build_executable "failed to prepare" $testfile $srcfile {debug}] { diff --git a/gdb/testsuite/gdb.multi/start-inferior-specific.exp b/gdb/testsuite/gdb.multi/start-inferior-specific.exp index 819c1c3..74f984c 100644 --- a/gdb/testsuite/gdb.multi/start-inferior-specific.exp +++ b/gdb/testsuite/gdb.multi/start-inferior-specific.exp @@ -25,6 +25,8 @@ standard_testfile .c -other.c +require allow_multi_inferior_tests + require !use_gdb_stub set srcfile_other ${srcfile2} diff --git a/gdb/testsuite/gdb.multi/stop-all-on-exit.exp b/gdb/testsuite/gdb.multi/stop-all-on-exit.exp index b4ff09c..47071f3 100644 --- a/gdb/testsuite/gdb.multi/stop-all-on-exit.exp +++ b/gdb/testsuite/gdb.multi/stop-all-on-exit.exp @@ -18,6 +18,8 @@ # Test that in all-stop mode with multiple inferiors, GDB stops all # threads upon receiving an exit event from one of the inferiors. +require allow_multi_inferior_tests + # This is a test specific for a native target, where we use the # "-exec" argument to "add-inferior" and we explicitly don't do # "maint set target-non-stop on". diff --git a/gdb/testsuite/gdb.multi/tids-gid-reset.exp b/gdb/testsuite/gdb.multi/tids-gid-reset.exp index 6cc27eb..1785ac2 100644 --- a/gdb/testsuite/gdb.multi/tids-gid-reset.exp +++ b/gdb/testsuite/gdb.multi/tids-gid-reset.exp @@ -54,6 +54,8 @@ with_test_prefix "single-inferior" { # non-extended gdbserver is not supported. require !use_gdb_stub +require allow_multi_inferior_tests + # Test with multiple inferiors. This time, since we restart inferior # 1 while inferior 2 still has threads, then the new thread 1.1 should # end up with GID == 3, since we won't be able to reset the global diff --git a/gdb/testsuite/gdb.multi/tids.exp b/gdb/testsuite/gdb.multi/tids.exp index b84f908..436a38a 100644 --- a/gdb/testsuite/gdb.multi/tids.exp +++ b/gdb/testsuite/gdb.multi/tids.exp @@ -124,6 +124,9 @@ with_test_prefix "single inferior" { gdb_test "print \$_inferior_thread_count" " = 1" } +# The rest of the tests require running multiple inferiors. +require allow_multi_inferior_tests + # "info threads" while there are multiple inferiors should show # qualified thread IDs. with_test_prefix "two inferiors" { @@ -290,7 +293,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 +415,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.multi/watchpoint-multi-exit.exp b/gdb/testsuite/gdb.multi/watchpoint-multi-exit.exp index 70c3da9..3446296 100644 --- a/gdb/testsuite/gdb.multi/watchpoint-multi-exit.exp +++ b/gdb/testsuite/gdb.multi/watchpoint-multi-exit.exp @@ -17,6 +17,8 @@ # watchpoints don't end up with stale locations, preventing resumption # of other inferiors. +require allow_fork_tests + standard_testfile if {[build_executable "failed to build" $testfile $srcfile {debug}]} { diff --git a/gdb/testsuite/gdb.multi/watchpoint-multi.exp b/gdb/testsuite/gdb.multi/watchpoint-multi.exp index f448689..b0c8731 100644 --- a/gdb/testsuite/gdb.multi/watchpoint-multi.exp +++ b/gdb/testsuite/gdb.multi/watchpoint-multi.exp @@ -16,6 +16,8 @@ standard_testfile set executable ${testfile} +require allow_multi_inferior_tests + # Multiple inferiors are needed, therefore both native and extended gdbserver # modes are supported. Only non-extended gdbserver is not supported. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.opt/break-on-_exit.exp b/gdb/testsuite/gdb.opt/break-on-_exit.exp index 2b94be8..9295fea 100644 --- a/gdb/testsuite/gdb.opt/break-on-_exit.exp +++ b/gdb/testsuite/gdb.opt/break-on-_exit.exp @@ -25,7 +25,7 @@ # for libc, then the breakpoint is set on the exec-local _exit@plt instead, # and that functionality will also not be used. # -# We may get the required setup in case of a libc with misssing separate +# We may get the required setup in case of a libc with missing separate # debuginfo, but we want the same effect if that debuginfo is installed. # # So, we use -readnever to read minimal symbols, but not non-miminal symbols. diff --git a/gdb/testsuite/gdb.pascal/integers.exp b/gdb/testsuite/gdb.pascal/integers.exp index 5c878c5..c9974a1 100644 --- a/gdb/testsuite/gdb.pascal/integers.exp +++ b/gdb/testsuite/gdb.pascal/integers.exp @@ -59,7 +59,7 @@ gdb_test "next" "l := k;" "next to 'l := k' line" gdb_test "print j" " = 2" # k should be equal to 3 gdb_test "print k" " = 3" -# But l shoud still be zero +# But l should still be zero if { $pascal_compiler_is_gpc } { setup_xfail *-*-* } diff --git a/gdb/testsuite/gdb.python/gdb_leak_detector.py b/gdb/testsuite/gdb.python/gdb_leak_detector.py index b0f6d47..8f74b67 100644 --- a/gdb/testsuite/gdb.python/gdb_leak_detector.py +++ b/gdb/testsuite/gdb.python/gdb_leak_detector.py @@ -19,6 +19,7 @@ import os import tracemalloc + import gdb diff --git a/gdb/testsuite/gdb.python/py-cmd.exp b/gdb/testsuite/gdb.python/py-cmd.exp index f76c176..5ac5712 100644 --- a/gdb/testsuite/gdb.python/py-cmd.exp +++ b/gdb/testsuite/gdb.python/py-cmd.exp @@ -328,4 +328,89 @@ proc_with_prefix test_command_redefining_itself {} { "call command redefining itself 2" } +# Try to create commands using unknown prefixes and check GDB gives an +# error. There's also a test in here for an ambiguous prefix, which +# gives the same error. +proc_with_prefix test_unknown_prefix {} { + clean_restart + + gdb_test_no_output "python gdb.Command('foo1', gdb.COMMAND_NONE, prefix=True)" + gdb_test_no_output "python gdb.Command('foo cmd', gdb.COMMAND_NONE)" + + foreach prefix { "xxx" "foo xxx" "foo1 xxx" } { + gdb_test "python gdb.Command('$prefix cmd', gdb.COMMAND_NONE)" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \ + "Error occurred in Python: Could not find command prefix $prefix\\."] + } + + gdb_test_no_output "python gdb.Command('foo2', gdb.COMMAND_NONE, prefix=True)" + + foreach prefix { "foo" "foo xxx" "foo1 xxx" "foo2 xxx" } { + gdb_test "python gdb.Command('$prefix cmd2', gdb.COMMAND_NONE)" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \ + "Error occurred in Python: Could not find command prefix $prefix\\."] + } +} + +# Check what happens if a command object is called without an 'invoke' +# method. +proc_with_prefix test_deleting_invoke_methods {} { + clean_restart + + gdb_test_multiline "create 'foo' prefix command" \ + "python" "" \ + "class test_prefix(gdb.Command):" "" \ + " def __init__ (self):" "" \ + " super().__init__ (\"foo\", gdb.COMMAND_USER, prefix=True)" "" \ + " def invoke (self, arg, from_tty):" "" \ + " print(\"In 'foo' invoke: %s\" % arg)" "" \ + "foo = test_prefix()" "" \ + "end" "" + + gdb_test_multiline "create 'foo bar' command" \ + "python" "" \ + "class test_cmd(gdb.Command):" "" \ + " def __init__ (self):" "" \ + " super().__init__ (\"foo bar\", gdb.COMMAND_USER)" "" \ + " def invoke (self, arg, from_tty):" "" \ + " print(\"In 'foo bar' invoke: %s\" % arg)" "" \ + "foo_bar = test_cmd()" "" \ + "end" "" + + gdb_test "foo def" "In 'foo' invoke: def" \ + "call 'foo' with an unknown sub-command" + + gdb_test "foo bar def" "In 'foo bar' invoke: def" \ + "call 'foo bar' with arguments" + + gdb_test_no_output "python del(foo_bar.__class__.invoke)" \ + "delete invoke from test_cmd class" + + with_test_prefix "after deleting test_cmd.invoke" { + gdb_test "foo def" "In 'foo' invoke: def" \ + "call 'foo' with an unknown sub-command" + + gdb_test "foo bar def" \ + "^Python command object missing 'invoke' method\\." \ + "call 'foo bar' with arguments" + } + + gdb_test_no_output "python del(foo.__class__.invoke)" \ + "delete invoke from test_prefix class" + + with_test_prefix "after deleting test_prefix.invoke" { + gdb_test "foo def" \ + "^Python command object missing 'invoke' method\\." \ + "call 'foo' with an unknown sub-command" + + gdb_test "foo bar def" \ + "^Python command object missing 'invoke' method\\." \ + "call 'foo bar' with arguments" + } +} + test_command_redefining_itself +test_unknown_prefix +test_deleting_invoke_methods 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 38f33c3..bf61668 100644 --- a/gdb/testsuite/gdb.python/py-inferior-leak.py +++ b/gdb/testsuite/gdb.python/py-inferior-leak.py @@ -13,7 +13,14 @@ # 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-parameter-prefix.exp b/gdb/testsuite/gdb.python/py-parameter-prefix.exp new file mode 100644 index 0000000..eb09fe7 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-parameter-prefix.exp @@ -0,0 +1,382 @@ +# 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/>. + +# This file is part of the GDB testsuite. It tests +# gdb.ParameterPrefix. See each of the test procs for a full +# description of what is being tested. + +load_lib gdb-python.exp + +require allow_python_tests + +clean_restart + +# Helper proc to generate the output of 'show PREFIX' commands for the +# case where the prefix command doesn't handle unknown sub-commands. +# In this case GDB will list the value of every sub-command under +# PREFIX. +proc make_show_prefix_re { prefix } { + return "$prefix param-1:\\s+The current value of '$prefix param-1' is \"off\"\\." +} + +# Helper proc to generate the help text that describes all of the sub +# commands under PREFIX. The MODE is either 'set' or 'show'. This +# output will appear for 'help MODE PREFIX' and also for 'set PREFIX'. +proc make_sub_cmd_help_re { mode prefix } { + if { $mode == "set" } { + set word "Set" + } else { + set word "Show" + } + + return \ + [multi_line \ + "List of \"$mode $prefix\" subcommands:" \ + "" \ + "$mode $prefix param-1 -- $word the current value of '$prefix param-1'\\." \ + "" \ + "Type \"help $mode $prefix\" followed by subcommand name for full documentation\\." \ + "Type \"apropos word\" to search for commands related to \"word\"\\." \ + "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \ + "Command name abbreviations are allowed if unambiguous\\."] +} + +# Helper proc to generate the output of 'help MODE PREFIX', where MODE +# will be either 'set' or 'show'. The HELP_TEXT is the expected help +# text for this prefix command, this should not be a regexp, as this +# proc converts the text to a regexp. +# +# Return a single regexp which should match the output. +proc make_help_re { mode prefix help_text } { + set help_re [string_to_regexp $help_text] + + return \ + [multi_line \ + "$help_re" \ + "" \ + [make_sub_cmd_help_re $mode $prefix]] +} + +# Create gdb.ParameterPrefix without using a sub-class, both with, and +# without a doc string. For the doc string case, test single line, +# and multi-line doc strings. +proc_with_prefix test_basic_usage {} { + gdb_test_multiline "some basic ParameterPrefix usage" \ + "python" "" \ + "gdb.ParameterPrefix('prefix-1', gdb.COMMAND_NONE)" "" \ + "gdb.Parameter('prefix-1 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-1 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.ParameterPrefix('prefix-2', gdb.COMMAND_NONE," "" \ + " \"\"\"This is prefix-2 help string.\"\"\")" "" \ + "gdb.Parameter('prefix-2 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.ParameterPrefix('prefix-3', gdb.COMMAND_NONE," "" \ + " \"\"\"This is prefix-3 help string." "" \ + " " "" \ + " This help text spans multiple lines.\"\"\")" "" \ + "gdb.Parameter('prefix-3 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + foreach mode { "set" "show" } { + gdb_test "help $mode prefix-1" \ + [make_help_re $mode "prefix-1" \ + "This command is not documented."] + + gdb_test "help $mode prefix-2" \ + [make_help_re $mode "prefix-2" \ + "This is prefix-2 help string."] + + gdb_test "help $mode prefix-3" \ + [make_help_re $mode "prefix-3" \ + [multi_line \ + "This is prefix-3 help string." \ + "" \ + "This help text spans multiple lines."]] + + foreach prefix { prefix-1 prefix-2 prefix-3 } { + gdb_test "$mode $prefix xxx" \ + "^Undefined $mode $prefix command: \"xxx\"\\. Try \"help $mode $prefix\"\\." + } + } + + foreach prefix { prefix-1 prefix-2 prefix-3 } { + gdb_test "set $prefix" \ + [make_sub_cmd_help_re "set" $prefix] + + gdb_test "show $prefix" \ + [make_show_prefix_re $prefix] + } +} + +# Create a sub-class of gdb.ParameterPrefix, but don't do anything +# particularly interesting. Again test the with and without +# documentation string cases. +proc_with_prefix test_simple_sub_class {} { + gdb_test_multiline "some basic ParameterPrefix usage" \ + "python" "" \ + "class BasicParamPrefix(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefix('prefix-4')" "" \ + "gdb.Parameter('prefix-4 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithSingleLineDoc(gdb.ParameterPrefix):" "" \ + " \"\"\"This is a single line doc string.\"\"\"" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefixWithSingleLineDoc('prefix-5')" "" \ + "gdb.Parameter('prefix-5 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithMultiLineDoc(gdb.ParameterPrefix):" "" \ + " \"\"\"This is a multi line doc string." "" \ + " " "" \ + " The rest of the doc string is here.\"\"\"" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefixWithMultiLineDoc('prefix-6')" "" \ + "gdb.Parameter('prefix-6 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithDocParameter(gdb.ParameterPrefix):" "" \ + " \"\"\"This is an unsused doc string.\"\"\"" "" \ + " def __init__(self, name, doc):" "" \ + " super().__init__(name, gdb.COMMAND_NONE, doc)" "" \ + "BasicParamPrefixWithDocParameter('prefix-7'," "" \ + " \"\"\"The doc string text is here.\"\"\")" "" \ + "gdb.Parameter('prefix-7 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + foreach mode { "set" "show" } { + gdb_test "help $mode prefix-4" \ + [make_help_re $mode "prefix-4" \ + "This command is not documented."] + + gdb_test "help $mode prefix-5" \ + [make_help_re $mode "prefix-5" \ + "This is a single line doc string."] + + gdb_test "help $mode prefix-6" \ + [make_help_re $mode "prefix-6" \ + [multi_line \ + "This is a multi line doc string." \ + "" \ + "The rest of the doc string is here."]] + + gdb_test "help $mode prefix-7" \ + [make_help_re $mode "prefix-7" \ + "The doc string text is here."] + + foreach prefix { prefix-4 prefix-5 prefix-6 prefix-7 } { + gdb_test "$mode $prefix xxx" \ + "^Undefined $mode $prefix command: \"xxx\"\\. Try \"help $mode $prefix\"\\." + } + } + + foreach prefix { prefix-4 prefix-5 prefix-6 prefix-7 } { + gdb_test "set $prefix" \ + [make_sub_cmd_help_re "set" $prefix] + + gdb_test "show $prefix" \ + [make_show_prefix_re $prefix] + } +} + +# Create a sub-class of gdb.ParameterPrefix, and make use of +# 'invoke_set' and 'invoke_show'. Test that the invoke method is +# executed when expected, and that, by default, these invoke methods +# repeat when the user issues an empty command. +proc_with_prefix test_prefix_with_invoke {} { + gdb_test_multiline "ParameterPrefix with invoke_set" \ + "python" "" \ + "class PrefixWithInvokeSet(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set (a): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeSet('prefix-8')" "" \ + "gdb.Parameter('prefix-8 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithInvokeShow(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show (b): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeShow('prefix-9')" "" \ + "gdb.Parameter('prefix-9 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithBothInvoke(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set (c): \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show (d): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithBothInvoke('prefix-10')" "" \ + "gdb.Parameter('prefix-10 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + gdb_test "set prefix-8 xxx yyy" \ + "^invoke_set \\(a\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set \\(a\\): \"xxx yyy\" True" \ + "repeat set prefix-8 xxx yyy" + + gdb_test "show prefix-8 xxx yyy" \ + "^Undefined show prefix-8 command: \"xxx yyy\"\\. Try \"help show prefix-8\"\\." + + gdb_test "set prefix-9 xxx yyy" \ + "^Undefined set prefix-9 command: \"xxx yyy\"\\. Try \"help set prefix-9\"\\." + + gdb_test "show prefix-9 xxx yyy" \ + "^invoke_show \\(b\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_show \\(b\\): \"xxx yyy\" True" \ + "repeat show prefix-9 xxx yyy" + + gdb_test "set prefix-10 xxx yyy" \ + "^invoke_set \\(c\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set \\(c\\): \"xxx yyy\" True" \ + "repeat set prefix-10 xxx yyy" + + gdb_test "show prefix-10 xxx yyy" \ + "^invoke_show \\(d\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_show \\(d\\): \"xxx yyy\" True" \ + "repeat show prefix-10 xxx yyy" + + gdb_test "set prefix-8" \ + "^invoke_set \\(a\\): \"\" True" + + gdb_test "show prefix-8" \ + [make_show_prefix_re "prefix-8"] + + gdb_test "set prefix-9" \ + [make_sub_cmd_help_re "set" "prefix-9"] + + gdb_test "show prefix-9" \ + "^invoke_show \\(b\\): \"\" True" + + gdb_test "set prefix-10" \ + "^invoke_set \\(c\\): \"\" True" + + gdb_test "show prefix-10" \ + "^invoke_show \\(d\\): \"\" True" +} + +# Create ParameterPrefix sub-classes that make use of the +# dont_repeat() method. Check that the relevant set/show invoke +# callback doesn't repeat when an empty command is used. +proc_with_prefix test_dont_repeat {} { + gdb_test_multiline "ParameterPrefix with invoke_set and dont_repeat" \ + "python" "" \ + "class PrefixWithInvokeAndDoNotRepeatSet(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " self.dont_repeat()" "" \ + " print(f\"invoke_set: \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show: \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeAndDoNotRepeatSet('prefix-11')" "" \ + "gdb.Parameter('prefix-11 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithInvokeAndDoNotRepeatShow(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set: \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " self.dont_repeat()" "" \ + " print(f\"invoke_show: \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeAndDoNotRepeatShow('prefix-12')" "" \ + "gdb.Parameter('prefix-12 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + gdb_test "set prefix-11 xxx yyy" \ + "^invoke_set: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^" \ + "repeat set prefix-11 xxx yyy" + + gdb_test "show prefix-11 xxx yyy" \ + "^invoke_show: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "invoke_show: \"xxx yyy\" True" \ + "repeat show prefix-11 xxx yyy" + + gdb_test "set prefix-12 xxx yyy" \ + "^invoke_set: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set: \"xxx yyy\" True" \ + "repeat set prefix-12 xxx yyy" + + gdb_test "show prefix-12 xxx yyy" \ + "^invoke_show: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^" \ + "repeat show prefix-12 xxx yyy" +} + +# Create a parameter prefixm, and immediately add another prefix under +# the first. The important thing here is that the second prefix is +# created into an otherwise empty prefix as this triggered a bug at +# one point. +proc_with_prefix test_nested {} { + gdb_test_multiline "Create nested parameter prefixes" \ + "python" "" \ + "gdb.ParameterPrefix('prefix-13', gdb.COMMAND_NONE)" "" \ + "gdb.ParameterPrefix('prefix-13 prefix-14', gdb.COMMAND_NONE)" "" \ + "gdb.Parameter('prefix-13 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-13 param-2', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-13 prefix-14 param-3', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-13 prefix-14 param-4', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" "" + + gdb_test "show prefix-13 prefix-14" \ + [multi_line \ + "^prefix-13 prefix-14 param-3: The current value of 'prefix-13 prefix-14 param-3' is \"off\"\\." \ + "prefix-13 prefix-14 param-4: The current value of 'prefix-13 prefix-14 param-4' is \"off\"\\."] + + gdb_test "show prefix-13" \ + [multi_line \ + "^prefix-13 param-1: The current value of 'prefix-13 param-1' is \"off\"\\." \ + "prefix-13 param-2: The current value of 'prefix-13 param-2' is \"off\"\\." \ + "prefix-13 prefix-14 param-3: The current value of 'prefix-13 prefix-14 param-3' is \"off\"\\." \ + "prefix-13 prefix-14 param-4: The current value of 'prefix-13 prefix-14 param-4' is \"off\"\\."] + + gdb_test "set prefix-13 prefix-14" \ + [multi_line \ + "" \ + "set prefix-13 prefix-14 param-3 -- Set the current value of 'prefix-13 prefix-14 param-3'\\." \ + "set prefix-13 prefix-14 param-4 -- Set the current value of 'prefix-13 prefix-14 param-4'\\." \ + "" \ + ".*"] + + gdb_test "set prefix-13" \ + [multi_line \ + "" \ + "set prefix-13 param-1 -- Set the current value of 'prefix-13 param-1'\\." \ + "set prefix-13 param-2 -- Set the current value of 'prefix-13 param-2'\\." \ + "set prefix-13 prefix-14 -- This command is not documented\\." \ + "" \ + ".*"] +} + +test_basic_usage +test_simple_sub_class +test_prefix_with_invoke +test_dont_repeat +test_nested diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp index c15bef1..214c570 100644 --- a/gdb/testsuite/gdb.python/py-parameter.exp +++ b/gdb/testsuite/gdb.python/py-parameter.exp @@ -346,6 +346,91 @@ proc_with_prefix test_really_undocumented_parameter { } { "test general help" } +# Test a parameter in which the __doc__ string is empty or None. +proc_with_prefix test_empty_doc_parameter {} { + gdb_test_multiline "empty __doc__ parameter" \ + "python" "" \ + "class EmptyDocParam(gdb.Parameter):" "" \ + " __doc__ = \"\"" "" \ + " def __init__(self, name):" "" \ + " super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_empty_doc_param = EmptyDocParam('print test-empty-doc-param')" ""\ + "end" + + # Setting the __doc__ string to empty means GDB will completely + # elide it from the output. + gdb_test "help set print test-empty-doc-param" \ + "^Set the current value of 'print test-empty-doc-param'\\." + + gdb_test_multiline "None __doc__ parameter" \ + "python" "" \ + "class NoneDocParam(gdb.Parameter):" "" \ + " __doc__ = None" "" \ + " def __init__(self, name):" "" \ + " super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_none_doc_param = NoneDocParam('print test-none-doc-param')" ""\ + "end" + + # Setting the __doc__ string to None, or anything else that isn't + # a string, causes GDB to use a default string instead. + gdb_test "help set print test-none-doc-param" \ + [multi_line \ + "^Set the current value of 'print test-none-doc-param'\\." \ + "This command is not documented\\."] +} + +# Test a parameter in which the set_doc/show_doc strings are either +# empty, or None. +proc_with_prefix test_empty_set_show_doc_parameter {} { + gdb_test_multiline "empty set/show doc parameter" \ + "python" "" \ + "class EmptySetShowParam(gdb.Parameter):" "" \ + " set_doc = \"\"" "" \ + " show_doc = \"\"" "" \ + " def __init__(self, name):" "" \ + " super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_empty_set_show_param = EmptySetShowParam('print test-empty-set-show-param')" ""\ + "end" + + # Setting the set_doc/show_doc string to empty means GDB will use + # a suitable default string. + gdb_test "help set print test-empty-set-show-param" \ + [multi_line \ + "^Set the current value of 'print test-empty-set-show-param'\\." \ + "This command is not documented\\."] + + gdb_test "help show print test-empty-set-show-param" \ + [multi_line \ + "^Show the current value of 'print test-empty-set-show-param'\\." \ + "This command is not documented\\."] + + gdb_test_multiline "None set/show doc parameter" \ + "python" "" \ + "class NoneSetShowParam(gdb.Parameter):" "" \ + " set_doc = None" "" \ + " show_doc = None" "" \ + " def __init__(self, name):" "" \ + " super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_none_set_show_param = NoneSetShowParam('print test-none-set-show-param')" ""\ + "end" + + # Setting the set_doc/show_doc string to None (or any non-string + # value) means GDB will use a suitable default string. + gdb_test "help set print test-none-set-show-param" \ + [multi_line \ + "^Set the current value of 'print test-none-set-show-param'\\." \ + "This command is not documented\\."] + + gdb_test "help show print test-none-set-show-param" \ + [multi_line \ + "^Show the current value of 'print test-none-set-show-param'\\." \ + "This command is not documented\\."] +} + # Test deprecated API. Do not use in your own implementations. proc_with_prefix test_deprecated_api_parameter { } { clean_restart @@ -669,6 +754,104 @@ proc_with_prefix test_ambiguous_parameter {} { "Parameter .* is ambiguous.*Error occurred in Python.*" gdb_test "python print(gdb.parameter('test-ambiguous-value-1a'))" \ "Could not find parameter.*Error occurred in Python.*" + + # Create command prefixs 'set foo1' and 'show foo1'. + gdb_test_no_output "python gdb.Command('set foo1', gdb.COMMAND_NONE, prefix=True)" + gdb_test_no_output "python gdb.Command('show foo1', gdb.COMMAND_NONE, prefix=True)" + + # Create a parameter under 'foo1', but use a truncated prefix. At + # this point though, the prefix is not ambiguous. + gdb_test_no_output "python gdb.Parameter('foo bar', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" + gdb_test "python print(gdb.parameter('foo1 bar'))" "False" + + # Create another prefix command, similar in name to the first. + gdb_test_no_output "python gdb.Command('set foo2', gdb.COMMAND_NONE, prefix=True)" + gdb_test_no_output "python gdb.Command('show foo2', gdb.COMMAND_NONE, prefix=True)" + + # An attempt to create a parameter using an ambiguous prefix will give an error. + gdb_test "python gdb.Parameter('foo baz', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: Could not find command prefix foo\\." \ + "Error occurred in Python: Could not find command prefix foo\\."] +} + +# Check that creating a gdb.Parameter with an unknown command prefix results in an error. +proc_with_prefix test_unknown_prefix {} { + gdb_test_multiline "create parameter" \ + "python" "" \ + "class UnknownPrefixParam(gdb.Parameter):" "" \ + " def __init__ (self, name):" "" \ + " super().__init__ (name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "end" + + foreach prefix { "unknown-prefix" "style unknown-prefix" "style disassembler unknown-prefix"} { + gdb_test "python UnknownPrefixParam('$prefix new-param')" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \ + "Error occurred in Python: Could not find command prefix $prefix\\."] + } +} + +# Test the default behaviour of a set/show parameter prefix command. +proc_with_prefix test_set_show_parameters {} { + # This first set/show prefix command doesn't have an invoke + # method. As such, GDB installs the default invoke behaviour; set + # prints the full list of sub-commands, and show prints all the + # sub-command values. + gdb_test_multiline "Setup set/show parameter prefix with no invoke" \ + "python" "" \ + "class TestParamPrefix(gdb.Command):" "" \ + " \"\"\"TestParamPrefix documentation string.\"\"\"" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE, prefix = True)" "" \ + "TestParamPrefix('set test-prefix')" "" \ + "TestParamPrefix('show test-prefix')" "" \ + "gdb.Parameter('test-prefix param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('test-prefix param-2', gdb.COMMAND_NONE, gdb.PARAM_INTEGER)" "" \ + "gdb.Parameter('test-prefix param-3', gdb.COMMAND_NONE, gdb.PARAM_STRING)" "" \ + "end" + + gdb_test "set test-prefix" \ + [multi_line \ + "List of \"set test-prefix\" subcommands:" \ + "" \ + "set test-prefix param-1 -- Set the current value of 'test-prefix param-1'." \ + "set test-prefix param-2 -- Set the current value of 'test-prefix param-2'." \ + "set test-prefix param-3 -- Set the current value of 'test-prefix param-3'." \ + "" \ + "Type \"help set test-prefix\" followed by subcommand name for full documentation\\." \ + "Type \"apropos word\" to search for commands related to \"word\"\\." \ + "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \ + "Command name abbreviations are allowed if unambiguous\\."] + + gdb_test "show test-prefix" \ + [multi_line \ + "test-prefix param-1: The current value of 'test-prefix param-1' is \"off\"\\." \ + "test-prefix param-2: The current value of 'test-prefix param-2' is \"0\"\\." \ + "test-prefix param-3: The current value of 'test-prefix param-3' is \"\"\\."] + + # This next set/show prefix has an invoke method, which will be + # called instead of the default behaviour tested above. + gdb_test_multiline "Setup set/show parameter prefix with invoke" \ + "python" "" \ + "class TestParamPrefix(gdb.Command):" "" \ + " \"\"\"TestParamPrefix documentation string.\"\"\"" "" \ + " def __init__(self, name, mode):" "" \ + " self._mode = mode" "" \ + " super().__init__(self._mode + ' ' + name, gdb.COMMAND_NONE, prefix = True)" "" \ + " def invoke(self, args, from_tty):" "" \ + " print('invoke -- ' + self._mode)" "" \ + "TestParamPrefix('test-prefix-2', 'set')" "" \ + "TestParamPrefix('test-prefix-2', 'show')" "" \ + "gdb.Parameter('test-prefix-2 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('test-prefix-2 param-2', gdb.COMMAND_NONE, gdb.PARAM_INTEGER)" "" \ + "gdb.Parameter('test-prefix-2 param-3', gdb.COMMAND_NONE, gdb.PARAM_STRING)" "" \ + "end" + + gdb_test "set test-prefix-2" "^invoke -- set" + + gdb_test "show test-prefix-2" "^invoke -- show" } test_directories @@ -679,11 +862,15 @@ test_color_parameter test_file_parameter test_undocumented_parameter test_really_undocumented_parameter +test_empty_doc_parameter +test_empty_set_show_doc_parameter test_deprecated_api_parameter test_gdb_parameter test_integer_parameter test_throwing_parameter test_language test_ambiguous_parameter +test_unknown_prefix +test_set_show_parameters rename py_param_test_maybe_no_output "" 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.python/py-source-styling-2.exp b/gdb/testsuite/gdb.python/py-source-styling-2.exp index b13ee1f..ebf7f32 100644 --- a/gdb/testsuite/gdb.python/py-source-styling-2.exp +++ b/gdb/testsuite/gdb.python/py-source-styling-2.exp @@ -32,7 +32,9 @@ if { [build_executable "failed to build" $testfile $srcfile $opts] == -1 } { return } -clean_restart +with_ansi_styling_terminal { + clean_restart +} gdb_test_no_output "maint set gnu-source-highlight enabled off" @@ -40,16 +42,14 @@ gdb_load $binfile require {gdb_py_module_available pygments} -with_ansi_styling_terminal { - gdb_test_no_output "set style enabled on" - - gdb_test_multiple "list $line_number" "Styling of c++ keyword try" { - -re -wrap " try\r\n.*" { - # Unstyled. - fail $gdb_test_name - } - -re -wrap "" { - pass $gdb_test_name - } +gdb_test_no_output "set style enabled on" + +gdb_test_multiple "list $line_number" "Styling of c++ keyword try" { + -re -wrap " try\r\n.*" { + # Unstyled. + fail $gdb_test_name + } + -re -wrap "" { + pass $gdb_test_name } } diff --git a/gdb/testsuite/gdb.python/py-warning.exp b/gdb/testsuite/gdb.python/py-warning.exp new file mode 100644 index 0000000..6b26a4e --- /dev/null +++ b/gdb/testsuite/gdb.python/py-warning.exp @@ -0,0 +1,63 @@ +# 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/>. + +# Test the gdb.warning() function. + +load_lib gdb-python.exp + +require allow_python_tests + +clean_restart + +# Basic usage. +gdb_test "python gdb.warning(\"some text\")" \ + "warning: some text" + +# Basic usage with named argument. +gdb_test "python gdb.warning(text=\"a warning message\")" \ + "warning: a warning message" + +# Make sure GDB prints format specifiers correctly. +gdb_test "python gdb.warning(\"%s %d %p\")" \ + "warning: %s %d %p" + +# Empty string gives an error. +gdb_test "python gdb.warning(\"\")" \ + [multi_line \ + "Python Exception <class 'ValueError'>: Empty text string passed to gdb\\.warning" \ + "Error occurred in Python: Empty text string passed to gdb\\.warning"] + +# Missing argument gives an error. +set re1 \ + [multi_line \ + [string_to_regexp \ + [concat \ + "Python Exception <class 'TypeError'>:" \ + "function missing required argument 'text' (pos 1)"]] \ + [string_to_regexp \ + [concat \ + "Error occurred in Python:" \ + "function missing required argument 'text' (pos 1)"]]] +set re2 \ + [multi_line \ + [string_to_regexp \ + [concat \ + "Python Exception <class 'TypeError'>:" \ + "Required argument 'text' (pos 1) not found"]] \ + [string_to_regexp \ + [concat \ + "Error occurred in Python:" \ + "Required argument 'text' (pos 1) not found"]]] +gdb_test "python gdb.warning()" $re1|$re2 diff --git a/gdb/testsuite/gdb.replay/connect.exp b/gdb/testsuite/gdb.replay/connect.exp index 5790d38..26b7aa3 100644 --- a/gdb/testsuite/gdb.replay/connect.exp +++ b/gdb/testsuite/gdb.replay/connect.exp @@ -13,7 +13,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/>. */ # -# Starts a communication with gdbsever setting the remotelog file. +# Starts a communication with gdbserver setting the remotelog file. # Modifies the remotelog with update_log proc, injects an error message # instead of the expected replay to the vMustReplyEmpty packet in order # to test GDB reacts to the error response properly. After the remotelog diff --git a/gdb/testsuite/gdb.reverse/i386-avx-reverse.exp b/gdb/testsuite/gdb.reverse/i386-avx-reverse.exp index 0ff56dd..00f58f8 100644 --- a/gdb/testsuite/gdb.reverse/i386-avx-reverse.exp +++ b/gdb/testsuite/gdb.reverse/i386-avx-reverse.exp @@ -397,7 +397,7 @@ gdb_test_no_output "set \$ymm15.v2_int128 = {0x0, 0xcafeface}" "set ymm15 for vp if {[record_full_function "vzeroupper"] == true} { # Since vzeroupper needs to save 8 or 16 registers, let's check what was # actually recorded, instead of just undoing an instruction. Only - # really check the values of egisters 0, 1, 2 and 15 because those are + # really check the values of registers 0, 1, 2 and 15 because those are # the only ones we're setting. gdb_test "maint print record-instruction" \ [multi_line "Register ymm0h changed: 74565" \ diff --git a/gdb/testsuite/gdb.reverse/solib-precsave.exp b/gdb/testsuite/gdb.reverse/solib-precsave.exp index 277e33c..82b08cd 100644 --- a/gdb/testsuite/gdb.reverse/solib-precsave.exp +++ b/gdb/testsuite/gdb.reverse/solib-precsave.exp @@ -140,7 +140,7 @@ gdb_test_multiple "reverse-step" "reverse-step into solib function one" { pass $gdb_test_name } } -# Depending on wether the closing } has a line associated, we might have +# Depending on whether the closing } has a line associated, we might have # different acceptable results here gdb_test_multiple "reverse-step" "reverse-step within solib function one" { -re -wrap "return y;.*" { diff --git a/gdb/testsuite/gdb.reverse/solib-reverse.exp b/gdb/testsuite/gdb.reverse/solib-reverse.exp index 1e22e91..b2ef9b0 100644 --- a/gdb/testsuite/gdb.reverse/solib-reverse.exp +++ b/gdb/testsuite/gdb.reverse/solib-reverse.exp @@ -116,7 +116,7 @@ gdb_test_multiple "reverse-step" "reverse-step into solib function one" { pass $gdb_test_name } } -# Depending on wether the closing } has a line associated, we might have +# Depending on whether the closing } has a line associated, we might have # different acceptable results here gdb_test_multiple "reverse-step" "reverse-step within solib function one" { -re -wrap "return y;.*" { 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/code-object-load-while-breakpoint-hit.cpp b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.cpp new file mode 100644 index 0000000..d75bc76 --- /dev/null +++ b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.cpp @@ -0,0 +1,86 @@ +/* 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/>. */ + +#ifdef DEVICE + +#include <hip/hip_runtime.h> + +constexpr unsigned int NUM_BREAKPOINT_HITS = 5; + +static __device__ void +break_here () +{ +} + +extern "C" __global__ void +kernel () +{ + for (int n = 0; n < NUM_BREAKPOINT_HITS; ++n) + break_here (); +} + +#else + +#include <hip/hip_runtime.h> +#include <unistd.h> + +constexpr unsigned int NUM_ITEMS_PER_BLOCK = 256; +constexpr unsigned int NUM_BLOCKS = 128; +constexpr unsigned int NUM_ITEMS = NUM_ITEMS_PER_BLOCK * NUM_BLOCKS; +constexpr unsigned int NUM_LOAD_UNLOADS = 5; + +#define CHECK(cmd) \ + { \ + hipError_t error = cmd; \ + if (error != hipSuccess) \ + { \ + fprintf (stderr, "error: '%s'(%d) at %s:%d\n", \ + hipGetErrorString (error), error, __FILE__, __LINE__); \ + exit (EXIT_FAILURE); \ + } \ + } + +int +main (int argc, const char **argv) +{ + if (argc != 2) + { + fprintf (stderr, "Usage: %s <hip_module_path>\n", argv[0]); + return 1; + } + + const auto module_path = argv[1]; + hipModule_t module; + CHECK (hipModuleLoad (&module, module_path)); + + /* Launch the kernel. */ + hipFunction_t function; + CHECK (hipModuleGetFunction (&function, module, "kernel")); + CHECK (hipModuleLaunchKernel (function, NUM_BLOCKS, 1, 1, + NUM_ITEMS_PER_BLOCK, 1, 1, 0, nullptr, nullptr, + nullptr)); + + /* Load and unload the module many times. */ + for (int i = 0; i < NUM_LOAD_UNLOADS; ++i) + { + hipModule_t dummy_module; + CHECK (hipModuleLoad (&dummy_module, module_path)); + CHECK (hipModuleUnload (dummy_module)); + } +} + +#endif diff --git a/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.exp b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.exp new file mode 100644 index 0000000..3fe6a95 --- /dev/null +++ b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.exp @@ -0,0 +1,68 @@ +# Copyright 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 test verifies what happens when a code object list update happens at the +# same time as some wave stop events are reported. It was added following a +# performance bug fix, where forward progress requirement disabled when +# pulling events from amd-dbgapi in amd_dbgapi_target_breakpoint::check_status. +# +# The test launches a kernel that hits a breakpoint with an always false +# condition a certain number of times. Meanwhile, the host loads and unloads +# a code object in a loop, causing check_status to be called. The hope is that +# check_status, when calling process_event_queue, will pull many WAVE_STOP +# events from the kernel hitting the breakpoint. +# +# Without the appropriate fix (of disabling forward progress requirement in +# check_status), GDB would hit the newly-added assert in process_event_queue, +# which verifies that forward progress requirement is disabled. Even without +# this assert, the test would likely time out (depending on the actual timeout +# value). + +load_lib rocm.exp +standard_testfile .cpp +require allow_hipcc_tests + +# Build the host executable. +if { [build_executable "failed to prepare" \ + $testfile $srcfile {debug hip}] == -1 } { + return -1 +} + +set hipmodule_path [standard_output_file ${testfile}.co] + +# Build the kernel object file. +if { [gdb_compile $srcdir/$subdir/$srcfile \ + $hipmodule_path object \ + { debug hip additional_flags=--genco additional_flags=-DDEVICE } ] != "" } { + return -1 +} + +proc do_test { } { + with_rocm_gpu_lock { + clean_restart $::binfile + gdb_test_no_output "set args $::hipmodule_path" "set args" + + if { ![runto_main] } { + return + } + + gdb_test "with breakpoint pending on -- break break_here if 0" + gdb_continue_to_end "continue to end" "continue" 1 + } +} + +do_test diff --git a/gdb/testsuite/gdb.rocm/fork-exec-gpu-to-non-gpu.exp b/gdb/testsuite/gdb.rocm/fork-exec-gpu-to-non-gpu.exp index 7588525..22d4b75 100644 --- a/gdb/testsuite/gdb.rocm/fork-exec-gpu-to-non-gpu.exp +++ b/gdb/testsuite/gdb.rocm/fork-exec-gpu-to-non-gpu.exp @@ -21,6 +21,7 @@ load_lib rocm.exp require allow_hipcc_tests +require allow_fork_tests standard_testfile -execer.cpp -execee.cpp diff --git a/gdb/testsuite/gdb.rocm/fork-exec-non-gpu-to-gpu.exp b/gdb/testsuite/gdb.rocm/fork-exec-non-gpu-to-gpu.exp index a6bcf69..1386099 100644 --- a/gdb/testsuite/gdb.rocm/fork-exec-non-gpu-to-gpu.exp +++ b/gdb/testsuite/gdb.rocm/fork-exec-non-gpu-to-gpu.exp @@ -20,6 +20,7 @@ load_lib rocm.exp require allow_hipcc_tests +require allow_fork_tests standard_testfile -execer.cpp -execee.cpp 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.rocm/precise-memory-fork.exp b/gdb/testsuite/gdb.rocm/precise-memory-fork.exp index d326c2e..23c1ebe 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory-fork.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory-fork.exp @@ -21,6 +21,7 @@ load_lib rocm.exp require allow_hipcc_tests +require allow_fork_tests standard_testfile .c diff --git a/gdb/testsuite/gdb.rocm/precise-memory.exp b/gdb/testsuite/gdb.rocm/precise-memory.exp index fbcb451..6711d80 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory.exp @@ -59,7 +59,7 @@ proc do_test { } { return } - # Get to the begining of the GPU kernel without precise memory enabled. + # Get to the beginning of the GPU kernel without precise memory enabled. with_test_prefix "goto gpu code" { gdb_test_no_output "set amdgpu precise-memory off" gdb_breakpoint "kernel" allow-pending diff --git a/gdb/testsuite/gdb.server/build-id-seqno.exp b/gdb/testsuite/gdb.server/build-id-seqno.exp index a508a44..8475ccc 100644 --- a/gdb/testsuite/gdb.server/build-id-seqno.exp +++ b/gdb/testsuite/gdb.server/build-id-seqno.exp @@ -90,13 +90,13 @@ proc load_binfile_check_debug_is_found { debuginfo_file testname } { with_test_prefix "$testname" { with_timeout_factor 5 { # Probing for .build-id based debug files on remote - # targets uses the vFile:stat packet by default, though + # targets uses the vFile:lstat packet by default, though # there is a work around that avoids this which can be # used if GDB is connected to an older gdbserver without # 'stat' support. # # Check the work around works by disabling use of the - # vFile:stat packet. + # vFile:lstat packet. foreach_with_prefix stat_pkt {auto off} { clean_restart @@ -105,7 +105,7 @@ proc load_binfile_check_debug_is_found { debuginfo_file testname } { gdb_test_no_output "set sysroot target:" - gdb_test "set remote hostio-stat-packet $stat_pkt" + gdb_test "set remote hostio-lstat-packet $stat_pkt" # Make sure we're disconnected, in case we're testing with an # extended-remote board, therefore already connected. diff --git a/gdb/testsuite/gdb.server/fileio-packets.exp b/gdb/testsuite/gdb.server/fileio-packets.exp new file mode 100644 index 0000000..9435efd --- /dev/null +++ b/gdb/testsuite/gdb.server/fileio-packets.exp @@ -0,0 +1,66 @@ +# 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 some remote file I/O. The associated Python script uses the +# Python API to create and send vFile:* packets to gdbserver to +# perform actions like 'stat'. The same action is then performed +# directly from Python (e.g. a 'stat' is performed), and the results, +# from gdbserver, and from the local syscall, are compared. + +load_lib gdb-python.exp +load_lib gdbserver-support.exp + +require allow_python_tests +require allow_gdbserver_tests +require {!is_remote host} +require {!is_remote target} + +standard_testfile + +clean_restart + +# Make sure we're disconnected, in case we're testing with an +# extended-remote board, therefore already connected. +gdb_test "disconnect" ".*" + +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] +gdb_test_no_output "source $pyfile" "source the script" + +# Start gdbserver, but always in extended-remote mode, and then +# connect to it from GDB. +set res [gdbserver_start "--multi --once" ""] +set gdbserver_protocol "extended-remote" +set gdbserver_gdbport [lindex $res 1] +gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport + +gdb_test_no_output "set python print-stack full" + +set test_file_1 [standard_output_file "test_file_1"] +remote_exec host "touch $test_file_1" + +set test_file_2 [standard_output_file "test_file_2"] +remote_exec host "ln -s $test_file_1 $test_file_2" + +gdb_test "python check_lstat(\"$test_file_1\")" "PASS" \ + "check remote lstat works on a normal file" + +gdb_test "python check_lstat(\"$test_file_2\")" "PASS" \ + "check remote lstat works on a symbolic link" + +gdb_test "python check_stat(\"$test_file_1\")" "PASS" \ + "check remote stat works on a normal file" + +gdb_test "python check_stat(\"$test_file_2\")" "PASS" \ + "check remote stat works on a symbolic link" diff --git a/gdb/testsuite/gdb.server/fileio-packets.py b/gdb/testsuite/gdb.server/fileio-packets.py new file mode 100644 index 0000000..f132e91 --- /dev/null +++ b/gdb/testsuite/gdb.server/fileio-packets.py @@ -0,0 +1,208 @@ +# Copyright (C) 2025 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import stat + + +# Hex encode INPUT_STRING in the same way that GDB does. Each +# character in INPUT_STRING is expanded to its two digit hex +# representation in the returned string. +# +# Only ASCII characters may appear in INPUT_STRING, this is more +# restrictive than GDB, but is good enough for testing. +def hex_encode(input_string): + byte_string = input_string.encode("ascii") + hex_string = byte_string.hex() + return hex_string + + +# Binary remote data packets can contain some escaped bytes. Decode +# the packet now. +def unescape_remote_data(buf): + escaped = False + res = bytearray() + for b in buf: + if escaped: + res.append(b ^ 0x20) + escaped = False + elif b == ord("}"): + escaped = True + else: + res.append(b) + res = bytes(res) + return res + + +# Decode the results of a remote stat like command from BUF. Returns +# None if BUF is not a valid stat result (e.g. if it indicates an +# error, or the buffer is too short). If BUF is valid then the fields +# are decoded according to the GDB remote protocol and placed into a +# dictionary, this dictionary is then returned. +def decode_stat_reply(buf, byteorder="big"): + + buf = unescape_remote_data(buf) + + if ( + buf[0] != ord("F") + or buf[1] != ord("4") + or buf[2] != ord("0") + or buf[3] != ord(";") + or len(buf) != 68 + ): + l = len(buf) + print(f"decode_stat_reply failed: {buf}\t(length = {l})") + return None + + # Discard the 'F40;' prefix. The rest is the 64 bytes of data to + # be decoded. + buf = buf[4:] + + st_dev = int.from_bytes(buf[0:4], byteorder=byteorder) + st_ino = int.from_bytes(buf[4:8], byteorder=byteorder) + st_mode = int.from_bytes(buf[8:12], byteorder=byteorder) + st_nlink = int.from_bytes(buf[12:16], byteorder=byteorder) + st_uid = int.from_bytes(buf[16:20], byteorder=byteorder) + st_gid = int.from_bytes(buf[20:24], byteorder=byteorder) + st_rdev = int.from_bytes(buf[24:28], byteorder=byteorder) + st_size = int.from_bytes(buf[28:36], byteorder=byteorder) + st_blksize = int.from_bytes(buf[36:44], byteorder=byteorder) + st_blocks = int.from_bytes(buf[44:52], byteorder=byteorder) + st_atime = int.from_bytes(buf[52:56], byteorder=byteorder) + st_mtime = int.from_bytes(buf[56:60], byteorder=byteorder) + st_ctime = int.from_bytes(buf[60:64], byteorder=byteorder) + + return { + "st_dev": st_dev, + "st_ino": st_ino, + "st_mode": st_mode, + "st_nlink": st_nlink, + "st_uid": st_uid, + "st_gid": st_gid, + "st_rdev": st_rdev, + "st_size": st_size, + "st_blksize": st_blksize, + "st_blocks": st_blocks, + "st_atime": st_atime, + "st_mtime": st_mtime, + "st_ctime": st_ctime, + } + + +# Perform an lstat of remote file FILENAME, and create a dictionary of +# the results, the keys are the fields of the stat structure. +def remote_lstat(filename): + conn = gdb.selected_inferior().connection + if not isinstance(conn, gdb.RemoteTargetConnection): + raise gdb.GdbError("connection is the wrong type") + + filename_hex = hex_encode(filename) + reply = conn.send_packet("vFile:lstat:%s" % filename_hex) + + stat = decode_stat_reply(reply) + return stat + + +# Perform a stat of remote file FILENAME, and create a dictionary of +# the results, the keys are the fields of the stat structure. +def remote_stat(filename): + conn = gdb.selected_inferior().connection + if not isinstance(conn, gdb.RemoteTargetConnection): + raise gdb.GdbError("connection is the wrong type") + + filename_hex = hex_encode(filename) + reply = conn.send_packet("vFile:stat:%s" % filename_hex) + + stat = decode_stat_reply(reply) + return stat + + +# Convert a stat_result object to a dictionary that should match the +# dictionary built from the remote protocol reply. +def stat_result_to_dict(res): + # GDB doesn't support the S_IFLNK flag for the remote protocol, so + # clear that flag in the local results. + if stat.S_ISLNK(res.st_mode): + st_mode = stat.S_IMODE(res.st_mode) + else: + st_mode = res.st_mode + + # GDB returns an integer for these fields, while Python returns a + # floating point value. Convert back to an integer to match GDB. + st_atime = int(res.st_atime) + st_mtime = int(res.st_mtime) + st_ctime = int(res.st_ctime) + + return { + "st_dev": res.st_dev, + "st_ino": res.st_ino, + "st_mode": st_mode, + "st_nlink": res.st_nlink, + "st_uid": res.st_uid, + "st_gid": res.st_gid, + "st_rdev": res.st_rdev, + "st_size": res.st_size, + "st_blksize": res.st_blksize, + "st_blocks": res.st_blocks, + "st_atime": st_atime, + "st_mtime": st_mtime, + "st_ctime": st_ctime, + } + + +# Perform an lstat of local file FILENAME, and create a dictionary of +# the results, the keys are the fields of the stat structure. +def local_lstat(filename): + res = os.lstat(filename) + return stat_result_to_dict(res) + + +# Perform an lstat of local file FILENAME, and create a dictionary of +# the results, the keys are the fields of the stat structure. +def local_stat(filename): + res = os.stat(filename) + return stat_result_to_dict(res) + + +# Perform a remote lstat using GDB, and a local lstat using os.lstat. +# Compare the results to check they are the same. +# +# For this test to work correctly, gdbserver, and GDB (where this +# Python script is running), must see the same filesystem. +def check_lstat(filename): + s1 = remote_lstat(filename) + s2 = local_lstat(filename) + + print(f"remote = {s1}") + print(f"local = {s2}") + + assert s1 == s2 + print("PASS") + + +# Perform a remote stat using GDB, and a local stat using os.stat. +# Compare the results to check they are the same. +# +# For this test to work correctly, gdbserver, and GDB (where this +# Python script is running), must see the same filesystem. +def check_stat(filename): + s1 = remote_stat(filename) + s2 = local_stat(filename) + + print(f"remote = {s1}") + print(f"local = {s2}") + + assert s1 == s2 + print("PASS") diff --git a/gdb/testsuite/gdb.server/no-thread-db.exp b/gdb/testsuite/gdb.server/no-thread-db.exp index cc24708..9fd2090 100644 --- a/gdb/testsuite/gdb.server/no-thread-db.exp +++ b/gdb/testsuite/gdb.server/no-thread-db.exp @@ -57,6 +57,8 @@ gdb_breakpoint ${srcfile}:[gdb_get_line_number "after tls assignment"] gdb_continue_to_breakpoint "after tls assignment" # Printing a tls variable should fail gracefully without a libthread_db. +# Alternately, the correct answer might be printed due GDB's internal +# TLS support for some targets. set re_exec "\[^\r\n\]*[file tail $binfile]" gdb_test "print foo" \ - "Cannot find thread-local storage for Thread \[^,\]+, executable file $re_exec:\[\r\n\]+Remote target failed to process qGetTLSAddr request" + "= 1|(?:Cannot find thread-local storage for Thread \[^,\]+, executable file $re_exec:\[\r\n\]+Remote target failed to process qGetTLSAddr request)" diff --git a/gdb/testsuite/gdb.server/pread-offset-size.S b/gdb/testsuite/gdb.server/pread-offset-size.S new file mode 100644 index 0000000..6ca8cf0 --- /dev/null +++ b/gdb/testsuite/gdb.server/pread-offset-size.S @@ -0,0 +1,29 @@ +/* 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/>. */ + +/* Here we are trying to create a large binary (> 2 GB), + 3742415472 bytes is about 3.5 gigabytes. */ + + .text + .globl _start +_start: + .skip 3742415472 + ret + .globl f + .type f, @function +f: + ret diff --git a/gdb/testsuite/gdb.server/pread-offset-size.exp b/gdb/testsuite/gdb.server/pread-offset-size.exp new file mode 100644 index 0000000..54e67c5 --- /dev/null +++ b/gdb/testsuite/gdb.server/pread-offset-size.exp @@ -0,0 +1,49 @@ +# 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/>. +# +# Check that GDBserver's vFile::pread implementation is able to access +# large files (> 2GB). + +load_lib gdbserver-support.exp + +require allow_gdbserver_tests + +standard_testfile .S + +if { [prepare_for_testing ${testfile}.exp $testfile \ + $srcfile {debug additional_flags=-nostdlib} ] } { + return -1 +} + +clean_restart + +gdb_test_no_output "set remote exec-file $binfile" \ + "set remote exec-file" + +# Make sure we're disconnected, in case we're testing with an +# extended-remote board, therefore already connected. +gdb_test "disconnect" ".*" + +set res [gdbserver_spawn ""] +set gdbserver_protocol [lindex $res 0] +set gdbserver_gdbport [lindex $res 1] + +gdb_test "target $gdbserver_protocol $gdbserver_gdbport" \ + "Remote debugging using .*" \ + "target $gdbserver_protocol" + +# If loading the large binary was successful, we should be able to +# place a breakpoint on f. +gdb_test "break f" "Breakpoint 1.*" diff --git a/gdb/testsuite/gdb.stabs/weird.def b/gdb/testsuite/gdb.stabs/weird.def index 179b126..f809963 100644 --- a/gdb/testsuite/gdb.stabs/weird.def +++ b/gdb/testsuite/gdb.stabs/weird.def @@ -294,7 +294,7 @@ attr69: # Using double quotes requires an escaping, as the stabs string # is a double quote delimited string. .stabs "constString2:c=s\"Double quote String2\"", N_LSYM,0,0, 0 -# Escaping sinlge quote with is easy +# Escaping single quote with is easy .stabs "constString3:c=s'String3 with embedded quote \' in the middle'", N_LSYM,0,0, 0 # Esaping double quotes is less clear... .stabs "constString4:c=s\"String4 with embedded quote \\" in the middle\"", N_LSYM,0,0, 0 diff --git a/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.exp b/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.exp new file mode 100644 index 0000000..a05ce61 --- /dev/null +++ b/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.exp @@ -0,0 +1,84 @@ +# 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 gdb_test_multiple -lbl, particularly with patterns that share a +# common prefix. + +standard_testfile + +clean_restart + +gdb_test_no_output "source ${srcdir}/${subdir}/$testfile.gdb" \ + "source gdb test script" + +set saw_prompt 0 +set saw_prefix 0 +set saw_command 0 +set saw_prefix_foo 0 +set saw_prefix_bar 0 + +# #1 - We need anchors so that the "prefix foo" pattern below does not +# match when the expect output buffer contains: +# +# "\r\nprefix xxx\r\n\prefix foo\r\n" +# +# #2 - We need an anchor on the prompt match as otherwise the prompt +# regexp would match: +# +# "\r\nmeant-to-be-matched-by-lbl-2\r\nprefix xxx\r\n(gdb) " +# +# This test would fail if -lbl did not force the built-in prompt match +# regexp to have an anchor as well, as without it, the built-in prompt +# regexp would have the exact same issue as #2 above. + +gdb_test_multiple "command" "" -lbl { + -re "^command(?=\r\n)" { + verbose -log <COMMAND> + incr saw_command + exp_continue + } + -re "^\r\nprefix foo(?=\r\n)" { + verbose -log <PREFIX-FOO> + incr saw_prefix_foo + exp_continue + } + -re "^\r\nprefix bar(?=\r\n)" { + verbose -log <PREFIX-BAR> + incr saw_prefix_bar + exp_continue + } + -re "^\r\nprefix \[^\r\n\]*(?=\r\n)" { + verbose -log <PREFIX> + incr saw_prefix + exp_continue + } + -re "^\r\n$gdb_prompt $" { + verbose -log <PROMPT> + incr saw_prompt + pass $gdb_test_name + } +} + +verbose -log "saw_command: $saw_command" +verbose -log "saw_prefix_foo: $saw_prefix_foo" +verbose -log "saw_prefix_bar: $saw_prefix_bar" +verbose -log "saw_prefix: $saw_prefix" +verbose -log "saw_prompt: $saw_prompt" + +gdb_assert {$saw_command == 1} +gdb_assert {$saw_prefix_foo == 1} +gdb_assert {$saw_prefix_bar == 1} +gdb_assert {$saw_prefix == 3} +gdb_assert {$saw_prompt == 1} diff --git a/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.gdb b/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.gdb new file mode 100755 index 0000000..8c94dfa --- /dev/null +++ b/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.gdb @@ -0,0 +1,25 @@ +# 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/>. + +define command + echo prefix xxx\n + echo meant-to-be-matched-by-lbl-1\n + echo prefix foo\n + echo prefix bar\n + echo meant-to-be-matched-by-lbl-2\n + echo prefix xxx\n + echo prefix xxx\n + echo meant-to-be-matched-by-lbl-3\n +end diff --git a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp index b41e3b2..fec31c3 100644 --- a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp +++ b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp @@ -74,42 +74,45 @@ proc test { non_stop } { delete_breakpoints # Start the second inferior. - with_test_prefix "second inferior" { - # With stub targets that do reload on run, if we let the new - # inferior share inferior 1's connection, runto would - # fail because GDB is already connected to something, like - # e.g. with --target_board=native-gdbserver: - # - # (gdb) kill - # ... - # (gdb) target remote localhost:2348 - # Already connected to a remote target. Disconnect? (y or n) - # - # Instead, start the inferior with no connection, and let - # gdb_load/runto spawn a new remote connection/gdbserver. - # - # OTOH, with extended-remote, we must let the new inferior - # reuse the current connection, so that runto below can - # issue the "run" command, and have the inferior run on the - # remote target. If we forced no connection, then "run" would - # either fail if "set auto-connect-native-target" is on, like - # the native-extended-gdbserver board enforces, or it would - # run the inferior on the native target, which isn't what is - # being tested. - # - # Since it's reload_on_run targets that need special care, we - # default to reusing the connection on most targets. - if [target_info exists gdb,do_reload_on_run] { - gdb_test "add-inferior -no-connection" "New inferior 2.*" - } else { - gdb_test "add-inferior" "New inferior 2.*" - } - gdb_test "inferior 2" "Switching to inferior 2 .*" - - gdb_load $binfile - - if ![runto setup_done] { - return -1 + if {[allow_multi_inferior_tests]} { + with_test_prefix "second inferior" { + # With stub targets that do reload on run, if we let the + # new inferior share inferior 1's connection, runto would + # fail because GDB is already connected to something, like + # e.g. with --target_board=native-gdbserver: + # + # (gdb) kill + # ... + # (gdb) target remote localhost:2348 + # Already connected to a remote target. Disconnect? (y or n) + # + # Instead, start the inferior with no connection, and let + # gdb_load/runto spawn a new remote connection/gdbserver. + # + # OTOH, with extended-remote, we must let the new inferior + # reuse the current connection, so that runto below can + # issue the "run" command, and have the inferior run on + # the remote target. If we forced no connection, then + # "run" would either fail if "set + # auto-connect-native-target" is on, like the + # native-extended-gdbserver board enforces, or it would + # run the inferior on the native target, which isn't what + # is being tested. + # + # Since it's reload_on_run targets that need special care, + # we default to reusing the connection on most targets. + if [target_info exists gdb,do_reload_on_run] { + gdb_test "add-inferior -no-connection" "New inferior 2.*" + } else { + gdb_test "add-inferior" "New inferior 2.*" + } + gdb_test "inferior 2" "Switching to inferior 2 .*" + + gdb_load $binfile + + if ![runto setup_done] { + return -1 + } } } @@ -158,13 +161,15 @@ proc test { non_stop } { verbose -log "xxxxx: iteration $iter" gdb_test -nopass "info threads" - if {$inf == 1} { - set inf 2 - } else { - set inf 1 - } + if {[allow_multi_inferior_tests]} { + if {$inf == 1} { + set inf 2 + } else { + set inf 1 + } - my_gdb_test "inferior $inf" ".*" "inferior $inf" + my_gdb_test "inferior $inf" ".*" "inferior $inf" + } my_gdb_test "print global_var = 555" " = 555" \ "write to global_var" 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/detach-step-over.exp b/gdb/testsuite/gdb.threads/detach-step-over.exp index e48b83c..8a1cb29 100644 --- a/gdb/testsuite/gdb.threads/detach-step-over.exp +++ b/gdb/testsuite/gdb.threads/detach-step-over.exp @@ -50,6 +50,8 @@ require can_spawn_for_attach +require allow_multi_inferior_tests + standard_testfile set bp_lineno [gdb_get_line_number "Set breakpoint here"] diff --git a/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp b/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp index 5245988..8ab540c 100644 --- a/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp +++ b/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp @@ -17,6 +17,8 @@ # another thread, in different combinations of "set follow-fork # parent/child", and other execution modes. +require allow_fork_tests + standard_testfile # Line where to stop the main thread. diff --git a/gdb/testsuite/gdb.threads/fork-child-threads.exp b/gdb/testsuite/gdb.threads/fork-child-threads.exp index abe9769..ba9dfc2 100644 --- a/gdb/testsuite/gdb.threads/fork-child-threads.exp +++ b/gdb/testsuite/gdb.threads/fork-child-threads.exp @@ -13,10 +13,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/>. -# Only GNU/Linux is known to support `set follow-fork-mode child'. -if { ! [istarget "*-*-linux*"] } { - return 0 -} +require allow_fork_tests standard_testfile diff --git a/gdb/testsuite/gdb.threads/fork-plus-threads.exp b/gdb/testsuite/gdb.threads/fork-plus-threads.exp index 3a5e66a..4ce88d3 100644 --- a/gdb/testsuite/gdb.threads/fork-plus-threads.exp +++ b/gdb/testsuite/gdb.threads/fork-plus-threads.exp @@ -20,6 +20,8 @@ # # See https://sourceware.org/bugzilla/show_bug.cgi?id=18600 +require allow_fork_tests + # In remote mode, we cannot continue debugging after all # inferiors have terminated, and this test requires that. if { [target_info exists gdb_protocol] diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp index d0a1ca1..538e1ca 100644 --- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp +++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp @@ -13,11 +13,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/>. -# Only GNU/Linux is known to support `set follow-fork-mode child'. -# -if { ! [istarget "*-*-linux*"] } { - return 0 -} +require allow_fork_tests standard_testfile diff --git a/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp b/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp index 1f76898..c668a65 100644 --- a/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp +++ b/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp @@ -16,6 +16,8 @@ # This test verifies that several threads forking while another thread # is constantly stepping over a breakpoint is properly handled. +require allow_fork_tests + standard_testfile set linenum [gdb_get_line_number "set break here"] 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/next-fork-exec-other-thread.exp b/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp index bd81438..3a97127 100644 --- a/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp +++ b/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp @@ -25,6 +25,8 @@ # 20.04.5 LTS with 32-bit kernel + 32-bit userland. It was NOT reproducible # using a circa 2023 Raspberry Pi OS w/ 64-bit kernel and 32-bit userland. +require allow_fork_tests + standard_testfile # Line where to stop the main thread. diff --git a/gdb/testsuite/gdb.threads/next-fork-other-thread.exp b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp index 183fda6..1cd6685 100644 --- a/gdb/testsuite/gdb.threads/next-fork-other-thread.exp +++ b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp @@ -16,6 +16,8 @@ # Test doing a "next" on a thread during which forks or vforks happen in other # threads. +require allow_fork_tests + standard_testfile # Line where to stop the main thread. diff --git a/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp b/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp index e6e311e..29a011e 100644 --- a/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp +++ b/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp @@ -29,6 +29,8 @@ # parent thread from waitpid'ing it, preventing the main thread from joining # it, prevent it from writing the flag file, failing the test. +require allow_fork_tests + standard_testfile if { [is_remote target] } { diff --git a/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp b/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp index 8e77ab0..e627241 100644 --- a/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp +++ b/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp @@ -34,6 +34,8 @@ # event, and erroneously create a new inferior for it. Once fixed, the child # process' thread is hidden by whoever holds the pending fork event. +require allow_fork_tests + standard_testfile .c -touch-file.c set touch_file_bin $binfile-touch-file 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.threads/thread-execl.c b/gdb/testsuite/gdb.threads/thread-execl.c index 403aa31..2d312d4 100644 --- a/gdb/testsuite/gdb.threads/thread-execl.c +++ b/gdb/testsuite/gdb.threads/thread-execl.c @@ -25,8 +25,9 @@ static const char *image; void * thread_execler (void *arg) { - /* Exec ourselves again. */ - if (execl (image, image, NULL) == -1) + /* Exec ourselves again. Pass an extra argument so that the + post-exec image knows to not re-exec yet again. */ + if (execl (image, image, "1", NULL) == -1) { perror ("execl"); abort (); @@ -40,6 +41,11 @@ main (int argc, char **argv) { pthread_t thread; + /* An extra argument means we're in the post-exec image, so we're + done. Don't re-exec again. */ + if (argc > 1) + exit (0); + image = argv[0]; pthread_create (&thread, NULL, thread_execler, NULL); diff --git a/gdb/testsuite/gdb.threads/threadapply.exp b/gdb/testsuite/gdb.threads/threadapply.exp index c53db79..9110617 100644 --- a/gdb/testsuite/gdb.threads/threadapply.exp +++ b/gdb/testsuite/gdb.threads/threadapply.exp @@ -224,6 +224,8 @@ proc kill_and_remove_inferior {thread_set} { # Test both "all" and a thread list, because those are implemented as # different commands in GDB. -foreach_with_prefix thread_set {"all" "1.1"} { - kill_and_remove_inferior $thread_set +if {[allow_multi_inferior_tests]} { + foreach_with_prefix thread_set {"all" "1.1"} { + kill_and_remove_inferior $thread_set + } } diff --git a/gdb/testsuite/gdb.threads/tls.exp b/gdb/testsuite/gdb.threads/tls.exp index 1bc5df2..73fada7 100644 --- a/gdb/testsuite/gdb.threads/tls.exp +++ b/gdb/testsuite/gdb.threads/tls.exp @@ -159,7 +159,7 @@ gdb_test_multiple "print a_thread_local" "" { -re -wrap "Cannot find thread-local variables on this target" { kfail "gdb/25807" $gdb_test_name } - -re -wrap "Cannot read .a_thread_local. without registers" { + -re -wrap "Cannot (?:read|find address of TLS symbol) .a_thread_local. without registers" { pass $gdb_test_name } } diff --git a/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp b/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp index e23db0a..0b95a75 100644 --- a/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp +++ b/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp @@ -16,6 +16,8 @@ # Test following a vfork child that execs, when the vfork parent is a # threaded program, and it's a non-main thread that vforks. +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile {debug pthreads}]} { diff --git a/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp b/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp index a6b7f49..ced52df 100644 --- a/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp +++ b/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp @@ -16,6 +16,8 @@ # Test following a vfork child that exits, when the vfork parent is a # threaded program, and it's a non-main thread that vforks. +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile {debug pthreads}]} { diff --git a/gdb/testsuite/gdb.threads/vfork-multi-inferior.exp b/gdb/testsuite/gdb.threads/vfork-multi-inferior.exp index fd081b3..1f87427 100644 --- a/gdb/testsuite/gdb.threads/vfork-multi-inferior.exp +++ b/gdb/testsuite/gdb.threads/vfork-multi-inferior.exp @@ -25,6 +25,10 @@ # To catch the bug, this test verifies that we can hit a breakpoint after a # vfork call, while a second inferior runs in the background. +require allow_fork_tests + +require allow_multi_inferior_tests + require !use_gdb_stub standard_testfile .c -sleep.c diff --git a/gdb/testsuite/gdb.threads/vfork-multi-thread.exp b/gdb/testsuite/gdb.threads/vfork-multi-thread.exp index 2b9294d..61811ae 100644 --- a/gdb/testsuite/gdb.threads/vfork-multi-thread.exp +++ b/gdb/testsuite/gdb.threads/vfork-multi-thread.exp @@ -30,6 +30,8 @@ # breakpoints are removed, so the main thread would miss the breakpoint and run # until exit. +require allow_fork_tests + standard_testfile if { [build_executable "failed to prepare" ${testfile} ${srcfile} {debug pthreads}] } { diff --git a/gdb/testsuite/gdb.threads/watchpoint-fork.exp b/gdb/testsuite/gdb.threads/watchpoint-fork.exp index 376ca2a..8e9b1c3 100644 --- a/gdb/testsuite/gdb.threads/watchpoint-fork.exp +++ b/gdb/testsuite/gdb.threads/watchpoint-fork.exp @@ -21,6 +21,8 @@ # must be done before starting the test so as to not disrupt the execution # of the actual test. +require allow_fork_tests + set allow_hw_watchpoint_tests_p [allow_hw_watchpoint_tests] set testfile watchpoint-fork diff --git a/gdb/testsuite/gdb.trace/tspeed.exp b/gdb/testsuite/gdb.trace/tspeed.exp index 25862bf..be7f37e 100644 --- a/gdb/testsuite/gdb.trace/tspeed.exp +++ b/gdb/testsuite/gdb.trace/tspeed.exp @@ -17,7 +17,7 @@ load_lib "trace-support.exp" require allow_shlib_tests -# Do not run if gdbsever debug is enabled - the output file is many Gb. +# Do not run if gdbserver debug is enabled - the output file is many Gb. if [gdbserver_debug_enabled] { return 0 } 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/pr30056.exp b/gdb/testsuite/gdb.tui/pr30056.exp index 1123593..3403033 100644 --- a/gdb/testsuite/gdb.tui/pr30056.exp +++ b/gdb/testsuite/gdb.tui/pr30056.exp @@ -76,12 +76,12 @@ save_vars { env(LC_ALL) } { # open about this. kfail cli/30498 $test - # At this point we don't have a reponsive prompt. Send ^G to abort + # At this point we don't have a responsive prompt. Send ^G to abort # the i-search. send_gdb "\007" } - # We need a reponsive prompt here, to deal with the "monitor exit" + # We need a responsive prompt here, to deal with the "monitor exit" # that native-extended-gdbserver will send. Check that we have a # responsive prompt. Term::command "echo \\n" diff --git a/gdb/testsuite/gdb.tui/tui-focus.exp b/gdb/testsuite/gdb.tui/tui-focus.exp index 4c9d2a0..26e5060 100644 --- a/gdb/testsuite/gdb.tui/tui-focus.exp +++ b/gdb/testsuite/gdb.tui/tui-focus.exp @@ -73,7 +73,7 @@ foreach spec {{src true} {cmd true} {status true} {regs false} \ } } -# Use the Python TUI API to exercise some of the ambigous window name +# Use the Python TUI API to exercise some of the ambiguous window name # handling parts of the 'focus' command. Term::clean_restart 24 80 $binfile if {[allow_python_tests]} { 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/gdb.xml/bad-include.xml b/gdb/testsuite/gdb.xml/bad-include.xml index f7a2b72..cd9cce3 100644 --- a/gdb/testsuite/gdb.xml/bad-include.xml +++ b/gdb/testsuite/gdb.xml/bad-include.xml @@ -1 +1 @@ -<xi:include href="nonexistant.xml"/> +<xi:include href="nonexistent.xml"/> diff --git a/gdb/testsuite/gdb.xml/tdesc-xinclude.exp b/gdb/testsuite/gdb.xml/tdesc-xinclude.exp index 885d217..b934c80 100644 --- a/gdb/testsuite/gdb.xml/tdesc-xinclude.exp +++ b/gdb/testsuite/gdb.xml/tdesc-xinclude.exp @@ -43,7 +43,7 @@ set_arch "includes.xml" \ # This file contains a missing include. We should warn the user about # it. set_arch "bad-include.xml" \ - "warning:.*Could not load XML document \"nonexistant.xml\"$common_warn" + "warning:.*Could not load XML document \"nonexistent.xml\"$common_warn" # Make sure we detect infinite loops, eventually. set_arch "loop.xml" \ diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index 7e8778a..3a182c2 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 - @@ -1061,7 +1072,10 @@ namespace eval Dwarf { } proc _section {name {flags ""} {type ""}} { - if {$flags == "" && $type == ""} { + if {$name == ".debug_str"} { + # Hard-code this because it's always desirable. + _emit " .section $name, \"MS\", %progbits, 1" + } elseif {$flags == "" && $type == ""} { _emit " .section $name" } elseif {$type == ""} { _emit " .section $name, \"$flags\"" @@ -3385,6 +3399,58 @@ 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. + # Current options are: + # dwo 0|1 - boolean indicating if the sections have the dwo suffix. + # default = 0 (no .dwo suffix) + # base_offset label + # - generate label, to be used in DW_AT_str_offsets_base. + # default = "" (don't generate a label). + proc debug_str_offsets { options args } { + parse_options { + { dwo 0 } + { base_offset "" } + } + + if { $dwo } { + _section .debug_str.dwo + } else { + _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" + + if { $dwo } { + _section .debug_str_offsets.dwo + } else { + _section .debug_str_offsets + } + _op .4byte $initial_length "Initial_length" + debug_str_offsets_start: + _op .2byte 0x5 "version" + _op .2byte 0x0 "padding" + if { $base_offset != "" } { + $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..777d64d 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -269,6 +269,13 @@ if ![info exists INTERNAL_GDBFLAGS] { } set INTERNAL_GDBFLAGS [append_gdb_data_directory_option $INTERNAL_GDBFLAGS] + + # Handle the case that "interactive-mode auto" reports off. + append INTERNAL_GDBFLAGS { -iex "set interactive-mode on"} + + if { [ishost "*-*-mingw*"] } { + append INTERNAL_GDBFLAGS { -iex "maint set console-translation-mode binary"} + } } # The variable gdb_prompt is a regexp which matches the gdb prompt. @@ -1026,7 +1033,10 @@ proc command_to_message { command } { # should not be anchored at the end of the buffer. This means that the # pattern can match even if there is stuff output after the prompt. Does not # have any effect if -prompt is specified. -# -lbl specifies that line-by-line matching will be used. +# -lbl specifies that line-by-line matching will be used. This means +# that lines from GDB not matched by any pattern will be consumed from +# the output buffer. This helps avoid buffer overflows and timeouts +# when testing verbose commands. # EXPECT_ARGUMENTS will be fed to expect in addition to the standard # patterns. Pattern elements will be evaluated in the caller's # context; action elements will be executed in the caller's context. @@ -1124,6 +1134,7 @@ proc gdb_test_multiple { command message args } { global any_spawn_id set line_by_line 0 + set lbl_anchor_re "" set prompt_regexp "" set prompt_anchor 1 for {set i 0} {$i < [llength $args]} {incr i} { @@ -1133,6 +1144,7 @@ proc gdb_test_multiple { command message args } { set prompt_regexp [lindex $args $i] } elseif { $arg == "-lbl" } { set line_by_line 1 + set lbl_anchor_re "^" } elseif { $arg == "-no-prompt-anchor" } { set prompt_anchor 0 } else { @@ -1391,7 +1403,7 @@ proc gdb_test_multiple { command message args } { fail "$errmsg" set result -1 } - -re "\r\n$prompt_regexp" { + -re "${lbl_anchor_re}\r\n$prompt_regexp" { if {![string match "" $message]} { fail "$message" } @@ -2301,7 +2313,8 @@ proc default_gdb_exit {} { } } - if { [is_remote host] && [board_info host exists fileid] } { + if { ([is_remote host] && [board_info host exists fileid]) + || [istarget *-*-mingw*] } { send_gdb "quit\n" gdb_expect 10 { -re "y or n" { @@ -2314,7 +2327,9 @@ proc default_gdb_exit {} { } if ![is_remote host] { - remote_close host + if {[catch { remote_close host } message]} { + warning "closing gdb failed with: $message" + } } unset gdb_spawn_id unset ::gdb_tty_name @@ -2577,6 +2592,17 @@ proc default_gdb_start { } { # Output with -q, and bracketed paste mode enabled, see above. verbose "GDB initialized." } + -re "^\033\\\[6n$gdb_prompt $" { + # With MSYS2 and TERM={xterm,ansi}, I get: + # + # builtin_spawn gdb -q ... + # ^[[6n(gdb) + # + # We set TERM to dumb by default to avoid this, but some + # test-cases set TERM to xterm or ansi, in which case we get this + # output. + verbose "GDB initialized." + } -re "$gdb_prompt $" { perror "GDB never initialized." unset gdb_spawn_id @@ -5087,6 +5113,40 @@ proc skip_inline_var_tests {} { return 0 } +# Return whether we allow running fork-related testcases. Targets +# that don't even have any concept of fork will just fail to compile +# the testcases and skip the tests that way if this returns true for +# them. Unix targets that do have a fork system call, but don't +# support intercepting forks will want to return false here, otherwise +# the testcases that exercise fork may hit a number of long cascading +# time out sequences. + +proc allow_fork_tests {} { + if {[istarget "*-*-cygwin*"] || [istarget "*-*-mingw*"]} { + return 0 + } + + return 1 +} + +# Return whether we allow running testcases that want to debug +# multiple inferiors with the same target. Not all targets support +# this. Note that some tests add a second inferior but never start +# it. Those tests should not be skipped due to this proc returning +# false. + +proc allow_multi_inferior_tests {} { + if {[istarget "*-*-cygwin*"] || [istarget "*-*-mingw*"]} { + return 0 + } + + if {[use_gdb_stub]} { + return 0 + } + + return 1 +} + # Return a 1 if we should run tests that require hardware breakpoints proc allow_hw_breakpoint_tests {} { @@ -6920,7 +6980,7 @@ proc kill_wait_spawned_process { proc_spawn_id } { proc spawn_id_get_pid { spawn_id } { set testpid [exp_pid -i $spawn_id] - if { [istarget "*-*-cygwin*"] } { + if { [istarget "*-*-cygwin*"] || [istarget "*-*-mingw*"] } { # testpid is the Cygwin PID, GDB uses the Windows PID, which # might be different due to the way fork/exec works. set testpid [ exec ps -e | gawk "{ if (\$1 == $testpid) print \$4; }" ] @@ -7007,6 +7067,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 @@ -7471,6 +7549,22 @@ proc default_gdb_init { test_file_name } { setenv LC_CTYPE C setenv LANG C + # With MSYS2 and TERM={xterm,ansi}, I get: + # + # builtin_spawn gdb -q ... + # ^[[6n(gdb) + # + # While we're addressing this in default_gdb_start, this is not specific + # to gdb, other tools produce the same CSI sequence, and consequently we + # run into trouble in other places (like get_compiler_info). + # + # Set TERM to dumb to prevent the '^[[6n' from occurring. + # + # We could do this only for ishost *-*-mingw*, but that introduces + # inconsistency between platforms, with test-cases passing on one platform + # but failing on the other. So, we do this for all platforms. + setenv TERM dumb + # Don't let a .inputrc file or an existing setting of INPUTRC mess # up the test results. Certain tests (style tests and TUI tests) # want to set the terminal to a non-"dumb" value, and for those we @@ -9286,7 +9380,12 @@ proc core_find {binfile {deletefiles {}} {arg ""}} { file mkdir $coredir catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >/dev/null 2>&1\"" # remote_exec host "${binfile}" - foreach i "${coredir}/core ${coredir}/core.coremaker.c ${binfile}.core" { + set binfile_basename [file tail $binfile] + foreach i [list \ + ${coredir}/core \ + ${coredir}/core.coremaker.c \ + ${coredir}/${binfile_basename}.core \ + ${coredir}/${binfile_basename}.exe.core] { if [remote_file build exists $i] { remote_exec build "mv $i $destcore" set found 1 diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp index c285072..2389206 100644 --- a/gdb/testsuite/lib/gdbserver-support.exp +++ b/gdb/testsuite/lib/gdbserver-support.exp @@ -69,7 +69,7 @@ proc gdb_target_cmd_ext { targetname serialport {additional_text ""} } { } -re "Non-stop mode requested, but remote does not support non-stop.*$gdb_prompt $" { verbose "remote does not support non-stop" - return 1 + return 2 } -re "Remote MIPS debugging.*$additional_text.*$gdb_prompt" { verbose "Set target to $targetname" diff --git a/gdb/testsuite/make-check-all.sh b/gdb/testsuite/make-check-all.sh index c2fbadb..ab72574 100755 --- a/gdb/testsuite/make-check-all.sh +++ b/gdb/testsuite/make-check-all.sh @@ -192,7 +192,7 @@ do_tests () # Run make check. make $maketarget \ - RUNTESTFLAGS="${rtf[*]} ${tests[*]}" \ + RUNTESTFLAGS="${rtf[*]}" TESTS="${tests[*]}" \ 2>&1 \ | summary @@ -216,7 +216,7 @@ do_tests () cp gdb.sum gdb.log "$dir" # Record the 'make check' command to enable easy re-running. - echo "make $maketarget RUNTESTFLAGS=\"${rtf[*]} ${tests[*]}\"" \ + echo "make $maketarget RUNTESTFLAGS=\"${rtf[*]}\" TESTS=\"${tests[*]}\"" \ > "$dir/make-check.sh" fi } diff --git a/gdb/thread.c b/gdb/thread.c index b659463..920d8dc 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. */ @@ -2287,9 +2311,7 @@ static const struct internalvar_funcs inferior_thread_count_funcs = NULL, }; -void _initialize_thread (); -void -_initialize_thread () +INIT_GDB_FILE (thread) { static struct cmd_list_element *thread_apply_list = NULL; cmd_list_element *c; diff --git a/gdb/tic6x-linux-tdep.c b/gdb/tic6x-linux-tdep.c index 280d46d..5b3d402 100644 --- a/gdb/tic6x-linux-tdep.c +++ b/gdb/tic6x-linux-tdep.c @@ -169,7 +169,7 @@ tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) linux_init_abi (info, gdbarch, 0); /* Shared library handling. */ - set_gdbarch_so_ops (gdbarch, &dsbt_so_ops); + set_gdbarch_make_solib_ops (gdbarch, make_dsbt_solib_ops); tdep->syscall_next_pc = tic6x_linux_syscall_next_pc; @@ -203,9 +203,7 @@ tic6x_uclinux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) &tic6x_linux_rt_sigreturn_tramp_frame); } -void _initialize_tic6x_linux_tdep (); -void -_initialize_tic6x_linux_tdep () +INIT_GDB_FILE (tic6x_linux_tdep) { gdbarch_register_osabi (bfd_arch_tic6x, 0, GDB_OSABI_LINUX, tic6x_uclinux_init_abi); diff --git a/gdb/tic6x-tdep.c b/gdb/tic6x-tdep.c index 062a974..2ab8dac 100644 --- a/gdb/tic6x-tdep.c +++ b/gdb/tic6x-tdep.c @@ -1293,9 +1293,7 @@ tic6x_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_tic6x_tdep (); -void -_initialize_tic6x_tdep () +INIT_GDB_FILE (tic6x_tdep) { gdbarch_register (bfd_arch_tic6x, tic6x_gdbarch_init); } diff --git a/gdb/tilegx-linux-nat.c b/gdb/tilegx-linux-nat.c index bd077b2..99f2cc4 100644 --- a/gdb/tilegx-linux-nat.c +++ b/gdb/tilegx-linux-nat.c @@ -163,9 +163,7 @@ tilegx_linux_nat_target::store_registers (struct regcache *regcache, perror_with_name (_("Couldn't write registers")); } -void _initialize_tile_linux_nat (); -void -_initialize_tile_linux_nat () +INIT_GDB_FILE (tile_linux_nat) { linux_target = &the_tilegx_linux_nat_target; add_inf_child_target (&the_tilegx_linux_nat_target); diff --git a/gdb/tilegx-linux-tdep.c b/gdb/tilegx-linux-tdep.c index a0e6954..f54e280 100644 --- a/gdb/tilegx-linux-tdep.c +++ b/gdb/tilegx-linux-tdep.c @@ -19,6 +19,7 @@ #include "osabi.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "glibc-tdep.h" #include "solib-svr4.h" #include "symtab.h" @@ -119,11 +120,9 @@ tilegx_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* GNU/Linux uses SVR4-style shared libraries. */ if (arch_size == 32) - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); else - set_solib_svr4_fetch_link_map_offsets (gdbarch, - linux_lp64_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_lp64_svr4_solib_ops); /* Enable TLS support. */ set_gdbarch_fetch_tls_load_module_address (gdbarch, @@ -134,9 +133,7 @@ tilegx_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver); } -void _initialize_tilegx_linux_tdep (); -void -_initialize_tilegx_linux_tdep () +INIT_GDB_FILE (tilegx_linux_tdep) { gdbarch_register_osabi (bfd_arch_tilegx, bfd_mach_tilegx, GDB_OSABI_LINUX, tilegx_linux_init_abi); diff --git a/gdb/tilegx-tdep.c b/gdb/tilegx-tdep.c index 4922fff..a153f65 100644 --- a/gdb/tilegx-tdep.c +++ b/gdb/tilegx-tdep.c @@ -1021,9 +1021,7 @@ tilegx_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_tilegx_tdep (); -void -_initialize_tilegx_tdep () +INIT_GDB_FILE (tilegx_tdep) { gdbarch_register (bfd_arch_tilegx, tilegx_gdbarch_init); } @@ -415,7 +415,7 @@ wait_sync_command_done (void) point. */ scoped_enable_commit_resumed enable ("sync wait"); - while (gdb_do_one_event () >= 0) + while (current_interpreter ()->do_one_event () >= 0) if (ui->prompt_state != PROMPT_BLOCKED) break; } @@ -1031,7 +1031,7 @@ gdb_readline_wrapper (const char *prompt) (*after_char_processing_hook) (); gdb_assert (after_char_processing_hook == NULL); - while (gdb_do_one_event () >= 0) + while (current_interpreter ()->do_one_event () >= 0) if (gdb_readline_wrapper_done) break; @@ -2347,9 +2347,7 @@ gdb_init () init_colorsupport_var (); } -void _initialize_top (); -void -_initialize_top () +INIT_GDB_FILE (top) { /* Determine a default value for the history filename. */ const char *tmpenv = getenv ("GDBHISTFILE"); diff --git a/gdb/tracectf.c b/gdb/tracectf.c index 1650e67..0f80d08 100644 --- a/gdb/tracectf.c +++ b/gdb/tracectf.c @@ -1174,7 +1174,7 @@ ctf_target_open (const char *args, int from_tty) merge_uploaded_trace_state_variables (&uploaded_tsvs); merge_uploaded_tracepoints (&uploaded_tps); - post_create_inferior (from_tty); + post_create_inferior (from_tty, true); } /* This is the implementation of target_ops method to_close. Destroy @@ -1717,9 +1717,7 @@ ctf_target::traceframe_info () /* module initialization */ -void _initialize_ctf (); -void -_initialize_ctf () +INIT_GDB_FILE (ctf) { #if HAVE_LIBBABELTRACE add_target (ctf_target_info, ctf_target_open, diff --git a/gdb/tracefile-tfile.c b/gdb/tracefile-tfile.c index c2b5e3b..a45001c 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))); @@ -567,7 +567,7 @@ tfile_target_open (const char *arg, int from_tty) merge_uploaded_tracepoints (&uploaded_tps); - post_create_inferior (from_tty); + post_create_inferior (from_tty, true); } /* Interpret the given line from the definitions part of the trace @@ -1116,9 +1116,7 @@ tfile_append_tdesc_line (const char *line) trace_tdesc += "\n"; } -void _initialize_tracefile_tfile (); -void -_initialize_tracefile_tfile () +INIT_GDB_FILE (tracefile_tfile) { add_target (tfile_target_info, tfile_target_open, filename_maybe_quoted_completer); diff --git a/gdb/tracefile.c b/gdb/tracefile.c index dac2eb7..19b4d1e 100644 --- a/gdb/tracefile.c +++ b/gdb/tracefile.c @@ -473,9 +473,7 @@ tracefile_target::get_trace_status (struct trace_status *ts) return -1; } -void _initialize_tracefile (); -void -_initialize_tracefile () +INIT_GDB_FILE (tracefile) { add_com ("tsave", class_trace, tsave_command, _("\ Save the trace data to a file.\n\ diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index a2f1a75..c0f1eae 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")); @@ -3919,9 +3919,7 @@ static const struct internalvar_funcs sdata_funcs = cmd_list_element *while_stepping_cmd_element = nullptr; /* module initialization */ -void _initialize_tracepoint (); -void -_initialize_tracepoint () +INIT_GDB_FILE (tracepoint) { struct cmd_list_element *c; diff --git a/gdb/tui/tui-disasm.c b/gdb/tui/tui-disasm.c index 5488760..627f71c 100644 --- a/gdb/tui/tui-disasm.c +++ b/gdb/tui/tui-disasm.c @@ -546,9 +546,7 @@ run_tests () } /* namespace selftests */ #endif /* GDB_SELF_TEST */ -void _initialize_tui_disasm (); -void -_initialize_tui_disasm () +INIT_GDB_FILE (tui_disasm) { #if GDB_SELF_TEST selftests::register_test ("tui-disasm", selftests::tui::disasm::run_tests); diff --git a/gdb/tui/tui-hooks.c b/gdb/tui/tui-hooks.c index 814ddac..024fedd 100644 --- a/gdb/tui/tui-hooks.c +++ b/gdb/tui/tui-hooks.c @@ -262,9 +262,7 @@ tui_remove_hooks (void) tui_attach_detach_observers (false); } -void _initialize_tui_hooks (); -void -_initialize_tui_hooks () +INIT_GDB_FILE (tui_hooks) { /* Install the permanent hooks. */ gdb::observers::new_objfile.attach (tui_new_objfile_hook, "tui-hooks"); diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c index 53acf82..3d0e152 100644 --- a/gdb/tui/tui-interp.c +++ b/gdb/tui/tui-interp.c @@ -161,9 +161,7 @@ tui_interp_factory (const char *name) return new tui_interp (name); } -void _initialize_tui_interp (); -void -_initialize_tui_interp () +INIT_GDB_FILE (tui_interp) { interp_factory_register (INTERP_TUI, tui_interp_factory); diff --git a/gdb/tui/tui-layout.c b/gdb/tui/tui-layout.c index 73ba0c6..558055d 100644 --- a/gdb/tui/tui-layout.c +++ b/gdb/tui/tui-layout.c @@ -1294,9 +1294,7 @@ tui_new_layout_command (const char *spec, int from_tty) /* Function to initialize gdb commands, for tui window layout manipulation. */ -void _initialize_tui_layout (); -void -_initialize_tui_layout () +INIT_GDB_FILE (tui_layout) { struct cmd_list_element *layout_cmd = add_basic_prefix_cmd ("layout", class_tui, _("\ diff --git a/gdb/tui/tui-regs.c b/gdb/tui/tui-regs.c index 0b8cb85..ac2757a 100644 --- a/gdb/tui/tui-regs.c +++ b/gdb/tui/tui-regs.c @@ -566,9 +566,7 @@ tui_reggroup_completer (struct cmd_list_element *ignore, complete_on_enum (tracker, extra, text, word); } -void _initialize_tui_regs (); -void -_initialize_tui_regs () +INIT_GDB_FILE (tui_regs) { struct cmd_list_element **tuicmd, *cmd; diff --git a/gdb/tui/tui-status.c b/gdb/tui/tui-status.c index 484f4e5..c2d3873 100644 --- a/gdb/tui/tui-status.c +++ b/gdb/tui/tui-status.c @@ -325,9 +325,7 @@ tui_update_command (const char *arg, int from_tty) /* Function to initialize gdb commands, for tui window stack manipulation. */ -void _initialize_tui_stack (); -void -_initialize_tui_stack () +INIT_GDB_FILE (tui_stack) { add_com ("update", class_tui, tui_update_command, _("\ diff --git a/gdb/tui/tui-win.c b/gdb/tui/tui-win.c index f44600d..d4fbbf1 100644 --- a/gdb/tui/tui-win.c +++ b/gdb/tui/tui-win.c @@ -1091,9 +1091,7 @@ bool tui_left_margin_verbose = false; /* Function to initialize gdb commands, for tui window manipulation. */ -void _initialize_tui_win (); -void -_initialize_tui_win () +INIT_GDB_FILE (tui_win) { static struct cmd_list_element *tui_setlist; static struct cmd_list_element *tui_showlist; diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c index 2c7a6a0..5883d6c 100644 --- a/gdb/tui/tui.c +++ b/gdb/tui/tui.c @@ -618,9 +618,7 @@ tui_get_command_dimension (unsigned int *width, return true; } -void _initialize_tui (); -void -_initialize_tui () +INIT_GDB_FILE (tui) { struct cmd_list_element **tuicmd; diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 4bc5947..ef0c5aa 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -735,9 +735,7 @@ Display of struct members offsets and sizes in hexadecimal is %s\n"), value); } -void _initialize_typeprint (); -void -_initialize_typeprint () +INIT_GDB_FILE (typeprint) { struct cmd_list_element *c; @@ -237,9 +237,7 @@ new_ui_command (const char *args, int from_tty) gdb_printf ("New UI allocated\n"); } -void _initialize_ui (); -void -_initialize_ui () +INIT_GDB_FILE (ui) { cmd_list_element *c = add_cmd ("new-ui", class_support, new_ui_command, _("\ Create a new UI.\n\ diff --git a/gdb/unittests/array-view-selftests.c b/gdb/unittests/array-view-selftests.c index fa96d3b..914800a 100644 --- a/gdb/unittests/array-view-selftests.c +++ b/gdb/unittests/array-view-selftests.c @@ -695,9 +695,7 @@ run_copy_tests () } /* namespace array_view_tests */ } /* namespace selftests */ -void _initialize_array_view_selftests (); -void -_initialize_array_view_selftests () +INIT_GDB_FILE (array_view_selftests) { selftests::register_test ("array_view", selftests::array_view_tests::run_tests); diff --git a/gdb/unittests/child-path-selftests.c b/gdb/unittests/child-path-selftests.c index 0f29f7d..95b2c34 100644 --- a/gdb/unittests/child-path-selftests.c +++ b/gdb/unittests/child-path-selftests.c @@ -58,9 +58,7 @@ test () } } -void _initialize_child_path_selftests (); -void -_initialize_child_path_selftests () +INIT_GDB_FILE (child_path_selftests) { selftests::register_test ("child_path", selftests::child_path::test); diff --git a/gdb/unittests/cli-utils-selftests.c b/gdb/unittests/cli-utils-selftests.c index e6c64a9..1382ca6 100644 --- a/gdb/unittests/cli-utils-selftests.c +++ b/gdb/unittests/cli-utils-selftests.c @@ -109,9 +109,7 @@ test_cli_utils () } } -void _initialize_cli_utils_selftests (); -void -_initialize_cli_utils_selftests () +INIT_GDB_FILE (cli_utils_selftests) { selftests::register_test ("cli_utils", selftests::cli_utils::test_cli_utils); diff --git a/gdb/unittests/command-def-selftests.c b/gdb/unittests/command-def-selftests.c index 6f169f5..0a54d31 100644 --- a/gdb/unittests/command-def-selftests.c +++ b/gdb/unittests/command-def-selftests.c @@ -219,9 +219,7 @@ command_structure_invariants_tests () } /* namespace selftests */ -void _initialize_command_def_selftests (); -void -_initialize_command_def_selftests () +INIT_GDB_FILE (command_def_selftests) { selftests::register_test ("help_doc_invariants", diff --git a/gdb/unittests/common-utils-selftests.c b/gdb/unittests/common-utils-selftests.c index fab9cd1..ecc4769 100644 --- a/gdb/unittests/common-utils-selftests.c +++ b/gdb/unittests/common-utils-selftests.c @@ -127,9 +127,7 @@ string_vappendf_tests () } /* namespace selftests */ -void _initialize_common_utils_selftests (); -void -_initialize_common_utils_selftests () +INIT_GDB_FILE (common_utils_selftests) { selftests::register_test ("string_printf", selftests::string_printf_tests); selftests::register_test ("string_vprintf", selftests::string_vprintf_tests); diff --git a/gdb/unittests/copy_bitwise-selftests.c b/gdb/unittests/copy_bitwise-selftests.c index 8798cbd..6fcdcd7 100644 --- a/gdb/unittests/copy_bitwise-selftests.c +++ b/gdb/unittests/copy_bitwise-selftests.c @@ -151,9 +151,7 @@ copy_bitwise_tests (void) } /* namespace selftests */ -void _initialize_copy_bitwise_utils_selftests (); -void -_initialize_copy_bitwise_utils_selftests () +INIT_GDB_FILE (copy_bitwise_utils_selftests) { selftests::register_test ("copy_bitwise", selftests::copy_bitwise_tests); } diff --git a/gdb/unittests/enum-flags-selftests.c b/gdb/unittests/enum-flags-selftests.c index bb55019..3eb0f52 100644 --- a/gdb/unittests/enum-flags-selftests.c +++ b/gdb/unittests/enum-flags-selftests.c @@ -607,10 +607,7 @@ self_test () } /* namespace enum_flags_tests */ } /* namespace selftests */ -void _initialize_enum_flags_selftests (); - -void -_initialize_enum_flags_selftests () +INIT_GDB_FILE (enum_flags_selftests) { selftests::register_test ("enum-flags", selftests::enum_flags_tests::self_test); diff --git a/gdb/unittests/environ-selftests.c b/gdb/unittests/environ-selftests.c index 79bffed..5ac3384 100644 --- a/gdb/unittests/environ-selftests.c +++ b/gdb/unittests/environ-selftests.c @@ -297,9 +297,7 @@ run_tests () } /* namespace gdb_environ */ } /* namespace selftests */ -void _initialize_environ_selftests (); -void -_initialize_environ_selftests () +INIT_GDB_FILE (environ_selftests) { selftests::register_test ("gdb_environ", selftests::gdb_environ_tests::run_tests); diff --git a/gdb/unittests/filtered_iterator-selftests.c b/gdb/unittests/filtered_iterator-selftests.c index b3ec70b..49c95cb 100644 --- a/gdb/unittests/filtered_iterator-selftests.c +++ b/gdb/unittests/filtered_iterator-selftests.c @@ -154,9 +154,7 @@ test_filtered_iterator_eq () } /* namespace selftests */ -void _initialize_filtered_iterator_selftests (); -void -_initialize_filtered_iterator_selftests () +INIT_GDB_FILE (filtered_iterator_selftests) { selftests::register_test ("filtered_iterator", selftests::test_filtered_iterator); diff --git a/gdb/unittests/format_pieces-selftests.c b/gdb/unittests/format_pieces-selftests.c index 473d690..c7d8ff0 100644 --- a/gdb/unittests/format_pieces-selftests.c +++ b/gdb/unittests/format_pieces-selftests.c @@ -147,9 +147,7 @@ run_tests () } /* namespace format_pieces */ } /* namespace selftests */ -void _initialize_format_pieces_selftests (); -void -_initialize_format_pieces_selftests () +INIT_GDB_FILE (format_pieces_selftests) { selftests::register_test ("format_pieces", selftests::format_pieces::run_tests); diff --git a/gdb/unittests/frame_info_ptr-selftests.c b/gdb/unittests/frame_info_ptr-selftests.c index c9eb4f4..d518482 100644 --- a/gdb/unittests/frame_info_ptr-selftests.c +++ b/gdb/unittests/frame_info_ptr-selftests.c @@ -66,9 +66,7 @@ test_user_created_frame () } /* namespace selftests */ -void _initialize_frame_info_ptr_selftests (); -void -_initialize_frame_info_ptr_selftests () +INIT_GDB_FILE (frame_info_ptr_selftests) { selftests::register_test ("frame_info_ptr_user", selftests::test_user_created_frame); diff --git a/gdb/unittests/function-view-selftests.c b/gdb/unittests/function-view-selftests.c index 036c7f9..21838f1 100644 --- a/gdb/unittests/function-view-selftests.c +++ b/gdb/unittests/function-view-selftests.c @@ -250,9 +250,7 @@ run_tests () } /* namespace function_view */ } /* namespace selftests */ -void _initialize_function_view_selftests (); -void -_initialize_function_view_selftests () +INIT_GDB_FILE (function_view_selftests) { selftests::register_test ("function_view", selftests::function_view::run_tests); diff --git a/gdb/unittests/gdb_tilde_expand-selftests.c b/gdb/unittests/gdb_tilde_expand-selftests.c index b0b7027..28c759f 100644 --- a/gdb/unittests/gdb_tilde_expand-selftests.c +++ b/gdb/unittests/gdb_tilde_expand-selftests.c @@ -84,9 +84,7 @@ do_test () } /* namespace gdb_tilde_expand_tests */ } /* namespace selftests */ -void _initialize_gdb_tilde_expand_selftests (); -void -_initialize_gdb_tilde_expand_selftests () +INIT_GDB_FILE (gdb_tilde_expand_selftests) { selftests::register_test ("gdb_tilde_expand", selftests::gdb_tilde_expand_tests::do_test); diff --git a/gdb/unittests/gmp-utils-selftests.c b/gdb/unittests/gmp-utils-selftests.c index 495947af..9843fcb 100644 --- a/gdb/unittests/gmp-utils-selftests.c +++ b/gdb/unittests/gmp-utils-selftests.c @@ -498,10 +498,7 @@ gdb_mpq_write_fixed_point () } -void _initialize_gmp_utils_selftests (); - -void -_initialize_gmp_utils_selftests () +INIT_GDB_FILE (gmp_utils_selftests) { selftests::register_test ("gdb_mpz_as_integer", selftests::gdb_mpz_as_integer); diff --git a/gdb/unittests/intrusive_list-selftests.c b/gdb/unittests/intrusive_list-selftests.c index eaffbba..2dd1298 100644 --- a/gdb/unittests/intrusive_list-selftests.c +++ b/gdb/unittests/intrusive_list-selftests.c @@ -1648,10 +1648,7 @@ test_intrusive_list () test_node_is_linked (); } -void _initialize_intrusive_list_selftests (); - -void -_initialize_intrusive_list_selftests () +INIT_GDB_FILE (intrusive_list_selftests) { selftests::register_test ("intrusive_list", test_intrusive_list); } diff --git a/gdb/unittests/lookup_name_info-selftests.c b/gdb/unittests/lookup_name_info-selftests.c index 3e8440b..dc0d307 100644 --- a/gdb/unittests/lookup_name_info-selftests.c +++ b/gdb/unittests/lookup_name_info-selftests.c @@ -105,9 +105,7 @@ run_tests () }} /* namespace selftests::lookup_name */ -void _initialize_lookup_name_info_selftests (); -void -_initialize_lookup_name_info_selftests () +INIT_GDB_FILE (lookup_name_info_selftests) { selftests::register_test ("lookup_name_info", selftests::lookup_name::run_tests); diff --git a/gdb/unittests/main-thread-selftests.c b/gdb/unittests/main-thread-selftests.c index 5f3d790..77adf47 100644 --- a/gdb/unittests/main-thread-selftests.c +++ b/gdb/unittests/main-thread-selftests.c @@ -73,9 +73,7 @@ run_tests () } } -void _initialize_main_thread_selftests (); -void -_initialize_main_thread_selftests () +INIT_GDB_FILE (main_thread_selftests) { #if CXX_STD_THREAD selftests::register_test ("run_on_main_thread", diff --git a/gdb/unittests/memory-map-selftests.c b/gdb/unittests/memory-map-selftests.c index 6872d5a..c014c4d 100644 --- a/gdb/unittests/memory-map-selftests.c +++ b/gdb/unittests/memory-map-selftests.c @@ -75,9 +75,7 @@ parse_memory_map_tests () #endif /* HAVE_LIBEXPAT */ -void _initialize_memory_map_selftests (); -void -_initialize_memory_map_selftests () +INIT_GDB_FILE (memory_map_selftests) { #if defined(HAVE_LIBEXPAT) selftests::register_test diff --git a/gdb/unittests/memrange-selftests.c b/gdb/unittests/memrange-selftests.c index 1433105..17e08e8 100644 --- a/gdb/unittests/memrange-selftests.c +++ b/gdb/unittests/memrange-selftests.c @@ -105,9 +105,7 @@ normalize_mem_ranges_tests () } /* namespace memrange_tests */ } /* namespace selftests */ -void _initialize_memrange_selftests (); -void -_initialize_memrange_selftests () +INIT_GDB_FILE (memrange_selftests) { selftests::register_test ("normalize_mem_ranges", diff --git a/gdb/unittests/mkdir-recursive-selftests.c b/gdb/unittests/mkdir-recursive-selftests.c index 764fe1a..b2ae383 100644 --- a/gdb/unittests/mkdir-recursive-selftests.c +++ b/gdb/unittests/mkdir-recursive-selftests.c @@ -80,9 +80,7 @@ test () } } -void _initialize_mkdir_recursive_selftests (); -void -_initialize_mkdir_recursive_selftests () +INIT_GDB_FILE (mkdir_recursive_selftests) { selftests::register_test ("mkdir_recursive", selftests::mkdir_recursive::test); diff --git a/gdb/unittests/observable-selftests.c b/gdb/unittests/observable-selftests.c index 90d7392..0934249 100644 --- a/gdb/unittests/observable-selftests.c +++ b/gdb/unittests/observable-selftests.c @@ -237,9 +237,7 @@ run_tests () } /* namespace observers */ } /* namespace selftests */ -void _initialize_observer_selftest (); -void -_initialize_observer_selftest () +INIT_GDB_FILE (observer_selftest) { selftests::register_test ("gdb::observers", selftests::observers::run_tests); diff --git a/gdb/unittests/offset-type-selftests.c b/gdb/unittests/offset-type-selftests.c index 1623f0d..2cba4e1 100644 --- a/gdb/unittests/offset-type-selftests.c +++ b/gdb/unittests/offset-type-selftests.c @@ -170,9 +170,7 @@ run_tests () } /* namespace offset_type */ } /* namespace selftests */ -void _initialize_offset_type_selftests (); -void -_initialize_offset_type_selftests () +INIT_GDB_FILE (offset_type_selftests) { selftests::register_test ("offset_type", selftests::offset_type::run_tests); } diff --git a/gdb/unittests/packed-selftests.c b/gdb/unittests/packed-selftests.c index daa8036..5a91b29 100644 --- a/gdb/unittests/packed-selftests.c +++ b/gdb/unittests/packed-selftests.c @@ -119,9 +119,7 @@ run_tests () } /* namespace packed_tests */ } /* namespace selftests */ -void _initialize_packed_selftests (); -void -_initialize_packed_selftests () +INIT_GDB_FILE (packed_selftests) { selftests::register_test ("packed", selftests::packed_tests::run_tests); } diff --git a/gdb/unittests/parallel-for-selftests.c b/gdb/unittests/parallel-for-selftests.c index 841d914..f545614 100644 --- a/gdb/unittests/parallel-for-selftests.c +++ b/gdb/unittests/parallel-for-selftests.c @@ -17,13 +17,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* This file is divided in two parts: - - FOR_EACH-undefined, and - - FOR_EACH-defined. - The former includes the latter, more than once, with different values for - FOR_EACH. The FOR_EACH-defined part reads like a regular function. */ -#ifndef FOR_EACH - #include "gdbsupport/selftest.h" #include "gdbsupport/parallel-for.h" @@ -49,97 +42,77 @@ struct save_restore_n_threads int n_threads; }; -/* Define test_par using TEST in the FOR_EACH-defined part. */ -#define TEST test_par -#define FOR_EACH gdb::parallel_for_each -#include "parallel-for-selftests.c" -#undef FOR_EACH -#undef TEST - -/* Define test_seq using TEST in the FOR_EACH-defined part. */ -#define TEST test_seq -#define FOR_EACH gdb::sequential_for_each -#include "parallel-for-selftests.c" -#undef FOR_EACH -#undef TEST +using foreach_callback_t = gdb::function_view<void (int first, int last)>; +using do_foreach_t = gdb::function_view<void (int first, int last, + foreach_callback_t)>; static void -test (int n_threads) +test_one (int n_threads, do_foreach_t do_foreach) { - test_par (n_threads); - test_seq (n_threads); + save_restore_n_threads saver; + gdb::thread_pool::g_thread_pool->set_thread_count (n_threads); + + { + constexpr int upper_bound = 1000; + std::atomic<int> counter (0); + do_foreach (0, upper_bound, + [&] (int start, int end) { counter += end - start; }); + SELF_CHECK (counter == upper_bound); + } + + { + std::atomic<int> counter (0); + do_foreach (0, 0, [&] (int start, int end) { counter += end - start; }); + SELF_CHECK (counter == 0); + } + + { + /* Check that if there are fewer tasks than threads, then we won't + end up with a null result. */ + std::vector<std::unique_ptr<int>> intresults; + std::atomic<bool> any_empty_tasks (false); + + do_foreach (0, 1, + [&] (int start, int end) + { + if (start == end) + any_empty_tasks = true; + + return std::make_unique<int> (end - start); + }); + + SELF_CHECK (!any_empty_tasks); + SELF_CHECK (std::all_of (intresults.begin (), intresults.end (), + [] (const std::unique_ptr<int> &entry) + { return entry != nullptr; })); + } } static void -test_n_threads () +test_parallel_for_each () { - test (0); - test (1); - test (3); + const std::vector<do_foreach_t> for_each_functions + { + [] (int start, int end, foreach_callback_t callback) + { gdb::parallel_for_each<1> (start, end, callback); }, + [] (int start, int end, foreach_callback_t callback) + { gdb::sequential_for_each (start, end, callback);} + }; + + for (int n_threads : { 0, 1, 3 }) + for (const auto &for_each_function : for_each_functions) + test_one (n_threads, for_each_function); } -} -} +} /* namespace parallel_for */ +} /* namespace selftests */ #endif /* CXX_STD_THREAD */ -void _initialize_parallel_for_selftests (); -void -_initialize_parallel_for_selftests () +INIT_GDB_FILE (parallel_for_selftests) { #ifdef CXX_STD_THREAD selftests::register_test ("parallel_for", - selftests::parallel_for::test_n_threads); + selftests::parallel_for::test_parallel_for_each); #endif /* CXX_STD_THREAD */ } - -#else /* FOR_EACH */ - -static void -TEST (int n_threads) -{ - save_restore_n_threads saver; - gdb::thread_pool::g_thread_pool->set_thread_count (n_threads); - -#define NUMBER 10000 - - std::atomic<int> counter (0); - FOR_EACH (1, 0, NUMBER, - [&] (int start, int end) - { - counter += end - start; - }); - SELF_CHECK (counter == NUMBER); - - counter = 0; - FOR_EACH (1, 0, 0, - [&] (int start, int end) - { - counter += end - start; - }); - SELF_CHECK (counter == 0); - -#undef NUMBER - - /* Check that if there are fewer tasks than threads, then we won't - end up with a null result. */ - std::vector<std::unique_ptr<int>> intresults; - std::atomic<bool> any_empty_tasks (false); - - FOR_EACH (1, 0, 1, - [&] (int start, int end) - { - if (start == end) - any_empty_tasks = true; - return std::make_unique<int> (end - start); - }); - SELF_CHECK (!any_empty_tasks); - SELF_CHECK (std::all_of (intresults.begin (), - intresults.end (), - [] (const std::unique_ptr<int> &entry) - { - return entry != nullptr; - })); -} - -#endif /* FOR_EACH */ diff --git a/gdb/unittests/parse-connection-spec-selftests.c b/gdb/unittests/parse-connection-spec-selftests.c index 7104272..ce2da35 100644 --- a/gdb/unittests/parse-connection-spec-selftests.c +++ b/gdb/unittests/parse-connection-spec-selftests.c @@ -238,9 +238,7 @@ run_tests () } /* namespace parse_connection_spec_tests */ } /* namespace selftests */ -void _initialize_parse_connection_spec_selftests (); -void -_initialize_parse_connection_spec_selftests () +INIT_GDB_FILE (parse_connection_spec_selftests) { selftests::register_test ("parse_connection_spec", selftests::parse_connection_spec_tests::run_tests); diff --git a/gdb/unittests/path-join-selftests.c b/gdb/unittests/path-join-selftests.c index a4003f0..8bc5a91 100644 --- a/gdb/unittests/path-join-selftests.c +++ b/gdb/unittests/path-join-selftests.c @@ -63,9 +63,7 @@ test () } } -void _initialize_path_join_selftests (); -void -_initialize_path_join_selftests () +INIT_GDB_FILE (path_join_selftests) { selftests::register_test ("path_join", selftests::path_join::test); diff --git a/gdb/unittests/remote-arg-selftests.c b/gdb/unittests/remote-arg-selftests.c new file mode 100644 index 0000000..17b8d1d --- /dev/null +++ b/gdb/unittests/remote-arg-selftests.c @@ -0,0 +1,163 @@ +/* Self tests for GDB's argument splitting and merging. + + Copyright (C) 2023-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 "defs.h" +#include "gdbsupport/selftest.h" +#include "gdbsupport/buildargv.h" +#include "gdbsupport/common-inferior.h" +#include "gdbsupport/remote-args.h" +#include "gdbsupport/gdb_argv_vec.h" + +namespace selftests { +namespace remote_args_tests { + +/* The data needed to perform a single remote argument test. */ +struct arg_test_desc +{ + /* The original inferior argument string. */ + std::string input; + + /* The individual arguments once they have been split. */ + std::vector<std::string> split; + + /* The new inferior argument string, created by joining SPLIT. */ + std::string joined; +}; + +/* The list of tests. */ +arg_test_desc desc[] = { + { "abc", { "abc" }, "abc" }, + { "a b c", { "a", "b", "c" }, "a b c" }, + { "\"a b\" 'c d'", { "a b", "c d" }, "a\\ b c\\ d" }, + { "\\' \\\"", { "'", "\"" }, "\\' \\\"" }, + { "'\\'", { "\\" }, "\\\\" }, + { "\"\\\\\" \"\\\\\\\"\"", { "\\", "\\\"" }, "\\\\ \\\\\\\"" }, + { "\\ \" \" ' '", { " ", " ", " "}, "\\ \\ \\ " }, + { "\"'\"", { "'" }, "\\'" }, + { "'\"' '\\\"'", { "\"", "\\\"" } , "\\\" \\\\\\\""}, + { "\"first arg\" \"\" \"third-arg\" \"'\" \"\\\"\" \"\\\\\\\"\" \" \" \"\"", + { "first arg", "", "third-arg", "'", "\"", "\\\""," ", "" }, + "first\\ arg '' third-arg \\' \\\" \\\\\\\" \\ ''"}, + { "\"\\a\" \"\\&\" \"\\#\" \"\\<\" \"\\^\"", + { "\\a", "\\&", "\\#" , "\\<" , "\\^"}, + "\\\\a \\\\\\& \\\\\\# \\\\\\< \\\\\\^" }, + { "1 '\n' 3", { "1", "\n", "3" }, "1 '\n' 3" }, +}; + +/* Run the remote argument passing self tests. */ + +static void +self_test () +{ + int failure_count = 0; + for (const auto &d : desc) + { + if (run_verbose ()) + { + if (&d != &desc[0]) + debug_printf ("------------------------------\n"); + debug_printf ("Input (%s)\n", d.input.c_str ()); + } + + /* Split argument string into individual arguments. */ + std::vector<std::string> split_args = gdb::remote_args::split (d.input); + + if (run_verbose ()) + { + debug_printf ("Split:\n"); + + size_t len = std::max (split_args.size (), d.split.size ()); + for (size_t i = 0; i < len; ++i) + { + const char *got = "N/A"; + const char *expected = got; + + if (i < split_args.size ()) + got = split_args[i].c_str (); + + if (i < d.split.size ()) + expected = d.split[i].c_str (); + + debug_printf (" got (%s), expected (%s)\n", got, expected); + } + } + + if (split_args != d.split) + { + ++failure_count; + if (run_verbose ()) + debug_printf ("FAIL\n"); + continue; + } + + /* Now join the arguments. */ + gdb::argv_vec split_args_c_str; + for (const std::string &s : split_args) + split_args_c_str.push_back (xstrdup (s.c_str ())); + std::string joined_args + = gdb::remote_args::join (split_args_c_str.get ()); + + if (run_verbose ()) + debug_printf ("Joined (%s), expected (%s)\n", + joined_args.c_str (), d.joined.c_str ()); + + if (joined_args != d.joined) + { + ++failure_count; + if (run_verbose ()) + debug_printf ("FAIL\n"); + continue; + } + + /* The contents of JOINED_ARGS will not be identical to D.INPUT. + There are multiple ways that an argument can be escaped, and our + join function just picks one. However, if we split JOINED_ARGS + again then each individual argument should be the same as those in + SPLIT_ARGS. So test that next. */ + std::vector<std::string> split_args_v2 + = gdb::remote_args::split (joined_args); + + if (split_args_v2 != split_args) + { + ++failure_count; + if (run_verbose ()) + { + debug_printf ("Re-split:\n"); + for (const auto &a : split_args_v2) + debug_printf (" got (%s)\n", a.c_str ()); + debug_printf ("FAIL\n"); + } + continue; + } + + if (run_verbose ()) + debug_printf ("PASS\n"); + } + + SELF_CHECK (failure_count == 0); +} + +} /* namespace remote_args_tests */ +} /* namespace selftests */ + +INIT_GDB_FILE (remote_arg_selftests) +{ + selftests::register_test ("remote-args", + selftests::remote_args_tests::self_test); +} diff --git a/gdb/unittests/rsp-low-selftests.c b/gdb/unittests/rsp-low-selftests.c index 7a1b2de..d0002ae 100644 --- a/gdb/unittests/rsp-low-selftests.c +++ b/gdb/unittests/rsp-low-selftests.c @@ -60,9 +60,7 @@ static void test_hex2str () } /* namespace rsp_low */ } /* namespace selftests */ -void _initialize_rsp_low_selftests (); -void -_initialize_rsp_low_selftests () +INIT_GDB_FILE (rsp_low_selftests) { selftests::register_test ("hex2bin_byte_vector", selftests::rsp_low::test_hex2bin_byte_vector); diff --git a/gdb/unittests/scoped_fd-selftests.c b/gdb/unittests/scoped_fd-selftests.c index 7abbbd8..da59a18 100644 --- a/gdb/unittests/scoped_fd-selftests.c +++ b/gdb/unittests/scoped_fd-selftests.c @@ -92,9 +92,7 @@ run_tests () } /* namespace scoped_fd */ } /* namespace selftests */ -void _initialize_scoped_fd_selftests (); -void -_initialize_scoped_fd_selftests () +INIT_GDB_FILE (scoped_fd_selftests) { selftests::register_test ("scoped_fd", selftests::scoped_fd::run_tests); diff --git a/gdb/unittests/scoped_ignore_signal-selftests.c b/gdb/unittests/scoped_ignore_signal-selftests.c index fdfaca3..86c5031 100644 --- a/gdb/unittests/scoped_ignore_signal-selftests.c +++ b/gdb/unittests/scoped_ignore_signal-selftests.c @@ -114,9 +114,7 @@ test_sigpipe () } /* namespace scoped_ignore_sig */ } /* namespace selftests */ -void _initialize_scoped_ignore_signal_selftests (); -void -_initialize_scoped_ignore_signal_selftests () +INIT_GDB_FILE (scoped_ignore_signal_selftests) { #ifdef SIGPIPE selftests::register_test ("scoped_ignore_sigpipe", diff --git a/gdb/unittests/scoped_mmap-selftests.c b/gdb/unittests/scoped_mmap-selftests.c index 695ed1e..72568fe 100644 --- a/gdb/unittests/scoped_mmap-selftests.c +++ b/gdb/unittests/scoped_mmap-selftests.c @@ -135,9 +135,7 @@ run_tests () #endif /* !defined(HAVE_SYS_MMAN_H) */ -void _initialize_scoped_mmap_selftests (); -void -_initialize_scoped_mmap_selftests () +INIT_GDB_FILE (scoped_mmap_selftests) { #if defined(HAVE_SYS_MMAN_H) selftests::register_test ("scoped_mmap", diff --git a/gdb/unittests/scoped_restore-selftests.c b/gdb/unittests/scoped_restore-selftests.c index 18d898b..4396046 100644 --- a/gdb/unittests/scoped_restore-selftests.c +++ b/gdb/unittests/scoped_restore-selftests.c @@ -102,9 +102,7 @@ run_tests () } /* namespace scoped_restore_tests */ } /* namespace selftests */ -void _initialize_scoped_restore_selftests (); -void -_initialize_scoped_restore_selftests () +INIT_GDB_FILE (scoped_restore_selftests) { selftests::register_test ("scoped_restore", selftests::scoped_restore_tests::run_tests); diff --git a/gdb/unittests/search-memory-selftests.c b/gdb/unittests/search-memory-selftests.c index 0e82d6c..c1942fc 100644 --- a/gdb/unittests/search-memory-selftests.c +++ b/gdb/unittests/search-memory-selftests.c @@ -89,9 +89,7 @@ run_tests () } /* namespace selftests */ -void _initialize_search_memory_selftests (); -void -_initialize_search_memory_selftests () +INIT_GDB_FILE (search_memory_selftests) { selftests::register_test ("search_memory", selftests::search_memory_tests::run_tests); diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c index 2cbe194..f2a37bb 100644 --- a/gdb/unittests/style-selftests.c +++ b/gdb/unittests/style-selftests.c @@ -100,9 +100,7 @@ run_tests () } /* namespace style */ } /* namespace selftests */ -void _initialize_style_selftest (); -void -_initialize_style_selftest () +INIT_GDB_FILE (style_selftest) { selftests::register_test ("style", selftests::style::run_tests); diff --git a/gdb/unittests/tracepoint-selftests.c b/gdb/unittests/tracepoint-selftests.c index 235dd1e..bef51ae 100644 --- a/gdb/unittests/tracepoint-selftests.c +++ b/gdb/unittests/tracepoint-selftests.c @@ -60,9 +60,7 @@ test_parse_static_tracepoint_marker_definition () } /* namespace tracepoint_tests */ } /* namespace selftests */ -void _initialize_tracepoint_selftests (); -void -_initialize_tracepoint_selftests () +INIT_GDB_FILE (tracepoint_selftests) { selftests::register_test ("parse_static_tracepoint_marker_definition", diff --git a/gdb/unittests/tui-selftests.c b/gdb/unittests/tui-selftests.c index e61b0db..5235a07 100644 --- a/gdb/unittests/tui-selftests.c +++ b/gdb/unittests/tui-selftests.c @@ -45,9 +45,7 @@ run_tests () #endif /* TUI */ -void _initialize_tui_selftest (); -void -_initialize_tui_selftest () +INIT_GDB_FILE (tui_selftest) { #ifdef TUI selftests::register_test ("tui", selftests::tui::run_tests); diff --git a/gdb/unittests/ui-file-selftests.c b/gdb/unittests/ui-file-selftests.c index b62fd4f..d78c6b0 100644 --- a/gdb/unittests/ui-file-selftests.c +++ b/gdb/unittests/ui-file-selftests.c @@ -53,9 +53,7 @@ run_tests () } /* namespace file*/ } /* namespace selftests */ -void _initialize_ui_file_selftest (); -void -_initialize_ui_file_selftest () +INIT_GDB_FILE (ui_file_selftest) { selftests::register_test ("ui-file", selftests::file::run_tests); diff --git a/gdb/unittests/unique_xmalloc_ptr_char.c b/gdb/unittests/unique_xmalloc_ptr_char.c index 984f692..7f61c06 100644 --- a/gdb/unittests/unique_xmalloc_ptr_char.c +++ b/gdb/unittests/unique_xmalloc_ptr_char.c @@ -47,9 +47,7 @@ unique_xmalloc_ptr_char () } } -void _initialize_unique_xmalloc_ptr_char (); -void -_initialize_unique_xmalloc_ptr_char () +INIT_GDB_FILE (unique_xmalloc_ptr_char) { selftests::register_test ("unique_xmalloc_ptr_char", selftests::unpack::unique_xmalloc_ptr_char); diff --git a/gdb/unittests/unpack-selftests.c b/gdb/unittests/unpack-selftests.c index 54af777..fccf7a2 100644 --- a/gdb/unittests/unpack-selftests.c +++ b/gdb/unittests/unpack-selftests.c @@ -52,9 +52,7 @@ unpack_field_as_long_tests (struct gdbarch *arch) } } -void _initialize_unpack_selftests (); -void -_initialize_unpack_selftests () +INIT_GDB_FILE (unpack_selftests) { selftests::register_test_foreach_arch ("unpack_field_as_long", selftests::unpack::unpack_field_as_long_tests); 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/unittests/vec-utils-selftests.c b/gdb/unittests/vec-utils-selftests.c index 4be0b86..11fad31 100644 --- a/gdb/unittests/vec-utils-selftests.c +++ b/gdb/unittests/vec-utils-selftests.c @@ -65,9 +65,7 @@ unordered_remove_tests () } /* namespace vector_utils_tests */ } /* namespace selftests */ -void _initialize_vec_utils_selftests (); -void -_initialize_vec_utils_selftests () +INIT_GDB_FILE (vec_utils_selftests) { selftests::register_test ("unordered_remove", diff --git a/gdb/unittests/xml-utils-selftests.c b/gdb/unittests/xml-utils-selftests.c index 5884faa..f4a637b 100644 --- a/gdb/unittests/xml-utils-selftests.c +++ b/gdb/unittests/xml-utils-selftests.c @@ -47,9 +47,7 @@ static void test_xml_escape_text_append () } } -void _initialize_xml_utils (); -void -_initialize_xml_utils () +INIT_GDB_FILE (xml_utils) { selftests::register_test ("xml_escape_text", selftests::xml_utils::test_xml_escape_text); diff --git a/gdb/user-regs.c b/gdb/user-regs.c index 5860d7d..7170cda 100644 --- a/gdb/user-regs.c +++ b/gdb/user-regs.c @@ -237,9 +237,7 @@ maintenance_print_user_registers (const char *args, int from_tty) } } -void _initialize_user_regs (); -void -_initialize_user_regs () +INIT_GDB_FILE (user_regs) { add_cmd ("user-registers", class_maintenance, maintenance_print_user_registers, diff --git a/gdb/utils.c b/gdb/utils.c index ce3c26e..3114a4b 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -54,7 +54,6 @@ #include "top.h" #include "ui.h" #include "main.h" -#include "solist.h" #include "inferior.h" @@ -177,6 +176,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"); @@ -1401,7 +1401,26 @@ pager_file::emit_style_escape (const ui_file_style &style) { m_applied_style = style; if (m_paging) - m_stream->emit_style_escape (style); + { + /* Previous style changes will have been sent to m_stream via + escape sequences encoded in the m_wrap_buffer. As a result, + the m_stream->m_applied_style will not have been updated. + + If we now use m_stream->emit_style_escape, then the required + style might not actually be emitted as the requested style + might happen to match the out of date value in + m_stream->m_applied_style. + + Instead, send the style change directly using m_stream->puts. + + However, we track what style is currently applied to the + underlying stream in m_stream_style, this is updated whenever + m_wrap_buffer is flushed to the underlying stream. And so, if + the style we are applying matches what we know is currently + applied to the underlying stream, then we can skip sending + this style to the stream. */ + this->set_stream_style (m_applied_style); + } else m_wrap_buffer.append (style.to_ansi ()); } @@ -1424,8 +1443,8 @@ pager_file::prompt_for_continue () scoped_restore save_paging = make_scoped_restore (&m_paging, true); - /* Clear the current styling. */ - m_stream->emit_style_escape (ui_file_style ()); + /* Clear the current styling on ourselves and the managed stream. */ + this->emit_style_escape (ui_file_style ()); if (annotation_level > 1) m_stream->puts (("\n\032\032pre-prompt-for-continue\n")); @@ -1508,6 +1527,7 @@ pager_file::flush_wrap_buffer () if (!m_paging && !m_wrap_buffer.empty ()) { m_stream->puts (m_wrap_buffer.c_str ()); + m_stream_style = m_applied_style; m_wrap_buffer.clear (); } } @@ -1724,7 +1744,8 @@ pager_file::puts (const char *linebuffer) current applied style to how it was at the WRAP_COLUMN location. */ m_applied_style = m_wrap_style; - m_stream->emit_style_escape (ui_file_style ()); + this->set_stream_style (ui_file_style ()); + /* If we aren't actually wrapping, don't output newline -- if chars_per_line is right, we probably just overflowed anyway; if it's wrong, @@ -1752,7 +1773,7 @@ pager_file::puts (const char *linebuffer) /* Having finished inserting the wrapping we should restore the style as it was at the WRAP_COLUMN. */ - m_stream->emit_style_escape (m_wrap_style); + this->set_stream_style (m_wrap_style); /* The WRAP_BUFFER will still contain content, and that content might set some alternative style. Restore @@ -1767,7 +1788,7 @@ pager_file::puts (const char *linebuffer) m_wrap_column = 0; /* And disable fancy wrap */ } else if (did_paginate) - m_stream->emit_style_escape (save_style); + this->emit_style_escape (save_style); } } @@ -3332,7 +3353,7 @@ gdb_argv_as_array_view_test () argument. */ std::string -ldirname (const char *filename) +gdb_ldirname (const char *filename) { std::string dirname; const char *base = lbasename (filename); @@ -3374,51 +3395,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 @@ -3740,9 +3716,7 @@ test_assign_set_return_if_changed () } #endif -void _initialize_utils (); -void -_initialize_utils () +INIT_GDB_FILE (utils) { add_setshow_uinteger_cmd ("width", class_support, &chars_per_line, _("\ Set number of characters where GDB should wrap lines of its output."), _("\ diff --git a/gdb/utils.h b/gdb/utils.h index bc8c2ef..b37e8f7 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -133,10 +133,7 @@ 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); +std::string gdb_ldirname (const char *filename); extern int count_path_elements (const char *path); diff --git a/gdb/v850-tdep.c b/gdb/v850-tdep.c index e9ec858..d592344 100644 --- a/gdb/v850-tdep.c +++ b/gdb/v850-tdep.c @@ -1461,9 +1461,7 @@ v850_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } -void _initialize_v850_tdep (); -void -_initialize_v850_tdep () +INIT_GDB_FILE (v850_tdep) { gdbarch_register (bfd_arch_v850, v850_gdbarch_init); gdbarch_register (bfd_arch_v850_rh850, v850_gdbarch_init); diff --git a/gdb/valops.c b/gdb/valops.c index 94f908d..88f3e32 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -4162,9 +4162,7 @@ cast_into_complex (struct type *type, struct value *val) error (_("cannot cast non-number to complex")); } -void _initialize_valops (); -void -_initialize_valops () +INIT_GDB_FILE (valops) { add_setshow_boolean_cmd ("overload-resolution", class_support, &overload_resolution, _("\ diff --git a/gdb/valprint.c b/gdb/valprint.c index ed03f6d..305faf7 100644 --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -3194,9 +3194,7 @@ test_print_flags (gdbarch *arch) #endif -void _initialize_valprint (); -void -_initialize_valprint () +INIT_GDB_FILE (valprint) { #if GDB_SELF_TEST selftests::register_test_foreach_arch ("print-flags", test_print_flags); diff --git a/gdb/value.c b/gdb/value.c index 0f1be2e..5574642 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, @@ -4486,9 +4496,7 @@ test_value_copy () } /* namespace selftests */ #endif /* GDB_SELF_TEST */ -void _initialize_values (); -void -_initialize_values () +INIT_GDB_FILE (values) { cmd_list_element *show_convenience_cmd = add_cmd ("convenience", no_class, show_convenience, _("\ 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 diff --git a/gdb/varobj.c b/gdb/varobj.c index 8dc7a30..4dc986a 100644 --- a/gdb/varobj.c +++ b/gdb/varobj.c @@ -2424,9 +2424,7 @@ eq_varobj_and_string (const void *a, const void *b) return obj->obj_name == name; } -void _initialize_varobj (); -void -_initialize_varobj () +INIT_GDB_FILE (varobj) { varobj_table = htab_create_alloc (5, hash_varobj, eq_varobj_and_string, nullptr, xcalloc, xfree); diff --git a/gdb/vax-bsd-nat.c b/gdb/vax-bsd-nat.c index 6d8bb54..717314e 100644 --- a/gdb/vax-bsd-nat.c +++ b/gdb/vax-bsd-nat.c @@ -136,9 +136,7 @@ vaxbsd_supply_pcb (struct regcache *regcache, struct pcb *pcb) return 1; } -void _initialize_vaxbsd_nat (); -void -_initialize_vaxbsd_nat () +INIT_GDB_FILE (vaxbsd_nat) { add_inf_child_target (&the_vax_bsd_nat_target); diff --git a/gdb/vax-netbsd-tdep.c b/gdb/vax-netbsd-tdep.c index 34a9150..7781264 100644 --- a/gdb/vax-netbsd-tdep.c +++ b/gdb/vax-netbsd-tdep.c @@ -32,13 +32,10 @@ vaxnbsd_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) nbsd_init_abi (info, gdbarch); /* NetBSD ELF uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); } -void _initialize_vaxnbsd_tdep (); -void -_initialize_vaxnbsd_tdep () +INIT_GDB_FILE (vaxnbsd_tdep) { gdbarch_register_osabi (bfd_arch_vax, 0, GDB_OSABI_NETBSD, vaxnbsd_elf_init_abi); diff --git a/gdb/vax-tdep.c b/gdb/vax-tdep.c index 94dc308..0a83f4f 100644 --- a/gdb/vax-tdep.c +++ b/gdb/vax-tdep.c @@ -506,9 +506,7 @@ vax_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return (gdbarch); } -void _initialize_vax_tdep (); -void -_initialize_vax_tdep () +INIT_GDB_FILE (vax_tdep) { gdbarch_register (bfd_arch_vax, vax_gdbarch_init, NULL); } diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index 0fee4ad..c001d38 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -56,7 +56,6 @@ #include "cli/cli-style.h" #include <unistd.h> #include "exec.h" -#include "solist.h" #include "solib.h" #include "xml-support.h" #include "inttypes.h" @@ -433,7 +432,12 @@ wait_for_single (HANDLE handle, DWORD howlong) { while (true) { - DWORD r = WaitForSingleObject (handle, howlong); + /* Using an INFINITE argument to WaitForSingleObject may cause a system + deadlock. Avoid it by waiting for a bit in a loop instead. */ + DWORD milliseconds = howlong == INFINITE ? 100 : howlong; + DWORD r = WaitForSingleObject (handle, milliseconds); + if (howlong == INFINITE && r == WAIT_TIMEOUT) + continue; if (r == WAIT_OBJECT_0) return; if (r == WAIT_FAILED) @@ -3089,9 +3093,7 @@ windows_nat_target::thread_name (struct thread_info *thr) } -void _initialize_windows_nat (); -void -_initialize_windows_nat () +INIT_GDB_FILE (windows_nat) { x86_dr_low.set_control = cygwin_set_dr7; x86_dr_low.set_addr = cygwin_set_dr; @@ -3265,9 +3267,7 @@ windows_nat_target::thread_alive (ptid_t ptid) return WaitForSingleObject (th->h, 0) != WAIT_OBJECT_0; } -void _initialize_check_for_gdb_ini (); -void -_initialize_check_for_gdb_ini () +INIT_GDB_FILE (check_for_gdb_ini) { char *homedir; if (inhibit_gdbinit) diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c index 0ba1472..05335d2 100644 --- a/gdb/windows-tdep.c +++ b/gdb/windows-tdep.c @@ -36,7 +36,6 @@ #include "gdbcore.h" #include "coff/internal.h" #include "libcoff.h" -#include "solist.h" #define CYGWIN_DLL_NAME "cygwin1.dll" @@ -863,10 +862,25 @@ windows_get_siginfo_type (struct gdbarch *gdbarch) return siginfo_type; } +/* solib_ops for Windows systems. */ + +struct windows_solib_ops : target_solib_ops +{ + void create_inferior_hook (int from_tty) const override; +}; + +/* Return a new solib_ops for Windows systems. */ + +static solib_ops_up +make_windows_solib_ops () +{ + return std::make_unique<windows_solib_ops> (); +} + /* Implement the "solib_create_inferior_hook" solib_ops method. */ -static void -windows_solib_create_inferior_hook (int from_tty) +void +windows_solib_ops::create_inferior_hook (int from_tty) const { CORE_ADDR exec_base = 0; @@ -911,8 +925,6 @@ windows_solib_create_inferior_hook (int from_tty) } } -static solib_ops windows_so_ops; - /* Common parts for gdbarch initialization for the Windows and Cygwin OS ABIs. */ @@ -929,10 +941,7 @@ windows_init_abi_common (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_iterate_over_objfiles_in_search_order (gdbarch, windows_iterate_over_objfiles_in_search_order); - windows_so_ops = solib_target_so_ops; - windows_so_ops.solib_create_inferior_hook - = windows_solib_create_inferior_hook; - set_gdbarch_so_ops (gdbarch, &windows_so_ops); + set_gdbarch_make_solib_ops (gdbarch, make_windows_solib_ops); set_gdbarch_get_siginfo_type (gdbarch, windows_get_siginfo_type); } @@ -1183,9 +1192,7 @@ windows_core_pid_to_str (struct gdbarch *gdbarch, ptid_t ptid) return normal_pid_to_str (ptid); } -void _initialize_windows_tdep (); -void -_initialize_windows_tdep () +INIT_GDB_FILE (windows_tdep) { init_w32_command_list (); cmd_list_element *info_w32_thread_information_block_cmd diff --git a/gdb/x86-bsd-nat.c b/gdb/x86-bsd-nat.c index 0c9ddfd..37376bb 100644 --- a/gdb/x86-bsd-nat.c +++ b/gdb/x86-bsd-nat.c @@ -128,9 +128,7 @@ x86bsd_dr_get_control (void) #endif /* PT_GETDBREGS */ -void _initialize_x86_bsd_nat (); -void -_initialize_x86_bsd_nat () +INIT_GDB_FILE (x86_bsd_nat) { #ifdef HAVE_PT_GETDBREGS x86_dr_low.set_control = x86bsd_dr_set_control; diff --git a/gdb/x86-gnu-nat.c b/gdb/x86-gnu-nat.c index b9ba95b..e088189 100644 --- a/gdb/x86-gnu-nat.c +++ b/gdb/x86-gnu-nat.c @@ -491,9 +491,7 @@ x86_gnu_dr_get_control (void) } #endif /* i386_DEBUG_STATE */ -void _initialize_x86_gnu_nat (); -void -_initialize_x86_gnu_nat () +INIT_GDB_FILE (x86_gnu_nat) { #ifdef i386_DEBUG_STATE x86_dr_low.set_control = x86_gnu_dr_set_control; diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c index fc7c5f6..81db5d8 100644 --- a/gdb/x86-linux-nat.c +++ b/gdb/x86-linux-nat.c @@ -210,9 +210,7 @@ x86_linux_get_thread_area (pid_t pid, void *addr, unsigned int *base_addr) } -void _initialize_x86_linux_nat (); -void -_initialize_x86_linux_nat () +INIT_GDB_FILE (x86_linux_nat) { /* Initialize the debug register function vectors. */ x86_dr_low.set_control = x86_linux_dr_set_control; diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c index e91c64c..70585b3 100644 --- a/gdb/xcoffread.c +++ b/gdb/xcoffread.c @@ -2999,9 +2999,7 @@ xcoff_get_n_import_files (bfd *abfd) return l_nimpid - 1; } -void _initialize_xcoffread (); -void -_initialize_xcoffread () +INIT_GDB_FILE (xcoffread) { add_symtab_fns (bfd_target_xcoff_flavour, &xcoff_sym_fns); } diff --git a/gdb/xml-support.c b/gdb/xml-support.c index fb8b612..08524f8 100644 --- a/gdb/xml-support.c +++ b/gdb/xml-support.c @@ -999,10 +999,7 @@ xml_fetch_content_from_file (const char *filename, const char *dirname) return text; } -void _initialize_xml_support (); -void _initialize_xml_support (); -void -_initialize_xml_support () +INIT_GDB_FILE (xml_support) { add_setshow_boolean_cmd ("xml", class_maintenance, &debug_xml, _("Set XML parser debugging."), diff --git a/gdb/xml-syscall.c b/gdb/xml-syscall.c index fe0ea2b..b58fe5d0 100644 --- a/gdb/xml-syscall.c +++ b/gdb/xml-syscall.c @@ -319,7 +319,7 @@ xml_init_syscalls_info (const char *filename) if (!full_file) return NULL; - const std::string dirname = ldirname (filename); + const std::string dirname = gdb_ldirname (filename); auto fetch_another = [&dirname] (const char *name) { return xml_fetch_content_from_file (name, dirname.c_str ()); diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c index 6c095af..2f213dc 100644 --- a/gdb/xml-tdesc.c +++ b/gdb/xml-tdesc.c @@ -670,7 +670,7 @@ file_read_description_xml (const char *filename) return NULL; } - const std::string dirname = ldirname (filename); + const std::string dirname = gdb_ldirname (filename); auto fetch_another = [&dirname] (const char *name) { return xml_fetch_content_from_file (name, dirname.c_str ()); diff --git a/gdb/xstormy16-tdep.c b/gdb/xstormy16-tdep.c index 8482461..b4042f1 100644 --- a/gdb/xstormy16-tdep.c +++ b/gdb/xstormy16-tdep.c @@ -828,9 +828,7 @@ xstormy16_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) Initializer function for the Sanyo Xstormy16a module. Called by gdb at start-up. */ -void _initialize_xstormy16_tdep (); -void -_initialize_xstormy16_tdep () +INIT_GDB_FILE (xstormy16_tdep) { gdbarch_register (bfd_arch_xstormy16, xstormy16_gdbarch_init); } diff --git a/gdb/xtensa-linux-nat.c b/gdb/xtensa-linux-nat.c index 11fbf55..4a50f38 100644 --- a/gdb/xtensa-linux-nat.c +++ b/gdb/xtensa-linux-nat.c @@ -327,9 +327,7 @@ ps_get_thread_area (struct ps_prochandle *ph, return PS_OK; } -void _initialize_xtensa_linux_nat (); -void -_initialize_xtensa_linux_nat () +INIT_GDB_FILE (xtensa_linux_nat) { const xtensa_regtable_t *ptr; diff --git a/gdb/xtensa-linux-tdep.c b/gdb/xtensa-linux-tdep.c index b72d683..7792408 100644 --- a/gdb/xtensa-linux-tdep.c +++ b/gdb/xtensa-linux-tdep.c @@ -20,6 +20,7 @@ #include "xtensa-tdep.h" #include "osabi.h" #include "linux-tdep.h" +#include "solib-svr4-linux.h" #include "solib-svr4.h" #include "symtab.h" #include "gdbarch.h" @@ -111,8 +112,7 @@ xtensa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) linux_init_abi (info, gdbarch, 0); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, linux_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_linux_ilp32_svr4_solib_ops); set_gdbarch_gdb_signal_from_target (gdbarch, xtensa_linux_gdb_signal_from_target); @@ -124,9 +124,7 @@ xtensa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) svr4_fetch_objfile_link_map); } -void _initialize_xtensa_linux_tdep (); -void -_initialize_xtensa_linux_tdep () +INIT_GDB_FILE (xtensa_linux_tdep) { gdbarch_register_osabi (bfd_arch_xtensa, bfd_mach_xtensa, GDB_OSABI_LINUX, xtensa_linux_init_abi); diff --git a/gdb/xtensa-tdep.c b/gdb/xtensa-tdep.c index a4bbffb..8a2f129 100644 --- a/gdb/xtensa-tdep.c +++ b/gdb/xtensa-tdep.c @@ -3238,8 +3238,7 @@ xtensa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_iterate_over_regset_sections (gdbarch, xtensa_iterate_over_regset_sections); - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); + set_solib_svr4_ops (gdbarch, make_svr4_ilp32_solib_ops); /* Hook in the ABI-specific overrides, if they have been registered. */ gdbarch_init_osabi (info, gdbarch); @@ -3253,9 +3252,7 @@ xtensa_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) error (_("xtensa_dump_tdep(): not implemented")); } -void _initialize_xtensa_tdep (); -void -_initialize_xtensa_tdep () +INIT_GDB_FILE (xtensa_tdep) { gdbarch_register (bfd_arch_xtensa, xtensa_gdbarch_init, xtensa_dump_tdep); xtensa_init_reggroups (); diff --git a/gdb/z80-tdep.c b/gdb/z80-tdep.c index bfa52dd..abba50b 100644 --- a/gdb/z80-tdep.c +++ b/gdb/z80-tdep.c @@ -1331,14 +1331,14 @@ ez80_ddfd_insn_table[] = { 0007, 0307, 2, insn_default }, //"ld rr,(ii+d)" { 0061, 0377, 2, insn_default }, //"ld ii,(ii+d)" /* common instructions */ - { 0011, 0367, 2, insn_default }, //"add ii,rr" + { 0011, 0317, 1, insn_default }, //"add ii,rr" { 0041, 0377, 3, insn_default }, //"ld ii,nn" { 0042, 0367, 3, insn_default }, //"ld (nn),ii", "ld ii,(nn)" { 0043, 0367, 1, insn_default }, //"inc ii", "dec ii" { 0044, 0366, 1, insn_default }, //"inc/dec iih/iil" { 0046, 0367, 2, insn_default }, //"ld iih,n", "ld iil,n" { 0064, 0376, 2, insn_default }, //"inc (ii+d)", "dec (ii+d)" - { 0066, 0377, 2, insn_default }, //"ld (ii+d),n" + { 0066, 0377, 3, insn_default }, //"ld (ii+d),n" { 0166, 0377, 0, insn_default }, //not an instruction { 0160, 0370, 2, insn_default }, //"ld (ii+d),r" { 0104, 0306, 1, insn_default }, //"ld r,iih", "ld r,iil" @@ -1360,7 +1360,7 @@ ez80_adl_ddfd_insn_table[] = { { 0007, 0307, 2, insn_default }, //"ld rr,(ii+d)" { 0061, 0377, 2, insn_default }, //"ld ii,(ii+d)" - { 0011, 0367, 1, insn_default }, //"add ii,rr" + { 0011, 0317, 1, insn_default }, //"add ii,rr" { 0041, 0377, 4, insn_default }, //"ld ii,nn" { 0042, 0367, 4, insn_default }, //"ld (nn),ii", "ld ii,(nn)" { 0043, 0367, 1, insn_default }, //"inc ii", "dec ii" @@ -1455,10 +1455,7 @@ z80_get_insn_info (struct gdbarch *gdbarch, const gdb_byte *buf, int *size) while (1); } -extern initialize_file_ftype _initialize_z80_tdep; - -void -_initialize_z80_tdep () +INIT_GDB_FILE (z80_tdep) { gdbarch_register (bfd_arch_z80, z80_gdbarch_init); initialize_tdesc_z80 (); |