aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/MAINTAINERS5
-rw-r--r--gdb/Makefile.in4
-rw-r--r--gdb/NEWS15
-rw-r--r--gdb/aarch64-tdep.c3
-rw-r--r--gdb/amd64-gnu-tdep.c231
-rw-r--r--gdb/breakpoint.c11
-rw-r--r--gdb/bsd-uthread.c5
-rw-r--r--gdb/cli/cli-decode.c12
-rw-r--r--gdb/config/i386/nm-x86-gnu.h (renamed from gdb/config/i386/nm-i386gnu.h)7
-rw-r--r--gdb/config/i386/x86-gnu.mn (renamed from gdb/config/i386/i386gnu.mn)0
-rw-r--r--gdb/configure.host1
-rw-r--r--gdb/configure.nat27
-rw-r--r--gdb/configure.tgt6
-rw-r--r--gdb/corelow.c6
-rw-r--r--gdb/doc/gdb.texinfo61
-rw-r--r--gdb/dwarf2/cooked-index.c40
-rw-r--r--gdb/dwarf2/cooked-index.h27
-rw-r--r--gdb/dwarf2/index-write.c98
-rw-r--r--gdb/dwarf2/mapped-index.h5
-rw-r--r--gdb/dwarf2/read-debug-names.c161
-rw-r--r--gdb/dwarf2/read-debug-names.h25
-rw-r--r--gdb/dwarf2/read.c19
-rw-r--r--gdb/dwarf2/read.h4
-rw-r--r--gdb/i386-gnu-tdep.c13
-rw-r--r--gdb/infcmd.c6
-rw-r--r--gdb/interps.c4
-rw-r--r--gdb/interps.h14
-rw-r--r--gdb/loongarch-tdep.c6
-rw-r--r--gdb/mi/mi-cmd-break.c3
-rw-r--r--gdb/mi/mi-cmd-disas.c1
-rw-r--r--gdb/mi/mi-cmd-env.c2
-rw-r--r--gdb/mi/mi-cmd-file.c3
-rw-r--r--gdb/mi/mi-cmd-stack.c4
-rw-r--r--gdb/mi/mi-cmd-var.c1
-rw-r--r--gdb/mi/mi-cmds.c1
-rw-r--r--gdb/mi/mi-interp.c27
-rw-r--r--gdb/mi/mi-interp.h2
-rw-r--r--gdb/mi/mi-main.c4
-rw-r--r--gdb/mi/mi-parse.c1
-rw-r--r--gdb/nat/fork-inferior.c81
-rw-r--r--gdb/observable.h3
-rw-r--r--gdb/solib.c30
-rw-r--r--gdb/stack.c6
-rw-r--r--gdb/testsuite/gdb.ada/inline-section-gc.exp12
-rw-r--r--gdb/testsuite/gdb.base/args.exp64
-rw-r--r--gdb/testsuite/gdb.base/condbreak-multi-context.cc6
-rw-r--r--gdb/testsuite/gdb.base/condbreak-multi-context.exp231
-rw-r--r--gdb/testsuite/gdb.base/corefile-exec-context.exp2
-rw-r--r--gdb/testsuite/gdb.base/dlmopen.exp244
-rw-r--r--gdb/testsuite/gdb.base/frame-selection.c21
-rw-r--r--gdb/testsuite/gdb.base/frame-selection.exp37
-rw-r--r--gdb/testsuite/gdb.base/inferior-args.exp105
-rw-r--r--gdb/testsuite/gdb.base/libsegfault.exp2
-rw-r--r--gdb/testsuite/gdb.base/startup-with-shell.exp149
-rw-r--r--gdb/testsuite/gdb.dap/memory.exp4
-rw-r--r--gdb/testsuite/gdb.mi/mi-dlmopen-lib-dep.c21
-rw-r--r--gdb/testsuite/gdb.mi/mi-dlmopen-lib.c28
-rw-r--r--gdb/testsuite/gdb.mi/mi-dlmopen.c59
-rw-r--r--gdb/testsuite/gdb.mi/mi-dlmopen.exp222
-rw-r--r--gdb/testsuite/gdb.mi/mi-sym-info.exp20
-rw-r--r--gdb/testsuite/gdb.python/py-inferior.exp4
-rw-r--r--gdb/testsuite/gdb.python/py-symbol.exp20
-rw-r--r--gdb/testsuite/gdb.threads/attach-slow-waitpid.exp2
-rw-r--r--gdb/testsuite/gdb.trace/basic-libipa.exp2
-rw-r--r--gdb/testsuite/gdb.tui/flush-after-run.c54
-rw-r--r--gdb/testsuite/gdb.tui/flush-after-run.exp66
-rw-r--r--gdb/testsuite/lib/gdb.exp51
-rw-r--r--gdb/testsuite/lib/gnat_debug_info_test.adb15
-rw-r--r--gdb/testsuite/lib/prelink-support.exp33
-rw-r--r--gdb/tui/tui-hooks.c5
-rw-r--r--gdb/tui/tui-status.c2
-rw-r--r--gdb/tui/tui-win.c2
-rw-r--r--gdb/tui/tui-wingeneral.c7
-rw-r--r--gdb/tui/tui-winsource.c7
-rw-r--r--gdb/tui/tui.c2
-rw-r--r--gdb/unittests/array-view-selftests.c13
-rw-r--r--gdb/x86-gnu-nat.c (renamed from gdb/i386-gnu-nat.c)171
77 files changed, 2112 insertions, 556 deletions
diff --git a/gdb/MAINTAINERS b/gdb/MAINTAINERS
index 927be43..187a1f6 100644
--- a/gdb/MAINTAINERS
+++ b/gdb/MAINTAINERS
@@ -366,7 +366,6 @@ the native maintainer when resolving ABI issues.
mcore Deleted
mep --target=mep-elf
- Kevin Buettner kevinb@redhat.com
microblaze --target=microblaze-xilinx-elf
--target=microblaze-linux-gnu
@@ -514,13 +513,9 @@ ARM Richard Earnshaw rearnsha@arm.com
Blackfin Mike Frysinger vapier@gentoo.org
CRIS Hans-Peter Nilsson hp@axis.com
IA64 Jeff Johnston jjohnstn@redhat.com
-PowerPC Kevin Buettner kevinb@redhat.com
S390 Ulrich Weigand uweigand@de.ibm.com
djgpp DJ Delorie dj@delorie.com
[Please use this address to contact DJ about DJGPP]
-ia64 Kevin Buettner kevinb@redhat.com
-AIX Kevin Buettner kevinb@redhat.com
-GNU/Linux PPC native Kevin Buettner kevinb@redhat.com
Pascal support Pierre Muller muller@sourceware.org
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b726b1d..1619c82 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -741,6 +741,7 @@ ALL_64_TARGET_OBS = \
amd64-darwin-tdep.o \
amd64-dicos-tdep.o \
amd64-fbsd-tdep.o \
+ amd64-gnu-tdep.o \
amd64-linux-tdep.o \
amd64-netbsd-tdep.o \
amd64-obsd-tdep.o \
@@ -1700,6 +1701,7 @@ ALLDEPFILES = \
amd64-dicos-tdep.c \
amd64-fbsd-nat.c \
amd64-fbsd-tdep.c \
+ amd64-gnu-tdep.c \
amd64-linux-nat.c \
amd64-linux-tdep.c \
amd64-nat.c \
@@ -1755,7 +1757,6 @@ ALLDEPFILES = \
i386-dicos-tdep.c \
i386-fbsd-nat.c \
i386-fbsd-tdep.c \
- i386-gnu-nat.c \
i386-gnu-tdep.c \
i386-linux-nat.c \
i386-linux-tdep.c \
@@ -1882,6 +1883,7 @@ ALLDEPFILES = \
vax-tdep.c \
windows-nat.c \
windows-tdep.c \
+ x86-gnu-nat.c \
x86-nat.c \
x86-tdep.c \
xcoffread.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index 39a5f66..2931a20 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -62,6 +62,21 @@ binary-upload in qSupported reply
stub doesn't report this feature supported, then GDB will not use
the 'x' packet.
+* MI changes
+
+** The =library-unloaded event now includes the 'ranges' field, which
+ has the same meaning as for the =library-loaded event.
+
+** The =library-unloaded event now includes the 'still-in-use' field.
+ This field is 'true' when a library is unloaded (removed from the
+ inferior's list of loaded libraries), but the mapping within the
+ inferior's address space is retained, as the library was mapped
+ multiple times, and the same mapping was being reused. In all
+ other cases, this field will have the value 'false'.
+
+* Support for stabs debugging format and the a.out/dbx object format is
+ deprecated, and will be removed in GDB 18.
+
*** Changes in GDB 16
* Support for Nios II targets has been removed as this architecture
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 840f987..6e712b1 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3290,6 +3290,9 @@ aarch64_pseudo_read_value (gdbarch *gdbarch, const frame_info_ptr &next_frame,
return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
pseudo_offset - AARCH64_SVE_V0_REGNUM);
+ if (tdep->has_pauth () && pseudo_reg_num == tdep->ra_sign_state_regnum)
+ return value::zero (builtin_type (gdbarch)->builtin_uint64, lval_register);
+
gdb_assert_not_reached ("regnum out of bound");
}
diff --git a/gdb/amd64-gnu-tdep.c b/gdb/amd64-gnu-tdep.c
new file mode 100644
index 0000000..435095e
--- /dev/null
+++ b/gdb/amd64-gnu-tdep.c
@@ -0,0 +1,231 @@
+/* Target-dependent code for the GNU Hurd.
+ 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/>. */
+
+#include "defs.h"
+#include "extract-store-integer.h"
+#include "gdbcore.h"
+#include "osabi.h"
+#include "solib-svr4.h"
+
+#include "amd64-tdep.h"
+#include "glibc-tdep.h"
+
+/* Recognizing signal handler frames. */
+
+/* When the GNU/Hurd libc calls a signal handler, the return address points
+ inside the trampoline assembly snippet.
+
+ If the trampoline function name can not be identified, we resort to reading
+ memory from the process in order to identify it. */
+
+static const gdb_byte gnu_sigtramp_code[] =
+{
+/* rpc_wait_trampoline: */
+ 0x48, 0xc7, 0xc0, 0xe7, 0xff, 0xff, 0xff, /* mov $-25,%rax */
+ 0x0f, 0x05, /* syscall */
+ 0x49, 0x89, 0x04, 0x24, /* mov %rax,(%r12) */
+ 0x48, 0x89, 0xdc, /* mov %rbx,%rsp */
+
+/* trampoline: */
+ 0x5f, /* pop %rdi */
+ 0x5e, /* pop %rsi */
+ 0x5a, /* pop %rdx */
+ 0x48, 0x83, 0xc4, 0x08, /* add $0x8,%rsp */
+ 0x41, 0xff, 0xd5, /* call *%r13 */
+
+/* RA HERE */
+ 0x48, 0x8b, 0x7c, 0x24, 0x10, /* mov 0x10(%rsp),%rdi */
+ 0xc3, /* ret */
+
+/* firewall: */
+ 0xf4, /* hlt */
+};
+
+#define GNU_SIGTRAMP_LEN (sizeof gnu_sigtramp_code)
+#define GNU_SIGTRAMP_TAIL 7 /* length of tail after RA */
+
+/* If THIS_FRAME is a sigtramp routine, return the address of the
+ start of the routine. Otherwise, return 0. */
+
+static CORE_ADDR
+amd64_gnu_sigtramp_start (frame_info_ptr this_frame)
+{
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ gdb_byte buf[GNU_SIGTRAMP_LEN];
+
+ if (!safe_frame_unwind_memory (this_frame,
+ pc + GNU_SIGTRAMP_TAIL - GNU_SIGTRAMP_LEN,
+ buf))
+ return 0;
+
+ if (memcmp (buf, gnu_sigtramp_code, GNU_SIGTRAMP_LEN) != 0)
+ return 0;
+
+ return pc;
+}
+
+/* Return whether THIS_FRAME corresponds to a Hurd sigtramp routine. */
+
+static int
+amd64_gnu_sigtramp_p (const frame_info_ptr &this_frame)
+{
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ const char *name;
+
+ find_pc_partial_function (pc, &name, NULL, NULL);
+
+ /* If we have a NAME, we can check for the trampoline function */
+ if (name != NULL && strcmp (name, "trampoline") == 0)
+ return 1;
+
+ return amd64_gnu_sigtramp_start (this_frame) != 0;
+}
+
+/* Offset to sc_i386_thread_state in sigcontext, from <bits/sigcontext.h>. */
+#define AMD64_GNU_SIGCONTEXT_THREAD_STATE_OFFSET 32
+
+/* Assuming THIS_FRAME is a Hurd sigtramp routine, return the
+ address of the associated sigcontext structure. */
+
+static CORE_ADDR
+amd64_gnu_sigcontext_addr (const frame_info_ptr &this_frame)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ CORE_ADDR pc;
+ CORE_ADDR sp;
+ gdb_byte buf[8];
+
+ get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
+ sp = extract_unsigned_integer (buf, 8, byte_order);
+
+ pc = amd64_gnu_sigtramp_start (this_frame);
+ if (pc)
+ {
+ CORE_ADDR sigcontext_addr;
+
+ /* The sigcontext structure address is passed as the third argument
+ * of the signal handler but %RDX is not saved across calls. Luckily,
+ * the structured is saved underneath the &__sigreturn and a dummy word
+ * to fill the slot for the address for __sigreturn to return to.
+ */
+ read_memory (sp + 16, buf, 8);
+ sigcontext_addr = extract_unsigned_integer (buf, 8, byte_order);
+ return sigcontext_addr + AMD64_GNU_SIGCONTEXT_THREAD_STATE_OFFSET;
+ }
+
+ error (_("Couldn't recognize signal trampoline."));
+ return 0;
+}
+
+/* Mapping between the general-purpose registers in `struct
+ sigcontext' format (starting at sc_i386_thread_state)
+ and GDB's register cache layout. */
+
+/* From <bits/sigcontext.h>. */
+static int amd64_gnu_sc_reg_offset[] =
+{
+ 15 * 8, /* %rax */
+ 12 * 8, /* %rbx */
+ 14 * 8, /* %rcx */
+ 13 * 8, /* %rdx */
+ 10 * 8, /* %rsi */
+ 9 * 8, /* %rdi */
+ 10 * 8, /* %rbp */
+ 11 * 8, /* %rsp */
+ 0 * 8, /* %r8 ... */
+ 8 * 8,
+ 7 * 8,
+ 6 * 8,
+ 3 * 8,
+ 2 * 8,
+ 1 * 8,
+ 0 * 8, /* ... %r15 */
+ 16 * 8, /* %rip */
+ 18 * 8, /* %eflags */
+ 17 * 8, /* %cs */
+};
+
+/* From <sys/ucontext.h>. */
+static int amd64_gnu_gregset_reg_offset[] =
+{
+ 10 * 8, /* %rax */
+ 5 * 8, /* %rbx */
+ 11 * 8, /* %rcx */
+ 12 * 8, /* %rdx */
+ 13 * 8, /* %rsi */
+ 14 * 8, /* %rdi */
+ 4 * 8, /* %rbp */
+ 19 * 8, /* %rsp */
+ 9 * 8, /* %r8 ... */
+ 8 * 8,
+ 7 * 8,
+ 6 * 8,
+ 3 * 8,
+ 2 * 8,
+ 1 * 8,
+ 0 * 8, /* ... %r15 */
+ 16 * 8, /* %rip */
+ 18 * 8, /* %eflags */
+ 17 * 8, /* %cs */
+ -1, /* %ss */
+ -1, /* %ds */
+ -1, /* %es */
+ -1, /* %fs */
+ -1, /* %gs */
+};
+
+static void
+amd64_gnu_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+
+ amd64_init_abi (info, gdbarch,
+ amd64_target_description (X86_XSTATE_SSE_MASK, true));
+
+ /* Enable TLS support. */
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
+
+ /* Hurd uses SVR4-style shared libraries. */
+ set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target);
+
+ /* Hurd uses the dynamic linker included in the GNU C Library. */
+ set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
+
+ tdep->gregset_reg_offset = amd64_gnu_gregset_reg_offset;
+ tdep->gregset_num_regs = ARRAY_SIZE (amd64_gnu_gregset_reg_offset);
+ tdep->sizeof_gregset = 21 * 8; /* sizeof (struct i386_thread_state); */
+
+ tdep->sigtramp_p = amd64_gnu_sigtramp_p;
+ tdep->sigcontext_addr = amd64_gnu_sigcontext_addr;
+ tdep->sc_reg_offset = amd64_gnu_sc_reg_offset;
+ 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);
+}
+
+void _initialize_amd64_gnu_tdep ();
+void
+_initialize_amd64_gnu_tdep ()
+{
+ gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
+ GDB_OSABI_HURD, amd64_gnu_init_abi);
+}
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index e7fdeca..6457285 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -8090,11 +8090,18 @@ disable_breakpoints_in_shlibs (program_space *pspace)
/* Disable any breakpoints and tracepoints that are in SOLIB upon
notification of unloaded_shlib. Only apply to enabled breakpoints,
- disabled ones can just stay disabled. */
+ disabled ones can just stay disabled.
+
+ When STILL_IN_USE is true, SOLIB hasn't really been unmapped from
+ the inferior. In this case, don't disable anything. */
static void
-disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib)
+disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib,
+ bool still_in_use)
{
+ if (still_in_use)
+ return;
+
bool disabled_shlib_breaks = false;
for (bp_location *loc : all_bp_locations ())
diff --git a/gdb/bsd-uthread.c b/gdb/bsd-uthread.c
index eb1ed42..67db0ca 100644
--- a/gdb/bsd-uthread.c
+++ b/gdb/bsd-uthread.c
@@ -294,9 +294,10 @@ bsd_uthread_solib_loaded (solib &so)
}
static void
-bsd_uthread_solib_unloaded (program_space *pspace, const solib &so)
+bsd_uthread_solib_unloaded (program_space *pspace, const solib &so,
+ bool still_in_use)
{
- if (bsd_uthread_solib_name.empty ())
+ if (bsd_uthread_solib_name.empty () || still_in_use)
return;
if (so.so_original_name == bsd_uthread_solib_name)
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index c5eab2f..e5de702 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -2912,14 +2912,20 @@ parse_cli_var_color (const char **args)
if (len != 7)
error_no_arg (_("invalid RGB hex triplet format"));
+ uint32_t rgb;
uint8_t r, g, b;
int scanned_chars = 0;
- int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n",
- &r, &g, &b, &scanned_chars);
+ int parsed_args = sscanf (*args, "#%6" SCNx32 "%n",
+ &rgb, &scanned_chars);
- if (parsed_args != 3 || scanned_chars != 7)
+ if (parsed_args != 1 || scanned_chars != 7)
error_no_arg (_("invalid RGB hex triplet format"));
+ gdb_assert ((rgb >> 24) == 0);
+ r = (rgb >> 16) & 0xff;
+ g = (rgb >> 8) & 0xff;
+ b = rgb & 0xff;
+
*args += len;
return ui_file_style::color (r, g, b);
}
diff --git a/gdb/config/i386/nm-i386gnu.h b/gdb/config/i386/nm-x86-gnu.h
index d2d5de8..ed4d172 100644
--- a/gdb/config/i386/nm-i386gnu.h
+++ b/gdb/config/i386/nm-x86-gnu.h
@@ -22,9 +22,16 @@
/* Thread flavors used in re-setting the T bit. */
#define THREAD_STATE_FLAVOR i386_REGS_SEGS_STATE
#define THREAD_STATE_SIZE i386_THREAD_STATE_COUNT
+#ifdef __x86_64__
+#define THREAD_STATE_SET_TRACED(state) \
+ ((struct i386_thread_state *) (state))->rfl |= 0x100
+#define THREAD_STATE_CLEAR_TRACED(state) \
+ ((((struct i386_thread_state *) (state))->rfl &= ~0x100), 1)
+#else
#define THREAD_STATE_SET_TRACED(state) \
((struct i386_thread_state *) (state))->efl |= 0x100
#define THREAD_STATE_CLEAR_TRACED(state) \
((((struct i386_thread_state *) (state))->efl &= ~0x100), 1)
+#endif /* __x86_64__ */
#endif /* CONFIG_I386_NM_I386GNU_H */
diff --git a/gdb/config/i386/i386gnu.mn b/gdb/config/i386/x86-gnu.mn
index 24c58b0..24c58b0 100644
--- a/gdb/config/i386/i386gnu.mn
+++ b/gdb/config/i386/x86-gnu.mn
diff --git a/gdb/configure.host b/gdb/configure.host
index 22855cd..fdd6519 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -183,6 +183,7 @@ x86_64-*-mingw*) gdb_host=mingw64
gdb_host_obs=mingw-hdep.o
;;
x86_64-*-cygwin*) gdb_host=cygwin64 ;;
+x86_64-*-gnu*) gdb_host=gnu64 ;;
m32r*-*-linux*) gdb_host=linux ;;
xtensa*-*-linux*) gdb_host=linux ;;
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 9e78091..7b38962 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -211,23 +211,44 @@ case ${gdb_host} in
;;
esac
;;
+ gnu64)
+ case ${gdb_host_cpu} in
+ i386)
+ # Host: x86_64 running the GNU Hurd
+ NATDEPFILES='x86-gnu-nat.o gnu-nat.o \
+ x86-nat.o nat/x86-dregs.o \
+ amd64-nat.o fork-child.o \
+ nat/fork-inferior.o \
+ notify_S.o process_reply_S.o msg_reply_S.o \
+ msg_U.o exc_request_U.o exc_request_S.o'
+ HAVE_NATIVE_GCORE_HOST=1
+
+ NAT_FILE='nm-x86-gnu.h'
+ MH_CFLAGS='-D_GNU_SOURCE'
+
+ XM_CLIBS='-lshouldbeinlibc'
+
+ nat_makefile_frag="${srcdir}/config/${gdb_host_cpu}/x86-gnu.mn"
+ ;;
+ esac
+ ;;
i386gnu)
case ${gdb_host_cpu} in
i386)
# Host: Intel 386 running the GNU Hurd
- NATDEPFILES='i386-gnu-nat.o gnu-nat.o \
+ NATDEPFILES='x86-gnu-nat.o gnu-nat.o \
x86-nat.o nat/x86-dregs.o fork-child.o \
nat/fork-inferior.o \
notify_S.o process_reply_S.o msg_reply_S.o \
msg_U.o exc_request_U.o exc_request_S.o'
HAVE_NATIVE_GCORE_HOST=1
- NAT_FILE='nm-i386gnu.h'
+ NAT_FILE='nm-x86-gnu.h'
MH_CFLAGS='-D_GNU_SOURCE'
XM_CLIBS='-lshouldbeinlibc'
- nat_makefile_frag="${srcdir}/config/${gdb_host_cpu}/i386gnu.mn"
+ nat_makefile_frag="${srcdir}/config/${gdb_host_cpu}/x86-gnu.mn"
;;
esac
;;
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index f7b9e32..18a15c0 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -327,7 +327,7 @@ i[34567]86-*-linux*)
;;
i[34567]86-*-gnu*)
# Target: Intel 386 running the GNU Hurd
- gdb_target_obs="i386-gnu-tdep.o solib-svr4.o"
+ gdb_target_obs="i386-gnu-tdep.o glibc-tdep.o solib-svr4.o"
;;
i[34567]86-*-cygwin*)
# Target: Intel 386 running win32
@@ -734,6 +734,10 @@ x86_64-*-openbsd*)
x86_64-*-rtems*)
gdb_target_obs="${amd64_tobjs} ${i386_tobjs} i386-bsd-tdep.o"
;;
+x86_64-*-gnu*)
+ # Target: x86_64 running the GNU Hurd
+ gdb_target_obs="amd64-gnu-tdep.o glibc-tdep.o solib-svr4.o"
+ ;;
xtensa*-*-*linux*)
# Target: GNU/Linux Xtensa
gdb_target_obs="xtensa-linux-tdep.o symfile-mem.o linux-tdep.o"
diff --git a/gdb/corelow.c b/gdb/corelow.c
index ee57a9c..59c1667 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -1188,11 +1188,11 @@ core_target_open (const char *arg, int from_tty)
}
else
{
- gdb::unique_xmalloc_ptr<char> failing_command = make_unique_xstrdup
- (bfd_core_file_failing_command (current_program_space->core_bfd ()));
+ const char *failing_command
+ = bfd_core_file_failing_command (current_program_space->core_bfd ());
if (failing_command != nullptr)
gdb_printf (_("Core was generated by `%s'.\n"),
- failing_command.get ());
+ failing_command);
}
/* Clearing any previous state of convenience variables. */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b37266b..88b6c68 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22976,16 +22976,48 @@ The DWARF specification documents an optional index section called
section. However, in order to work with @value{GDBN}, some extensions
were necessary.
-@value{GDBN} uses the augmentation string @samp{GDB2}. Earlier
-versions used the string @samp{GDB}, but these versions of the index
-are no longer supported.
+@value{GDBN} uses an augmentation string to specify which extensions
+are in use and to allow support of backwards-incompatible changes in
+this functionality. The augmentation string has the form
+@samp{GDB@var{n}}, where @var{n} is an integral version number of the
+extensions, which is incremented when the extensions are added or
+modified. The smallest @var{n} is 2; earlier versions of
+@value{GDBN} used just @samp{GDB} with no version number, but these
+versions of the index are no longer supported.
+
+Here is a list of augmentation string versions along with the changes
+introduced with each version, compared to the previous version.
+
+@table @samp
+
+@item GDB2
+Specifies the use of attributes @code{DW_IDX_GNU_internal},
+@code{DW_IDX_GNU_main}, @code{DW_IDX_GNU_language} and
+@code{DW_IDX_GNU_linkage_name}, described below.
+
+@item GDB3
+Changes the semantic of the @code{DW_IDX_parent} attribute.
+With @samp{GDB2}, @code{DW_IDX_parent} provided an offset into the name
+table. With @samp{GDB3}, it now provides an offset to the index entry
+of the parent, relative to the start of the entry pool region.
+
+@end table
+
+@value{GDBN} produces indexes with the augmentation string @samp{GDB3}.
+
+@value{GDBN} can read indexes with augmentation strings @samp{GDB2} or
+@samp{GDB3}. @value{GDBN} does not support reading indexes with any
+other augmentation strings.
@value{GDBN} does not use the specified hash table. Therefore,
because this hash table is optional, @value{GDBN} also does not write
it.
-@value{GDBN} also generates and uses some extra index attributes:
+@value{GDBN} generates and uses the following non-standard index
+attributes:
+
@table @code
+
@item DW_IDX_GNU_internal
This has the value @samp{0x2000}. It is a flag that, when set,
indicates that the associated entry has @code{static} linkage.
@@ -23002,6 +23034,7 @@ indicating the language of the associated entry.
This has the value @samp{0x2004}. It is a flag that, when set,
indicates that the associated entry is a linkage name, and not a
source name.
+
@end table
@node Symbol Errors
@@ -32253,12 +32286,22 @@ to this library.
@item =library-unloaded,...
Reports that a library was unloaded by the program. This notification
-has 3 fields---@var{id}, @var{target-name} and @var{host-name} with
-the same meaning as for the @code{=library-loaded} notification.
+has the following fields---@var{id}, @var{target-name},
+@var{host-name} and @var{ranges} with the same meaning as for the
+@code{=library-loaded} notification.
+
+It is possible that a library can appear multiple times in an
+inferior's library list, but the library is only mapped once into the
+inferior's address space. When this happens, and one copy of the
+library is unloaded, but there are remaining copies, the
+@var{still-in-use} field will be @samp{true}. In all other
+situations, the @var{still-in-use} field will have the value
+@samp{false}.
+
The @var{thread-group} field, if present, specifies the id of the
-thread group in whose context the library was unloaded. If the field is
-absent, it means the library was unloaded in the context of all present
-thread groups.
+thread group in whose context the library was unloaded. If the field
+is absent, it means the library was unloaded in the context of all
+present thread groups.
@item =traceframe-changed,num=@var{tfnum},tracepoint=@var{tpnum}
@itemx =traceframe-changed,end
diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c
index 2d9f6e9..896f587 100644
--- a/gdb/dwarf2/cooked-index.c
+++ b/gdb/dwarf2/cooked-index.c
@@ -647,11 +647,12 @@ cooked_index::wait (cooked_state desired_state, bool allow_quit)
}
void
-cooked_index::set_contents (vec_type &&vec, deferred_warnings *warn,
+cooked_index::set_contents (std::vector<cooked_index_shard_up> &&shards,
+ deferred_warnings *warn,
const parent_map_map *parent_maps)
{
- gdb_assert (m_vector.empty ());
- m_vector = std::move (vec);
+ gdb_assert (m_shards.empty ());
+ m_shards = std::move (shards);
m_state->set (cooked_state::MAIN_AVAILABLE);
@@ -667,10 +668,10 @@ cooked_index::set_contents (vec_type &&vec, deferred_warnings *warn,
m_state->set (cooked_state::CACHE_DONE);
});
- for (auto &idx : m_vector)
+ for (auto &shard : m_shards)
{
- auto this_index = idx.get ();
- finalizers.add_task ([=] () { this_index->finalize (parent_maps); });
+ auto this_shard = shard.get ();
+ finalizers.add_task ([=] () { this_shard->finalize (parent_maps); });
}
finalizers.start ();
@@ -696,9 +697,9 @@ cooked_index::lookup (unrelocated_addr addr)
{
/* Ensure that the address maps are ready. */
wait (cooked_state::MAIN_AVAILABLE, true);
- for (const auto &index : m_vector)
+ for (const auto &shard : m_shards)
{
- dwarf2_per_cu_data *result = index->lookup (addr);
+ dwarf2_per_cu_data *result = shard->lookup (addr);
if (result != nullptr)
return result;
}
@@ -713,8 +714,8 @@ cooked_index::get_addrmaps ()
/* Ensure that the address maps are ready. */
wait (cooked_state::MAIN_AVAILABLE, true);
std::vector<const addrmap *> result;
- for (const auto &index : m_vector)
- result.push_back (index->m_addrmap);
+ for (const auto &shard : m_shards)
+ result.push_back (shard->m_addrmap);
return result;
}
@@ -725,9 +726,9 @@ cooked_index::find (const std::string &name, bool completing)
{
wait (cooked_state::FINALIZED, true);
std::vector<cooked_index_shard::range> result_range;
- result_range.reserve (m_vector.size ());
- for (auto &entry : m_vector)
- result_range.push_back (entry->find (name, completing));
+ result_range.reserve (m_shards.size ());
+ for (auto &shard : m_shards)
+ result_range.push_back (shard->find (name, completing));
return range (std::move (result_range));
}
@@ -751,9 +752,9 @@ const cooked_index_entry *
cooked_index::get_main () const
{
const cooked_index_entry *best_entry = nullptr;
- for (const auto &index : m_vector)
+ for (const auto &shard : m_shards)
{
- const cooked_index_entry *entry = index->get_main ();
+ const cooked_index_entry *entry = shard->get_main ();
/* Choose the first "main" we see. We only do this for names
not requiring canonicalization. At this point in the process
names might not have been canonicalized. However, currently,
@@ -841,12 +842,15 @@ cooked_index::dump (gdbarch *arch)
std::vector<const addrmap *> addrmaps = this->get_addrmaps ();
for (i = 0; i < addrmaps.size (); ++i)
{
- const addrmap &addrmap = *addrmaps[i];
+ const addrmap *addrmap = addrmaps[i];
- gdb_printf (" [%zu] ((addrmap *) %p)\n", i, &addrmap);
+ gdb_printf (" [%zu] ((addrmap *) %p)\n", i, addrmap);
gdb_printf ("\n");
- addrmap.foreach ([arch] (CORE_ADDR start_addr, const void *obj)
+ if (addrmap == nullptr)
+ continue;
+
+ addrmap->foreach ([arch] (CORE_ADDR start_addr, const void *obj)
{
QUIT;
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index d1d81f8..b7a38b8 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -319,6 +319,9 @@ private:
found. */
dwarf2_per_cu_data *lookup (unrelocated_addr addr)
{
+ if (m_addrmap == nullptr)
+ return nullptr;
+
return (static_cast<dwarf2_per_cu_data *>
(m_addrmap->find ((CORE_ADDR) addr)));
}
@@ -364,6 +367,8 @@ private:
std::vector<gdb::unique_xmalloc_ptr<char>> m_names;
};
+using cooked_index_shard_up = std::unique_ptr<cooked_index_shard>;
+
class cutu_reader;
/* An instance of this is created when scanning DWARF to create a
@@ -621,11 +626,6 @@ protected:
class cooked_index : public dwarf_scanner_base
{
public:
-
- /* A convenience typedef for the vector that is contained in this
- object. */
- using vec_type = std::vector<std::unique_ptr<cooked_index_shard>>;
-
cooked_index (dwarf2_per_objfile *per_objfile,
std::unique_ptr<cooked_index_worker> &&worker);
~cooked_index () override;
@@ -633,7 +633,7 @@ public:
DISABLE_COPY_AND_ASSIGN (cooked_index);
/* Start reading the DWARF. */
- void start_reading ();
+ void start_reading () override;
/* Called by cooked_index_worker to set the contents of this index
and transition to the MAIN_AVAILABLE state. WARN is used to
@@ -641,7 +641,8 @@ public:
PARENT_MAPS is used when resolving pending parent links.
PARENT_MAPS may be NULL if there are no IS_PARENT_DEFERRED
entries in VEC. */
- void set_contents (vec_type &&vec, deferred_warnings *warn,
+ void set_contents (std::vector<cooked_index_shard_up> &&vec,
+ deferred_warnings *warn,
const parent_map_map *parent_maps);
/* A range over a vector of subranges. */
@@ -657,9 +658,9 @@ public:
{
wait (cooked_state::FINALIZED, true);
std::vector<cooked_index_shard::range> result_range;
- result_range.reserve (m_vector.size ());
- for (auto &entry : m_vector)
- result_range.push_back (entry->all_entries ());
+ result_range.reserve (m_shards.size ());
+ for (auto &shard : m_shards)
+ result_range.push_back (shard->all_entries ());
return range (std::move (result_range));
}
@@ -669,7 +670,9 @@ public:
dwarf2_per_cu_data *lookup (unrelocated_addr addr) override;
/* Return a new vector of all the addrmaps used by all the indexes
- held by this object. */
+ held by this object.
+
+ Elements of the vector may be nullptr. */
std::vector<const addrmap *> get_addrmaps ();
/* Return the entry that is believed to represent the program's
@@ -707,7 +710,7 @@ private:
/* The vector of cooked_index objects. This is stored because the
entries are stored on the obstacks in those objects. */
- vec_type m_vector;
+ std::vector<cooked_index_shard_up> m_shards;
/* This tracks the current state. When this is nullptr, it means
that the state is CACHE_DONE -- it's important to note that only
diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c
index e82d38a..f36f388 100644
--- a/gdb/dwarf2/index-write.c
+++ b/gdb/dwarf2/index-write.c
@@ -130,6 +130,15 @@ public:
::store_unsigned_integer (grow (len), len, byte_order, val);
}
+ /* Accept a host-format integer in VAL and write it in the buffer at offset
+ OFFSET as a target-format integer which is LEN bytes long. */
+ void write_uint (size_t offset, size_t len, bfd_endian byte_order,
+ ULONGEST val)
+ {
+ gdb_assert (offset + len <= m_vec.size ());
+ ::store_unsigned_integer (&m_vec[offset], len, byte_order, val);
+ }
+
/* Copy VALUE to the end of the buffer, little-endian. */
void append_offset (offset_type value)
{
@@ -675,10 +684,7 @@ public:
if (entry->lang == language_ada && entry->tag == DW_TAG_namespace)
return;
- const auto insertpair
- = m_name_to_value_set.try_emplace (c_str_view (entry->name));
- entry_list &elist = insertpair.first->second;
- elist.entries.push_back (entry);
+ m_name_to_value_set[entry->name].emplace_back (entry);
}
/* Build all the tables. All symbols must be already inserted.
@@ -692,25 +698,16 @@ public:
m_name_table_string_offs.reserve (name_count);
m_name_table_entry_offs.reserve (name_count);
- /* The name table is indexed from 1. The numbers are needed here
- so that parent entries can be handled correctly. */
- int next_name = 1;
- for (auto &item : m_name_to_value_set)
- item.second.index = next_name++;
-
/* The next available abbrev number. */
int next_abbrev = 1;
- for (auto &item : m_name_to_value_set)
+ for (auto &[name, these_entries] : m_name_to_value_set)
{
- const c_str_view &name = item.first;
- entry_list &these_entries = item.second;
-
/* Sort the items within each bucket. This ensures that the
generated index files will be the same no matter the order in
which symbols were added into the index. */
- std::sort (these_entries.entries.begin (),
- these_entries.entries.end (),
+ std::sort (these_entries.begin (),
+ these_entries.end (),
[] (const cooked_index_entry *a,
const cooked_index_entry *b)
{
@@ -730,7 +727,7 @@ public:
(m_debugstrlookup.lookup (name.c_str ())); /* ??? */
m_name_table_entry_offs.push_back_reorder (m_entry_pool.size ());
- for (const cooked_index_entry *entry : these_entries.entries)
+ for (const cooked_index_entry *entry : these_entries)
{
unit_kind kind = (entry->per_cu->is_debug_types
? unit_kind::tu
@@ -778,7 +775,7 @@ public:
if (parent != nullptr)
{
m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent);
- m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+ m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4);
}
/* Terminate attributes list. */
@@ -786,6 +783,14 @@ public:
m_abbrev_table.append_unsigned_leb128 (0);
}
+ /* Record the offset in the pool at which this entry will
+ reside. */
+ const auto offset_inserted
+ = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ())
+ .second);
+ gdb_assert (offset_inserted);
+
+ /* Write the entry to the pool. */
m_entry_pool.append_unsigned_leb128 (idx);
const auto it = m_cu_index_htab.find (entry->per_cu);
@@ -800,11 +805,11 @@ public:
if (parent != nullptr)
{
- c_str_view par_name (parent->name);
- auto name_iter = m_name_to_value_set.find (par_name);
- gdb_assert (name_iter != m_name_to_value_set.end ());
- gdb_assert (name_iter->second.index != 0);
- m_entry_pool.append_unsigned_leb128 (name_iter->second.index);
+ m_offsets_to_patch.emplace_back (m_entry_pool.size (), parent);
+
+ /* Write a dummy number, this gets patched later. */
+ m_entry_pool.append_uint (4, m_dwarf5_byte_order,
+ 0xfafafafa);
}
}
@@ -814,6 +819,15 @@ public:
/* Terminate tags list. */
m_abbrev_table.append_unsigned_leb128 (0);
+
+ /* Write the parent offset values. */
+ for (const auto &[reloc_offset, parent] : m_offsets_to_patch)
+ {
+ const auto parent_offset_it = m_entry_pool_offsets.find (parent);
+ gdb_assert (parent_offset_it != m_entry_pool_offsets.cend ());
+ m_entry_pool.write_uint (reloc_offset, 4, m_dwarf5_byte_order,
+ parent_offset_it->second);
+ }
}
/* Return .debug_names names count. This must be called only after
@@ -1059,15 +1073,26 @@ private:
offset_vec_tmpl<OffsetSize> m_name_table_entry_offs;
};
- struct entry_list
- {
- unsigned index = 0;
- std::vector<const cooked_index_entry *> entries;
- };
+ /* Store the index entries for each name.
+
+ Note that we rely on the sorting behavior of map to make the output
+ stable. */
+ std::map<c_str_view, std::vector<const cooked_index_entry *>>
+ m_name_to_value_set;
+
+ /* Offset at which each entry is written in the entry pool. */
+ gdb::unordered_map<const cooked_index_entry *, offset_type>
+ m_entry_pool_offsets;
+
+ /* The locations where we need to patch offset to entries.
+
+ The first element of the pair is the offset into the pool that needs to
+ be patched.
- /* Store value of each symbol. Note that we rely on the sorting
- behavior of map to make the output stable. */
- std::map<c_str_view, entry_list> m_name_to_value_set;
+ The second element is the entry the offset to which needs to be
+ patched in. */
+ std::vector<std::pair<offset_type, const cooked_index_entry *>>
+ m_offsets_to_patch;
const bfd_endian m_dwarf5_byte_order;
dwarf_tmpl<uint32_t> m_dwarf32;
@@ -1333,7 +1358,8 @@ write_gdbindex (dwarf2_per_bfd *per_bfd, cooked_index *table,
/* Dump the address map. */
data_buf addr_vec;
for (auto map : table->get_addrmaps ())
- write_address_map (map, addr_vec, cu_index_htab);
+ if (map != nullptr)
+ write_address_map (map, addr_vec, cu_index_htab);
/* Ensure symbol hash is built domestically. */
symtab.sort ();
@@ -1406,7 +1432,7 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table,
const offset_type bytes_of_header
= ((dwarf5_is_dwarf64 ? 12 : 4)
+ 2 + 2 + 7 * 4
- + sizeof (dwarf5_augmentation));
+ + sizeof (dwarf5_augmentation_3));
size_t expected_bytes = 0;
expected_bytes += bytes_of_header;
expected_bytes += cu_list.size ();
@@ -1458,9 +1484,9 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table,
/* augmentation_string_size - The size in bytes of the augmentation
string. This value is rounded up to a multiple of 4. */
- static_assert (sizeof (dwarf5_augmentation) % 4 == 0);
- header.append_uint (4, dwarf5_byte_order, sizeof (dwarf5_augmentation));
- header.append_array (dwarf5_augmentation);
+ static_assert (sizeof (dwarf5_augmentation_3) % 4 == 0);
+ header.append_uint (4, dwarf5_byte_order, sizeof (dwarf5_augmentation_3));
+ header.append_array (dwarf5_augmentation_3);
gdb_assert (header.size () == bytes_of_header);
diff --git a/gdb/dwarf2/mapped-index.h b/gdb/dwarf2/mapped-index.h
index 8bc81b4..b32fe6a 100644
--- a/gdb/dwarf2/mapped-index.h
+++ b/gdb/dwarf2/mapped-index.h
@@ -32,6 +32,11 @@ struct dwarf_scanner_base
virtual ~dwarf_scanner_base () = default;
DISABLE_COPY_AND_ASSIGN (dwarf_scanner_base);
+ /* Start the reading. This is only really relevant to the cooked
+ index; see cooked-index.h. */
+ virtual void start_reading ()
+ { }
+
/* Return a quick_symbol_functions instance that refers back to this
dwarf_scanner_base. */
virtual quick_symbol_functions_up make_quick_functions () const = 0;
diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c
index ffc4f3a..fe31a58 100644
--- a/gdb/dwarf2/read-debug-names.c
+++ b/gdb/dwarf2/read-debug-names.c
@@ -28,6 +28,7 @@
#include "read.h"
#include "stringify.h"
#include "extract-store-integer.h"
+#include "gdbsupport/thread-pool.h"
/* This is just like cooked_index_functions, but overrides a single
method so the test suite can distinguish the .debug_names case from
@@ -74,7 +75,15 @@ struct mapped_debug_names_reader
bfd *abfd = nullptr;
bfd_endian dwarf5_byte_order {};
bool dwarf5_is_dwarf64 = false;
+
+ /* True if the augmentation string indicates the index was produced by
+ GDB. */
bool augmentation_is_gdb = false;
+
+ /* If AUGMENTATION_IS_GDB is true, this indicates the version. Otherwise,
+ this value is meaningless. */
+ unsigned int gdb_augmentation_version = 0;
+
uint8_t offset_size = 0;
uint32_t cu_count = 0;
uint32_t tu_count = 0, bucket_count = 0, name_count = 0;
@@ -105,8 +114,33 @@ struct mapped_debug_names_reader
std::unordered_map<ULONGEST, index_val> abbrev_map;
- std::unique_ptr<cooked_index_shard> shard;
+ /* Even though the scanning of .debug_names and creation of the cooked index
+ entries is done serially, we create multiple shards so that the
+ finalization step can be parallelized. The shards are filled in a round
+ robin fashion. */
+ std::vector<cooked_index_shard_up> shards;
+
+ /* Next shard to insert an entry in. */
+ int next_shard = 0;
+
+ /* Maps entry pool offsets to cooked index entries. */
+ gdb::unordered_map<ULONGEST, cooked_index_entry *>
+ entry_pool_offsets_to_entries;
+
+ /* Cooked index entries for which the parent needs to be resolved.
+
+ The second value of the pair is the DW_IDX_parent value. Its meaning
+ depends on the augmentation string:
+
+ - GDB2: an index in the name table
+ - GDB3: an offset offset into the entry pool */
std::vector<std::pair<cooked_index_entry *, ULONGEST>> needs_parent;
+
+ /* All the cooked index entries created, in the same order and groups as
+ listed in the name table.
+
+ The size of the outer vector is equal to the number of entries in the name
+ table (NAME_COUNT). */
std::vector<std::vector<cooked_index_entry *>> all_entries;
};
@@ -121,6 +155,7 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
std::optional<ULONGEST> &parent)
{
unsigned int bytes_read;
+ const auto offset_in_entry_pool = entry - entry_pool;
const ULONGEST abbrev = read_unsigned_leb128 (abfd, entry, &bytes_read);
entry += bytes_read;
if (abbrev == 0)
@@ -239,8 +274,18 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
/* Skip if we couldn't find a valid CU/TU index. */
if (per_cu != nullptr)
- *result = shard->add (die_offset, (dwarf_tag) indexval.dwarf_tag, flags,
- lang, name, nullptr, per_cu);
+ {
+ *result
+ = shards[next_shard]->add (die_offset, (dwarf_tag) indexval.dwarf_tag,
+ flags, lang, name, nullptr, per_cu);
+
+ ++next_shard;
+ if (next_shard == shards.size ())
+ next_shard = 0;
+
+ entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result);
+ }
+
return entry;
}
@@ -296,19 +341,43 @@ mapped_debug_names_reader::scan_all_names ()
scan_entries (i, name, entry);
}
- /* Now update the parent pointers for all entries. This has to be
- done in a funny way because DWARF specifies the parent entry to
- point to a name -- but we don't know which specific one. */
- for (auto [entry, parent_idx] : needs_parent)
+ /* Resolve the parent pointers for all entries that have a parent.
+
+ If the augmentation string is "GDB2", the DW_IDX_parent value is an index
+ into the name table. Since there may be multiple index entries associated
+ to that name, we have a little heuristic to figure out which is the right
+ one.
+
+ Otherwise, the DW_IDX_parent value is an offset into the entry pool, which
+ is not ambiguous. */
+ for (auto &[entry, parent_val] : needs_parent)
{
- /* Name entries are indexed from 1 in DWARF. */
- std::vector<cooked_index_entry *> &entries = all_entries[parent_idx - 1];
- for (const auto &parent : entries)
- if (parent->lang == entry->lang)
- {
- entry->set_parent (parent);
- break;
- }
+ if (augmentation_is_gdb && gdb_augmentation_version == 2)
+ {
+ /* Name entries are indexed from 1 in DWARF. */
+ std::vector<cooked_index_entry *> &entries
+ = all_entries[parent_val - 1];
+
+ for (const auto &parent : entries)
+ if (parent->lang == entry->lang)
+ {
+ entry->set_parent (parent);
+ break;
+ }
+ }
+ else
+ {
+ const auto parent_it
+ = entry_pool_offsets_to_entries.find (parent_val);
+
+ if (parent_it == entry_pool_offsets_to_entries.cend ())
+ {
+ complaint (_ ("Parent entry not found for .debug_names entry"));
+ continue;
+ }
+
+ entry->set_parent (parent_it->second);
+ }
}
}
@@ -348,14 +417,13 @@ cooked_index_debug_names::do_reading ()
complaint_handler.release (),
std::move (exceptions),
parent_map ());
- std::vector<std::unique_ptr<cooked_index_shard>> indexes;
- indexes.push_back (std::move (m_map.shard));
cooked_index *table
= (gdb::checked_static_cast<cooked_index *>
(per_bfd->index_table.get ()));
+
/* Note that this code never uses IS_PARENT_DEFERRED, so it is safe
to pass nullptr here. */
- table->set_contents (std::move (indexes), &m_warnings, nullptr);
+ table->set_contents (std::move (m_map.shards), &m_warnings, nullptr);
bfd_thread_cleanup ();
}
@@ -404,16 +472,6 @@ check_signatured_type_table_from_debug_names
/* DWARF-5 debug_names reader. */
-/* The old, no-longer-supported GDB augmentation. */
-static const gdb_byte old_gdb_augmentation[]
- = { 'G', 'D', 'B', 0 };
-static_assert (sizeof (old_gdb_augmentation) % 4 == 0);
-
-/* DWARF-5 augmentation string for GDB's DW_IDX_GNU_* extension. This
- must have a size that is a multiple of 4. */
-const gdb_byte dwarf5_augmentation[8] = { 'G', 'D', 'B', '2', 0, 0, 0, 0 };
-static_assert (sizeof (dwarf5_augmentation) % 4 == 0);
-
/* A helper function that reads the .debug_names section in SECTION
and fills in MAP. FILENAME is the name of the file containing the
section; it is used for error reporting.
@@ -525,18 +583,24 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
addr += 4;
augmentation_string_size += (-augmentation_string_size) & 3;
- if (augmentation_string_size == sizeof (old_gdb_augmentation)
- && memcmp (addr, old_gdb_augmentation,
- sizeof (old_gdb_augmentation)) == 0)
+ const auto augmentation_string
+ = gdb::make_array_view (addr, augmentation_string_size);
+
+ if (augmentation_string == gdb::make_array_view (dwarf5_augmentation_1))
{
warning (_(".debug_names created by an old version of gdb; ignoring"));
return false;
}
-
- map.augmentation_is_gdb = ((augmentation_string_size
- == sizeof (dwarf5_augmentation))
- && memcmp (addr, dwarf5_augmentation,
- sizeof (dwarf5_augmentation)) == 0);
+ else if (augmentation_string == gdb::make_array_view (dwarf5_augmentation_2))
+ {
+ map.augmentation_is_gdb = true;
+ map.gdb_augmentation_version = 2;
+ }
+ else if (augmentation_string == gdb::make_array_view (dwarf5_augmentation_3))
+ {
+ map.augmentation_is_gdb = true;
+ map.gdb_augmentation_version = 3;
+ }
if (!map.augmentation_is_gdb)
{
@@ -544,6 +608,7 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
return false;
}
+ /* Skip past augmentation string. */
addr += augmentation_string_size;
/* List of CUs */
@@ -766,16 +831,24 @@ do_dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
&addrmap, &warnings);
warnings.emit ();
- map.shard = std::make_unique<cooked_index_shard> ();
- map.shard->install_addrmap (&addrmap);
+ const auto n_workers
+ = std::max<std::size_t> (gdb::thread_pool::g_thread_pool->thread_count (),
+ 1);
+
+ /* Create as many index shard as there are worker threads. */
+ for (int i = 0; i < n_workers; ++i)
+ map.shards.emplace_back (std::make_unique<cooked_index_shard> ());
- cooked_index *idx
- = new debug_names_index (per_objfile,
- (std::make_unique<cooked_index_debug_names>
- (per_objfile, std::move (map))));
- per_bfd->index_table.reset (idx);
+ /* There is a single address map for the whole index (coming from
+ .debug_aranges). We only need to install it into a single shard for it to
+ get searched by cooked_index. */
+ map.shards[0]->install_addrmap (&addrmap);
- idx->start_reading ();
+ auto cidn = (std::make_unique<cooked_index_debug_names>
+ (per_objfile, std::move (map)));
+ auto idx = std::make_unique<debug_names_index> (per_objfile,
+ std::move (cidn));
+ per_bfd->start_reading (std::move (idx));
return true;
}
diff --git a/gdb/dwarf2/read-debug-names.h b/gdb/dwarf2/read-debug-names.h
index 729fe89..ef91a70 100644
--- a/gdb/dwarf2/read-debug-names.h
+++ b/gdb/dwarf2/read-debug-names.h
@@ -22,7 +22,30 @@
struct dwarf2_per_objfile;
-extern const gdb_byte dwarf5_augmentation[8];
+/* DWARF-5 augmentation strings.
+
+ They must have a size that is a multiple of 4.
+
+ "GDB" is the old, no-longer-supported GDB augmentation.
+
+ The "GDB2" augmentation string specifies the use of the DW_IDX_GNU_*
+ attributes.
+
+ The meaning of the "GDB3" augmentation string is identical to "GDB2", except
+ for the meaning of DW_IDX_parent. With "GDB2", DW_IDX_parent represented an
+ index in the name table. With "GDB3", it represents an offset into the entry
+ pool. */
+
+constexpr gdb_byte dwarf5_augmentation_1[4] = { 'G', 'D', 'B', 0 };
+static_assert (sizeof (dwarf5_augmentation_1) % 4 == 0);
+
+constexpr gdb_byte dwarf5_augmentation_2[8]
+ = { 'G', 'D', 'B', '2', 0, 0, 0, 0 };
+static_assert (sizeof (dwarf5_augmentation_2) % 4 == 0);
+
+constexpr gdb_byte dwarf5_augmentation_3[8]
+ = { 'G', 'D', 'B', '3', 0, 0, 0, 0 };
+static_assert (sizeof (dwarf5_augmentation_3) % 4 == 0);
/* Read .debug_names. If everything went ok, initialize the "quick"
elements of all the CUs and return true. Otherwise, return false. */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 70e9022..be33bea 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1608,6 +1608,16 @@ dwarf2_per_bfd::map_info_sections (struct objfile *objfile)
section.read (objfile);
}
+/* See dwarf2/read.h. */
+
+void
+dwarf2_per_bfd::start_reading (std::unique_ptr<dwarf_scanner_base> new_table)
+{
+ gdb_assert (index_table == nullptr);
+ index_table = std::move (new_table);
+ index_table->start_reading ();
+}
+
/* DWARF quick_symbol_functions support. */
@@ -16340,12 +16350,9 @@ start_debug_info_reader (dwarf2_per_objfile *per_objfile)
scanning; and then start the scanning. */
dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
std::unique_ptr<cooked_index_worker> worker
- (new cooked_index_debug_info (per_objfile));
- cooked_index *idx = new cooked_index (per_objfile, std::move (worker));
- per_bfd->index_table.reset (idx);
- /* Don't start reading until after 'index_table' is set. This
- avoids races. */
- idx->start_reading ();
+ = std::make_unique<cooked_index_debug_info> (per_objfile);
+ per_bfd->start_reading (std::make_unique<cooked_index> (per_objfile,
+ std::move (worker)));
}
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 12d5f06..ebea8b7 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -451,6 +451,10 @@ struct dwarf2_per_bfd
.debug_info. */
void map_info_sections (struct objfile *objfile);
+ /* Set the 'index_table' member and then call start_reading on
+ it. */
+ void start_reading (std::unique_ptr<dwarf_scanner_base> new_table);
+
private:
/* This function is mapped across the sections and remembers the
offset and size of each of the debugging sections we are
diff --git a/gdb/i386-gnu-tdep.c b/gdb/i386-gnu-tdep.c
index 98e1151..5ffc138 100644
--- a/gdb/i386-gnu-tdep.c
+++ b/gdb/i386-gnu-tdep.c
@@ -21,6 +21,7 @@
#include "osabi.h"
#include "solib-svr4.h"
+#include "glibc-tdep.h"
#include "i386-tdep.h"
/* Recognizing signal handler frames. */
@@ -72,8 +73,7 @@ i386_gnu_sigtramp_start (const frame_info_ptr &this_frame)
return pc;
}
-/* Return whether THIS_FRAME corresponds to a GNU/Linux sigtramp
- routine. */
+/* Return whether THIS_FRAME corresponds to a Hurd sigtramp routine. */
static int
i386_gnu_sigtramp_p (const frame_info_ptr &this_frame)
@@ -178,9 +178,18 @@ i386gnu_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* GNU uses ELF. */
i386_elf_init_abi (info, 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);
+ /* Hurd uses the dynamic linker included in the GNU C Library. */
+ set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
+
+ /* Enable TLS support. */
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
+
tdep->gregset_reg_offset = i386gnu_gregset_reg_offset;
tdep->gregset_num_regs = ARRAY_SIZE (i386gnu_gregset_reg_offset);
tdep->sizeof_gregset = 19 * 4;
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index b6b21a4..00703e4 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -517,12 +517,6 @@ run_command (const char *args, int from_tty)
static void
start_command (const char *args, int from_tty)
{
- /* Some languages such as Ada need to search inside the program
- minimal symbols for the location where to put the temporary
- breakpoint before starting. */
- if (!have_minimal_symbols (current_program_space))
- error (_("No symbol table loaded. Use the \"file\" command."));
-
/* Run the program until reaching the main procedure... */
run_command_1 (args, from_tty, RUN_STOP_AT_MAIN);
}
diff --git a/gdb/interps.c b/gdb/interps.c
index bd65d1a..8b8b578 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -496,9 +496,9 @@ interps_notify_solib_loaded (const solib &so)
/* See interps.h. */
void
-interps_notify_solib_unloaded (const solib &so)
+interps_notify_solib_unloaded (const solib &so, bool still_in_use)
{
- interps_notify (&interp::on_solib_unloaded, so);
+ interps_notify (&interp::on_solib_unloaded, so, still_in_use);
}
/* See interps.h. */
diff --git a/gdb/interps.h b/gdb/interps.h
index 987465c..2dcf244 100644
--- a/gdb/interps.h
+++ b/gdb/interps.h
@@ -153,8 +153,11 @@ public:
/* Notify the interpreter that solib SO has been loaded. */
virtual void on_solib_loaded (const solib &so) {}
- /* Notify the interpreter that solib SO has been unloaded. */
- virtual void on_solib_unloaded (const solib &so) {}
+ /* Notify the interpreter that solib SO has been unloaded. When
+ STILL_IN_USE is true, the objfile backing SO is still in use,
+ this indicates that SO was loaded multiple times, but only mapped
+ in once (the mapping was reused). */
+ virtual void on_solib_unloaded (const solib &so, bool still_in_use) {}
/* Notify the interpreter that a command it is executing is about to cause
the inferior to proceed. */
@@ -332,8 +335,11 @@ extern void interps_notify_target_resumed (ptid_t ptid);
/* Notify all interpreters that solib SO has been loaded. */
extern void interps_notify_solib_loaded (const solib &so);
-/* Notify all interpreters that solib SO has been unloaded. */
-extern void interps_notify_solib_unloaded (const solib &so);
+/* Notify all interpreters that solib SO has been unloaded. When
+ STILL_IN_USE is true, the objfile backing SO is still in use, this
+ indicates that SO was loaded multiple times, but only mapped in
+ once (the mapping was reused). */
+extern void interps_notify_solib_unloaded (const solib &so, bool still_in_use);
/* Notify all interpreters that the selected traceframe changed.
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index 1cab29b..00c72d1 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -351,11 +351,13 @@ loongarch_deal_with_atomic_sequence (struct regcache *regcache, CORE_ADDR cur_pc
{
return {};
}
- /* Look for a conditional branch instruction, put a breakpoint in its destination address. */
+ /* Look for a conditional branch instruction, put a breakpoint in its destination address
+ which is outside of the ll/sc atomic instruction sequence. */
else if (loongarch_insn_is_cond_branch (insn))
{
next_pc = loongarch_next_pc (regcache, cur_pc);
- next_pcs.push_back (next_pc);
+ if (next_pc != cur_pc + insn_len)
+ next_pcs.push_back (next_pc);
}
/* Look for a Store Conditional instruction which closes the atomic sequence. */
else if (loongarch_insn_is_sc (insn))
diff --git a/gdb/mi/mi-cmd-break.c b/gdb/mi/mi-cmd-break.c
index 5c78acd..b44d676 100644
--- a/gdb/mi/mi-cmd-break.c
+++ b/gdb/mi/mi-cmd-break.c
@@ -20,8 +20,6 @@
#include "arch-utils.h"
#include "exceptions.h"
#include "mi-cmds.h"
-#include "ui-out.h"
-#include "mi-out.h"
#include "breakpoint.h"
#include "mi-getopt.h"
#include "observable.h"
@@ -30,7 +28,6 @@
#include "language.h"
#include "location.h"
#include "linespec.h"
-#include "gdbsupport/gdb_obstack.h"
#include <ctype.h>
#include "tracepoint.h"
diff --git a/gdb/mi/mi-cmd-disas.c b/gdb/mi/mi-cmd-disas.c
index a311e25..cbae0e2 100644
--- a/gdb/mi/mi-cmd-disas.c
+++ b/gdb/mi/mi-cmd-disas.c
@@ -19,7 +19,6 @@
#include "arch-utils.h"
#include "progspace.h"
-#include "target.h"
#include "value.h"
#include "mi-cmds.h"
#include "mi-getopt.h"
diff --git a/gdb/mi/mi-cmd-env.c b/gdb/mi/mi-cmd-env.c
index ae8c5e8..53077e9 100644
--- a/gdb/mi/mi-cmd-env.c
+++ b/gdb/mi/mi-cmd-env.c
@@ -19,11 +19,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "inferior.h"
-#include "value.h"
#include "mi-out.h"
#include "mi-cmds.h"
#include "mi-getopt.h"
-#include "symtab.h"
#include "target.h"
#include "gdbsupport/environ.h"
#include "command.h"
diff --git a/gdb/mi/mi-cmd-file.c b/gdb/mi/mi-cmd-file.c
index f2fe435..5119e21 100644
--- a/gdb/mi/mi-cmd-file.c
+++ b/gdb/mi/mi-cmd-file.c
@@ -20,13 +20,12 @@
#include "mi-cmds.h"
#include "mi-getopt.h"
#include "mi-interp.h"
+#include "progspace.h"
#include "ui-out.h"
#include "symtab.h"
#include "source.h"
-#include "objfiles.h"
#include "solib.h"
#include "solist.h"
-#include "gdbsupport/gdb_regex.h"
/* Return to the client the absolute path and line number of the
current file being executed. */
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 5e50428..05bc0ff 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -18,18 +18,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "event-top.h"
-#include "target.h"
#include "frame.h"
#include "value.h"
#include "mi-cmds.h"
#include "ui-out.h"
#include "symtab.h"
#include "block.h"
-#include "stack.h"
#include "dictionary.h"
#include "language.h"
#include "valprint.h"
-#include "utils.h"
#include "mi-getopt.h"
#include "extension.h"
#include <ctype.h>
@@ -37,7 +34,6 @@
#include <optional>
#include "gdbsupport/gdb-safe-ctype.h"
#include "inferior.h"
-#include "observable.h"
enum what_to_list { locals, arguments, all };
diff --git a/gdb/mi/mi-cmd-var.c b/gdb/mi/mi-cmd-var.c
index f311204..d889329 100644
--- a/gdb/mi/mi-cmd-var.c
+++ b/gdb/mi/mi-cmd-var.c
@@ -29,7 +29,6 @@
#include "mi-getopt.h"
#include "gdbthread.h"
#include "mi-parse.h"
-#include <optional>
#include "inferior.h"
static void varobj_update_one (struct varobj *var,
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index c7afb10..02d4dc3 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -18,7 +18,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/>. */
-#include "top.h"
#include "mi-cmds.h"
#include "mi-main.h"
#include "mi-parse.h"
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index ff4a0ff..8de3e47 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -23,7 +23,6 @@
#include "exceptions.h"
#include "interps.h"
#include "event-top.h"
-#include "gdbsupport/event-loop.h"
#include "inferior.h"
#include "infrun.h"
#include "ui-out.h"
@@ -721,15 +720,17 @@ mi_interp::on_target_resumed (ptid_t ptid)
/* See mi-interp.h. */
-void
-mi_output_solib_attribs (ui_out *uiout, const solib &solib)
+static void
+mi_output_solib_attribs_1 (ui_out *uiout, const solib &solib,
+ bool include_symbols_loaded_p)
{
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_signed ("symbols-loaded", solib.symbols_loaded);
+ if (include_symbols_loaded_p)
+ uiout->field_signed ("symbols-loaded", solib.symbols_loaded);
if (!gdbarch_has_global_solist (current_inferior ()->arch ()))
uiout->field_fmt ("thread-group", "i%d", current_inferior ()->num);
@@ -742,6 +743,14 @@ mi_output_solib_attribs (ui_out *uiout, const solib &solib)
}
}
+/* See mi-interp.h. */
+
+void
+mi_output_solib_attribs (ui_out *uiout, const solib &solib)
+{
+ mi_output_solib_attribs_1 (uiout, solib, true);
+}
+
void
mi_interp::on_solib_loaded (const solib &solib)
{
@@ -760,7 +769,7 @@ mi_interp::on_solib_loaded (const solib &solib)
}
void
-mi_interp::on_solib_unloaded (const solib &solib)
+mi_interp::on_solib_unloaded (const solib &solib, bool still_in_use)
{
ui_out *uiout = this->interp_ui_out ();
@@ -771,11 +780,9 @@ mi_interp::on_solib_unloaded (const solib &solib)
ui_out_redirect_pop redir (uiout, this->event_channel);
- 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);
- if (!gdbarch_has_global_solist (current_inferior ()->arch ()))
- uiout->field_fmt ("thread-group", "i%d", current_inferior ()->num);
+ /* Pass false here so that 'symbols-loaded' is not included. */
+ mi_output_solib_attribs_1 (uiout, solib, false);
+ uiout->field_string ("still-in-use", still_in_use ? "true" : "false");
gdb_flush (this->event_channel);
}
diff --git a/gdb/mi/mi-interp.h b/gdb/mi/mi-interp.h
index 8f5eee6..beff1c1 100644
--- a/gdb/mi/mi-interp.h
+++ b/gdb/mi/mi-interp.h
@@ -61,7 +61,7 @@ public:
const char *format) override;
void on_target_resumed (ptid_t ptid) override;
void on_solib_loaded (const solib &so) override;
- void on_solib_unloaded (const solib &so) override;
+ void on_solib_unloaded (const solib &so, bool still_in_use) override;
void on_about_to_proceed () override;
void on_traceframe_changed (int tfnum, int tpnum) override;
void on_tsv_created (const trace_state_variable *tsv) override;
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 1d0fae9..9d084c3 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -30,11 +30,9 @@
#include "mi-cmds.h"
#include "mi-parse.h"
#include "mi-getopt.h"
-#include "mi-console.h"
#include "ui-out.h"
#include "mi-out.h"
#include "interps.h"
-#include "gdbsupport/event-loop.h"
#include "event-top.h"
#include "gdbcore.h"
#include "value.h"
@@ -45,7 +43,6 @@
#include "language.h"
#include "valprint.h"
#include "osdata.h"
-#include "gdbsupport/gdb_splay_tree.h"
#include "tracepoint.h"
#include "ada-lang.h"
#include "linespec.h"
@@ -60,7 +57,6 @@
#include <chrono>
#include "progspace-and-thread.h"
#include "gdbsupport/rsp-low.h"
-#include <algorithm>
#include <set>
#include <map>
diff --git a/gdb/mi/mi-parse.c b/gdb/mi/mi-parse.c
index 8804c98..3ef9da5 100644
--- a/gdb/mi/mi-parse.c
+++ b/gdb/mi/mi-parse.c
@@ -21,7 +21,6 @@
#include "mi-cmds.h"
#include "mi-parse.h"
-#include "charset.h"
#include <ctype.h>
#include "cli/cli-utils.h"
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 41765b1..46fe897 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -27,6 +27,8 @@
#include "gdbsupport/signals-state-save-restore.h"
#include "gdbsupport/gdb_tilde_expand.h"
#include "gdbsupport/gdb_signals.h"
+#include "gdbsupport/buildargv.h"
+#include "gdbsupport/gdb_argv_vec.h"
#include <vector>
extern char **environ;
@@ -50,7 +52,7 @@ public:
strings to which the array point. */
char **argv ()
{
- return const_cast<char **> (&m_argv[0]);
+ return m_argv.argv ();
}
private:
@@ -69,66 +71,28 @@ private:
const std::string &allargs,
const char *shell_file);
- /* The argument vector built. Holds non-owning pointers. Elements
- either point to the strings passed to the execv_argv ctor, or
- inside M_STORAGE. */
- std::vector<const char *> m_argv;
-
- /* Storage. In the no-shell case, this contains a copy of the
- arguments passed to the ctor, split by '\0'. In the shell case,
- this contains the quoted shell command. I.e., SHELL_COMMAND in
- {"$SHELL" "-c", SHELL_COMMAND, NULL}. */
- std::string m_storage;
+ /* The argument vector. This owns the strings within it. */
+ gdb::argv_vec m_argv;
};
-/* Create argument vector for straight call to execvp. Breaks up
- ALLARGS into an argument vector suitable for passing to execvp and
- stores it in M_ARGV. E.g., on "run a b c d" this routine would get
- as input the string "a b c d", and as output it would fill in
- M_ARGV with the four arguments "a", "b", "c", "d". Each argument
- in M_ARGV points to a substring of a copy of ALLARGS stored in
- M_STORAGE. */
+/* Create argument vector for straight call to execvp. Breaks up ALLARGS
+ into an argument vector suitable for passing to execvp and stores it in
+ M_ARGV. EXEC_FILE is the executable to be run.
+
+ E.g., if EXEC_FILE is "foo", and the user does "run a b c d" then
+ ALLARGS would be "a b c d", and this function would fill M_ARGV with
+ give arguments "foo", "a", "b", "c", and "d". */
void
execv_argv::init_for_no_shell (const char *exec_file,
const std::string &allargs)
{
+ m_argv.push_back (xstrdup (exec_file));
- /* Save/work with a copy stored in our storage. The pointers pushed
- to M_ARGV point directly into M_STORAGE, which is modified in
- place with the necessary NULL terminators. This avoids N heap
- allocations and string dups when 1 is sufficient. */
- std::string &args_copy = m_storage = allargs;
-
- m_argv.push_back (exec_file);
-
- for (size_t cur_pos = 0; cur_pos < args_copy.size ();)
- {
- /* Skip whitespace-like chars. */
- std::size_t pos = args_copy.find_first_not_of (" \t\n", cur_pos);
-
- if (pos != std::string::npos)
- cur_pos = pos;
-
- /* Find the position of the next separator. */
- std::size_t next_sep = args_copy.find_first_of (" \t\n", cur_pos);
-
- if (next_sep == std::string::npos)
- {
- /* No separator found, which means this is the last
- argument. */
- next_sep = args_copy.size ();
- }
- else
- {
- /* Replace the separator with a terminator. */
- args_copy[next_sep++] = '\0';
- }
-
- m_argv.push_back (&args_copy[cur_pos]);
+ gdb_argv argv (allargs.c_str ());
- cur_pos = next_sep;
- }
+ for (const auto &a : argv)
+ m_argv.push_back (xstrdup (a));
/* NULL-terminate the vector. */
m_argv.push_back (NULL);
@@ -182,11 +146,7 @@ execv_argv::init_for_shell (const char *exec_file,
/* We're going to call a shell. */
bool escape_bang = escape_bang_in_quoted_argument (shell_file);
- /* We need to build a new shell command string, and make argv point
- to it. So build it in the storage. */
- std::string &shell_command = m_storage;
-
- shell_command = "exec ";
+ std::string shell_command = "exec ";
/* Add any exec wrapper. That may be a program name with arguments,
so the user must handle quoting. */
@@ -255,10 +215,9 @@ execv_argv::init_for_shell (const char *exec_file,
/* If we decided above to start up with a shell, we exec the shell.
"-c" says to interpret the next arg as a shell command to
execute, and this command is "exec <target-program> <args>". */
- m_argv.reserve (4);
- m_argv.push_back (shell_file);
- m_argv.push_back ("-c");
- m_argv.push_back (shell_command.c_str ());
+ m_argv.push_back (xstrdup (shell_file));
+ m_argv.push_back (xstrdup ("-c"));
+ m_argv.push_back (xstrdup (shell_command.c_str ()));
m_argv.push_back (NULL);
}
diff --git a/gdb/observable.h b/gdb/observable.h
index 077014c..deea1ff 100644
--- a/gdb/observable.h
+++ b/gdb/observable.h
@@ -104,7 +104,8 @@ extern observable<solib &/* solib */> solib_loaded;
/* The shared library SOLIB has been unloaded from program space PSPACE.
Note when gdb calls this observer, the library's symbols have not
been unloaded yet, and thus are still available. */
-extern observable<program_space *, const solib &/* solib */> solib_unloaded;
+extern observable<program_space *, const solib &/* solib */,
+ bool /* still_in_use */> solib_unloaded;
/* The symbol file specified by OBJFILE has been loaded. */
extern observable<struct objfile */* objfile */> new_objfile;
diff --git a/gdb/solib.c b/gdb/solib.c
index 4a04f1d..eee02ca 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -691,13 +691,17 @@ notify_solib_loaded (solib &so)
gdb::observers::solib_loaded.notify (so);
}
-/* Notify interpreters and observers that solib SO has been unloaded. */
+/* Notify interpreters and observers that solib SO has been unloaded.
+ When STILL_IN_USE is true, the objfile backing SO is still in use,
+ this indicates that SO was loaded multiple times, but only mapped
+ in once (the mapping was reused). */
static void
-notify_solib_unloaded (program_space *pspace, const solib &so)
+notify_solib_unloaded (program_space *pspace, const solib &so,
+ bool still_in_use)
{
- interps_notify_solib_unloaded (so);
- gdb::observers::solib_unloaded.notify (pspace, so);
+ interps_notify_solib_unloaded (so, still_in_use);
+ gdb::observers::solib_unloaded.notify (pspace, so, still_in_use);
}
/* See solib.h. */
@@ -792,18 +796,23 @@ update_solib_list (int from_tty)
/* If it's not on the inferior's list, remove it from GDB's tables. */
else
{
+ bool still_in_use
+ = (gdb_iter->objfile != nullptr
+ && solib_used (current_program_space, *gdb_iter));
+
/* Notify any observer that the shared object has been
unloaded before we remove it from GDB's tables. */
- notify_solib_unloaded (current_program_space, *gdb_iter);
-
- current_program_space->deleted_solibs.push_back (gdb_iter->so_name);
+ notify_solib_unloaded (current_program_space, *gdb_iter,
+ still_in_use);
/* Unless the user loaded it explicitly, free SO's objfile. */
if (gdb_iter->objfile != nullptr
&& !(gdb_iter->objfile->flags & OBJF_USERLOADED)
- && !solib_used (current_program_space, *gdb_iter))
+ && !still_in_use)
gdb_iter->objfile->unlink ();
+ current_program_space->deleted_solibs.push_back (gdb_iter->so_name);
+
/* Some targets' section tables might be referring to
sections from so.abfd; remove them. */
current_program_space->remove_target_sections (&*gdb_iter);
@@ -1158,7 +1167,10 @@ clear_solib (program_space *pspace)
for (solib &so : pspace->so_list)
{
- notify_solib_unloaded (pspace, so);
+ bool still_in_use
+ = (so.objfile != nullptr && solib_used (pspace, so));
+
+ notify_solib_unloaded (pspace, so, still_in_use);
pspace->remove_target_sections (&so);
};
diff --git a/gdb/stack.c b/gdb/stack.c
index 4a92449..6f986aa 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -2863,9 +2863,11 @@ find_frame_for_function (const char *function_name)
do
{
+ CORE_ADDR frame_pc = get_frame_address_in_block (frame);
+
for (size_t i = 0; (i < sals.size () && !found); i++)
- found = (get_frame_pc (frame) >= func_bounds[i].low
- && get_frame_pc (frame) < func_bounds[i].high);
+ found = (frame_pc >= func_bounds[i].low
+ && frame_pc < func_bounds[i].high);
if (!found)
{
level = 1;
diff --git a/gdb/testsuite/gdb.ada/inline-section-gc.exp b/gdb/testsuite/gdb.ada/inline-section-gc.exp
index 55cd156..78539aa 100644
--- a/gdb/testsuite/gdb.ada/inline-section-gc.exp
+++ b/gdb/testsuite/gdb.ada/inline-section-gc.exp
@@ -19,14 +19,20 @@ require allow_ada_tests
standard_ada_testfile caller
+# The ordering here works around a bug in older versions of dejagnu.
+# In particular we use "additional_flags" and not "ldflags" to ensure
+# the ordering (this is ok because the distinction doesn't really
+# matter for gnatmake anyway) and furthermore we take care to end with
+# "-margs" so that any flags appended by dejagnu are applied in the
+# correct mode.
set options {
debug
optimize=-O2
additional_flags=-ffunction-sections
- ldflags=-margs
additional_flags=-gnatn
- ldflags=-largs
- ldflags=-Wl,--gc-sections
+ additional_flags=-largs
+ additional_flags=-Wl,--gc-sections
+ additional_flags=-margs
}
if {[gdb_compile_ada "${srcfile}" "${binfile}" executable $options] != ""} {
return -1
diff --git a/gdb/testsuite/gdb.base/args.exp b/gdb/testsuite/gdb.base/args.exp
index f9235f2..34d722a 100644
--- a/gdb/testsuite/gdb.base/args.exp
+++ b/gdb/testsuite/gdb.base/args.exp
@@ -29,30 +29,54 @@ if {[build_executable $testfile.exp $testfile $srcfile] == -1} {
return -1
}
+set startup_with_shell_modes { "on" }
+if {![gdb_protocol_is_remote]} {
+ lappend startup_with_shell_modes "off"
+} else {
+ # Some of these tests will not work when using the remote protocol
+ # due to bug PR gdb/28392.
+ unsupported "gdbserver 'startup-with-shell off' broken PR gdb/28392"
+}
+
# NAME is the name to use for the tests and ARGLIST is the list of
# arguments that are passed to GDB when it is started.
+#
+# The optional RE_LIST is the list of patterns to check the arguments
+# against, these patterns should match ARGLIST. If the arguments are
+# expected to show up unmodified in the test output then RE_LIST can
+# be dropped, and this proc will reuse ARGLIST.
+
+proc args_test { name arglist {re_list {}} } {
+
+ # If RE_LIST is not supplied then we can reuse ARGLIST, this
+ # implies that the arguments will appear unmodified in the test
+ # output.
+ if {[llength $re_list] == 0} {
+ set re_list $arglist
+ }
-proc args_test { name arglist } {
- save_vars { ::GDBFLAGS } {
- set ::GDBFLAGS "$::GDBFLAGS --args $::binfile $arglist"
+ foreach_with_prefix startup_with_shell $::startup_with_shell_modes {
+ save_vars { ::GDBFLAGS } {
+ set ::GDBFLAGS "$::GDBFLAGS --args $::binfile $arglist"
- clean_restart $::binfile
+ clean_restart $::binfile
- runto_main
- gdb_breakpoint [gdb_get_line_number "set breakpoint here"]
- gdb_continue_to_breakpoint "breakpoint for $name"
+ gdb_test_no_output "set startup-with-shell ${startup_with_shell}" \
+ "set startup-with-shell for $name"
- set expected_len [expr 1 + [llength $arglist]]
- gdb_test "print argc" "\\\$$::decimal = $expected_len" "argc for $name"
+ runto_main
+ gdb_breakpoint [gdb_get_line_number "set breakpoint here"]
+ gdb_continue_to_breakpoint "breakpoint for $name"
- set i 1
- foreach arg $arglist {
- if { $arg eq "\n" } {
- set arg {\\n}
+ set expected_len [expr 1 + [llength $re_list]]
+ gdb_test "print argc" "\\\$$::decimal = $expected_len" "argc for $name"
+
+ set i 1
+ foreach arg $re_list {
+ gdb_test "print argv\[$i\]" "\\\$$::decimal = $::hex \"$arg\"" \
+ "argv\[$i\] for $name"
+ set i [expr $i + 1]
}
- gdb_test "print argv\[$i\]" "\\\$$::decimal = $::hex \"$arg\"" \
- "argv\[$i\] for $name"
- set i [expr $i + 1]
}
}
}
@@ -78,6 +102,10 @@ args_test "two empty with single quotes" {{1} {''} {''} {3}}
# Try with arguments containing literal newlines.
-args_test "one newline" {{1} "\n" {3}}
+args_test "one newline" {{1} "\n" {3}} {1 \\\\n 3}
+
+args_test "two newlines" {{1} "\n" "\n" {3}} {1 \\\\n \\\\n 3}
+
+args_test "lone single quote" {{1} \' {3}}
-args_test "two newlines" {{1} "\n" "\n" {3}}
+args_test "lone double quote" {{1} \" {3}} {1 \\\\\" 3}
diff --git a/gdb/testsuite/gdb.base/condbreak-multi-context.cc b/gdb/testsuite/gdb.base/condbreak-multi-context.cc
index b5628f0..a674fd2 100644
--- a/gdb/testsuite/gdb.base/condbreak-multi-context.cc
+++ b/gdb/testsuite/gdb.base/condbreak-multi-context.cc
@@ -18,7 +18,7 @@
class Base
{
public:
- static const int b = 20;
+ static const int bbb = 20;
void func () {}
};
@@ -26,7 +26,7 @@ public:
class A : public Base
{
public:
- static const int a = 10;
+ static const int aaa = 10;
void func () {}
};
@@ -34,7 +34,7 @@ public:
class C : public Base
{
public:
- static const int c = 30;
+ static const int ccc = 30;
void func () {}
};
diff --git a/gdb/testsuite/gdb.base/condbreak-multi-context.exp b/gdb/testsuite/gdb.base/condbreak-multi-context.exp
index 3a4fe37..024f768 100644
--- a/gdb/testsuite/gdb.base/condbreak-multi-context.exp
+++ b/gdb/testsuite/gdb.base/condbreak-multi-context.exp
@@ -22,7 +22,7 @@ set flags {}
lappend flags debug
lappend flags c++
-if {[prepare_for_testing "failed to prepare" ${binfile} ${srcfile} $flags]} {
+if {[build_executable "failed to prepare" ${binfile} ${srcfile} $flags]} {
return
}
@@ -41,17 +41,32 @@ set loc_index(A) 0
set loc_index(Base) 0
set loc_index(C) 0
-# Find breakpoint location contexts.
+# Find breakpoint location contexts. This is called before any of the
+# tests are run and captures the order in which GDB will create the
+# breakpoint locations.
+#
+# This whole test script does rely on GDB always creating the b/p
+# locations in the same order, but that's true right now, and doesn't
+# seem likely to change in the near future.
+
+proc find_location_contexts { } {
+ global loc_name loc_index fill
+ global decimal hex gdb_prompt binfile
+
+ clean_restart ${binfile}
+
+ if {![runto_main]} {
+ return
+ }
-proc find_location_contexts {} {
- global loc_name loc_index bpnum1 fill
- global decimal hex gdb_prompt
+ gdb_breakpoint func
+ set bpnum [get_integer_valueof "\$bpnum" "*UNKNOWN*"]
- gdb_test_multiple "info breakpoint $bpnum1" "find_location_indices" {
- -re "stop only if ${fill}\r\n" {
+ gdb_test_multiple "info breakpoint $bpnum" "find_location_indices" {
+ -re "\r\n$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<MULTIPLE>\\s*\r\n" {
exp_continue
}
- -re "^${bpnum1}\.($decimal) ${fill} ${hex} in (A|Base|C)::func${fill}\r\n" {
+ -re "^${bpnum}\.($decimal) ${fill} ${hex} in (A|Base|C)::func${fill}\r\n" {
set index $expect_out(1,string)
set name $expect_out(2,string)
set loc_name($index) $name
@@ -76,7 +91,7 @@ proc find_location_contexts {} {
# B::func, and 'n' at C::func, respectively.
proc check_bp_locations {bpnum states cond {msg ""}} {
- global loc_name fill
+ global loc_name fill loc_states
# Map location names to location states.
set loc_states(A) [lindex $states 0]
@@ -107,67 +122,87 @@ proc check_bp_locations {bpnum states cond {msg ""}} {
# Scenario 1: Define breakpoints conditionally, using the "break N if
# cond" syntax. Run the program, check that we hit those locations
# only.
+#
+# When START_BEFORE is true we create the breakpoints after running to
+# main. When START_BEFORE is false we create the breakpoints after
+# starting GDB, but before running to main.
+
+proc_with_prefix scenario_1 { start_before } {
+ global warning decimal fill bkptno_num_re binfile
+
+ clean_restart ${binfile}
+
+ if { $start_before } {
+ if {![runto_main temporary]} {
+ return
+ }
+ }
-with_test_prefix "scenario 1" {
# Define the conditional breakpoints. Two locations (Base::func
# and C::func) should be disabled. We do not test location
# indices strictly at this moment, because we don't know them,
# yet. We have strict location index tests below.
- gdb_test "break func if a == 10" \
+ gdb_test "break func if aaa == 10" \
[multi_line \
"${warning} at location $decimal, disabling:" \
- " No symbol \"a\" in current context." \
+ " No symbol \"aaa\" in current context." \
"${warning} at location $decimal, disabling:" \
- " No symbol \"a\" in current context." \
+ " No symbol \"aaa\" in current context." \
"Breakpoint $decimal at $fill .3 locations."] \
- "define bp with condition a == 10"
+ "define bp with condition aaa == 10"
set bpnum1 [get_integer_valueof "\$bpnum" 0 "get bpnum1"]
- gdb_test "break func if c == 30" \
+ gdb_test "break func if ccc == 30" \
[multi_line \
".*${warning} at location $decimal, disabling:" \
- " No symbol \"c\" in current context." \
+ " No symbol \"ccc\" in current context." \
".*${warning} at location $decimal, disabling:" \
- " No symbol \"c\" in current context." \
+ " No symbol \"ccc\" in current context." \
".*Breakpoint $decimal at $fill .3 locations."] \
- "define bp with condition c == 30"
+ "define bp with condition ccc == 30"
set bpnum2 [get_integer_valueof "\$bpnum" 0 "get bpnum2"]
- find_location_contexts
-
- with_test_prefix "before run" {
- check_bp_locations $bpnum1 {y N* N*} "a == 10"
- check_bp_locations $bpnum2 {N* N* y} "c == 30"
+ with_test_prefix "after creating b/p" {
+ check_bp_locations $bpnum1 {y N* N*} "aaa == 10"
+ check_bp_locations $bpnum2 {N* N* y} "ccc == 30"
}
- # Do not use runto_main, it deletes all breakpoints.
- gdb_run_cmd
+ if { !$start_before } {
+ if {![runto_main temporary no-delete-breakpoints]} {
+ return
+ }
+ }
# Check our conditional breakpoints.
- gdb_test "" ".*Breakpoint $bkptno_num_re, A::func .*" \
+ gdb_test "continue" ".*Breakpoint $bkptno_num_re, A::func .*" \
"run until A::func"
- gdb_test "print a" " = 10"
+ gdb_test "print aaa" " = 10"
gdb_test "continue" "Continuing.*Breakpoint $bkptno_num_re, C::func .*" \
"run until C::func"
- gdb_test "print c" " = 30"
+ gdb_test "print ccc" " = 30"
# No more hits!
gdb_continue_to_end
with_test_prefix "after run" {
- check_bp_locations $bpnum1 {y N* N*} "a == 10"
- check_bp_locations $bpnum2 {N* N* y} "c == 30"
+ check_bp_locations $bpnum1 {y N* N*} "aaa == 10"
+ check_bp_locations $bpnum2 {N* N* y} "ccc == 30"
}
}
-# Start GDB with two breakpoints and define the conditions separately.
+# Assuming GDB is already started, create two breakpoints and define
+# the conditions separately.
+#
+# BPNUM1_NAME and BPNUM2_NAME contain the name of two variables in the
+# parent contents. The breakpoint numbers of the two created
+# breakpoints are placed into these variables in the parent content.
-proc setup_bps {} {
- global srcfile binfile srcfile2 decimal
- global bpnum1 bpnum2 bp_location warning loc_index
+proc setup_bps { bpnum1_name bpnum2_name } {
+ global warning loc_index
- clean_restart ${binfile}
+ upvar $bpnum1_name bpnum1
+ upvar $bpnum2_name bpnum2
# Define the breakpoints.
gdb_breakpoint "func"
@@ -176,74 +211,105 @@ proc setup_bps {} {
gdb_breakpoint "func"
set bpnum2 [get_integer_valueof "\$bpnum" 0 "get bpnum2"]
- # Defining a condition on 'a' disables 2 locations.
+ # Defining a condition on 'aaa' disables 2 locations.
set locs [lsort -integer "$loc_index(Base) $loc_index(C)"]
- gdb_test "cond $bpnum1 a == 10" \
+ gdb_test "cond $bpnum1 aaa == 10" \
[multi_line \
"$warning at location ${bpnum1}.[lindex $locs 0], disabling:" \
- " No symbol \"a\" in current context." \
+ " No symbol \"aaa\" in current context." \
"$warning at location ${bpnum1}.[lindex $locs 1], disabling:" \
- " No symbol \"a\" in current context."]
+ " No symbol \"aaa\" in current context."]
# Defining a condition on 'c' disables 2 locations.
set locs [lsort -integer "$loc_index(Base) $loc_index(A)"]
- gdb_test "cond $bpnum2 c == 30" \
+ gdb_test "cond $bpnum2 ccc == 30" \
[multi_line \
"$warning at location ${bpnum2}.[lindex $locs 0], disabling:" \
- " No symbol \"c\" in current context." \
+ " No symbol \"ccc\" in current context." \
"$warning at location ${bpnum2}.[lindex $locs 1], disabling:" \
- " No symbol \"c\" in current context."]
+ " No symbol \"ccc\" in current context."]
}
# Scenario 2: Define breakpoints unconditionally, and then define
# conditions using the "cond N <cond>" syntax. Expect that the
# locations where <cond> is not evaluatable are disabled. Run the
# program, check that we hit the enabled locations only.
+#
+# When START_BEFORE is true we create the breakpoints after running to
+# main. When START_BEFORE is false we create the breakpoints after
+# starting GDB, but before running to main.
+
+proc_with_prefix scenario_2 { start_before } {
+ global binfile bkptno_num_re
+
+ clean_restart ${binfile}
-with_test_prefix "scenario 2" {
- setup_bps
+ if { $start_before } {
+ if {![runto_main temporary]} {
+ return
+ }
+ }
+
+ setup_bps bpnum1 bpnum2
- with_test_prefix "before run" {
- check_bp_locations $bpnum1 {y N* N*} "a == 10"
- check_bp_locations $bpnum2 {N* N* y} "c == 30"
+ with_test_prefix "after creating b/p" {
+ check_bp_locations $bpnum1 {y N* N*} "aaa == 10"
+ check_bp_locations $bpnum2 {N* N* y} "ccc == 30"
}
- # Do not use runto_main, it deletes all breakpoints.
- gdb_run_cmd
+ if { !$start_before } {
+ if {![runto_main temporary no-delete-breakpoints]} {
+ return
+ }
+ }
# Check that we hit enabled locations only.
- gdb_test "" ".*Breakpoint $bkptno_num_re, A::func .*" \
+ gdb_test "continue" ".*Breakpoint $bkptno_num_re, A::func .*" \
"run until A::func"
- gdb_test "print a" " = 10"
+ gdb_test "print aaa" " = 10"
gdb_test "continue" "Continuing.*Breakpoint $bkptno_num_re, C::func .*" \
"run until C::func"
- gdb_test "print c" " = 30"
+ gdb_test "print ccc" " = 30"
# No more hits!
gdb_continue_to_end
with_test_prefix "after run" {
- check_bp_locations $bpnum1 {y N* N*} "a == 10"
- check_bp_locations $bpnum2 {N* N* y} "c == 30"
+ check_bp_locations $bpnum1 {y N* N*} "aaa == 10"
+ check_bp_locations $bpnum2 {N* N* y} "ccc == 30"
}
}
# Scenario 3: Apply misc. checks on the already-defined breakpoints.
+#
+# When START_BEFORE is true we create the breakpoints after running to
+# main. When START_BEFORE is false we create the breakpoints after
+# starting GDB, but before running to main.
+
+proc_with_prefix scenario_3 { start_before } {
+ global binfile bkptno_num_re loc_index warning
+
+ clean_restart ${binfile}
-with_test_prefix "scenario 3" {
- setup_bps
+ if { $start_before } {
+ if {![runto_main temporary]} {
+ return
+ }
+ }
+
+ setup_bps bpnum1 bpnum2
set locs [lsort -integer "$loc_index(Base) $loc_index(A)"]
- gdb_test "cond $bpnum1 c == 30" \
+ gdb_test "cond $bpnum1 ccc == 30" \
[multi_line \
"${warning} at location ${bpnum1}.[lindex $locs 0], disabling:" \
- " No symbol \"c\" in current context." \
+ " No symbol \"ccc\" in current context." \
"${warning} at location ${bpnum1}.[lindex $locs 1], disabling:" \
- " No symbol \"c\" in current context." \
+ " No symbol \"ccc\" in current context." \
"Breakpoint ${bpnum1}'s condition is now valid at location $loc_index(C), enabling."] \
"change the condition of bp 1"
- check_bp_locations $bpnum1 {N* N* y} "c == 30" "after changing the condition"
+ check_bp_locations $bpnum1 {N* N* y} "ccc == 30" "after changing the condition"
gdb_test "cond $bpnum1" \
[multi_line \
@@ -254,7 +320,7 @@ with_test_prefix "scenario 3" {
check_bp_locations $bpnum1 {y y y} "" "after resetting the condition"
gdb_test_no_output "disable ${bpnum2}.$loc_index(A)"
- check_bp_locations $bpnum2 {N* N* y} "c == 30" "after disabling loc for A"
+ check_bp_locations $bpnum2 {N* N* y} "ccc == 30" "after disabling loc for A"
gdb_test "cond $bpnum2" ".*" "reset the condition of bp 2"
check_bp_locations $bpnum2 {n y y} "" "loc for A should remain disabled"
@@ -262,17 +328,17 @@ with_test_prefix "scenario 3" {
gdb_test_no_output "disable ${bpnum2}.$loc_index(C)"
check_bp_locations $bpnum2 {n y n} "" "after disabling loc for C"
- gdb_test "cond $bpnum2 c == 30" \
+ gdb_test "cond $bpnum2 ccc == 30" \
[multi_line \
"${warning} at location ${bpnum2}.$loc_index(Base), disabling:" \
- " No symbol \"c\" in current context."] \
+ " No symbol \"ccc\" in current context."] \
"re-define a condition"
- check_bp_locations $bpnum2 {N* N* n} "c == 30" "loc for C should remain disabled"
+ check_bp_locations $bpnum2 {N* N* n} "ccc == 30" "loc for C should remain disabled"
gdb_test "enable ${bpnum2}.$loc_index(Base)" \
"Breakpoint ${bpnum2}'s condition is invalid at location $loc_index(Base), cannot enable." \
"reject enabling a location that is disabled-by-cond"
- check_bp_locations $bpnum2 {N* N* n} "c == 30" "after enable attempt"
+ check_bp_locations $bpnum2 {N* N* n} "ccc == 30" "after enable attempt"
gdb_test "cond $bpnum2 garbage" \
"No symbol \"garbage\" in current context." \
@@ -281,19 +347,34 @@ with_test_prefix "scenario 3" {
gdb_test_no_output "delete $bpnum1"
# Do not use runto_main, it deletes all breakpoints.
- gdb_breakpoint "main"
- gdb_run_cmd
- gdb_test "" ".*reakpoint .*, main .*${srcfile}.*" "start"
+ if { !$start_before } {
+ if {![runto_main temporary no-delete-breakpoints]} {
+ return
+ }
+ }
# The second BP's locations are all disabled. No more hits!
gdb_continue_to_end
}
# Scenario 4: Test the '-force'/'-force-condition' flag.
+#
+# When START_BEFORE is true we create the breakpoints after running to
+# main. When START_BEFORE is false we create the breakpoints after
+# starting GDB, but before running to main, in fact we don't even
+# bother with a run to main in this case.
+
+proc_with_prefix scenario_4 { start_before } {
+ global binfile bkptno_num_re loc_index warning
-with_test_prefix "force" {
clean_restart ${binfile}
+ if { $start_before } {
+ if {![runto_main temporary]} {
+ return
+ }
+ }
+
gdb_breakpoint "func"
# Pick a condition that is invalid at every location.
set bpnum1 [get_integer_valueof "\$bpnum" 0 "get bpnum1"]
@@ -313,3 +394,15 @@ with_test_prefix "force" {
set bpnum2 [get_integer_valueof "\$bpnum" 0 "get bpnum2"]
check_bp_locations $bpnum2 {N* N* N*} "baz" "set using the break command"
}
+
+# Some initial setup. Needed for most of the different scenarios
+# below.
+find_location_contexts
+
+# Now run all of the separate scenarios.
+foreach_with_prefix start_before { true false } {
+ scenario_1 $start_before
+ scenario_2 $start_before
+ scenario_3 $start_before
+ scenario_4 $start_before
+}
diff --git a/gdb/testsuite/gdb.base/corefile-exec-context.exp b/gdb/testsuite/gdb.base/corefile-exec-context.exp
index d6d4c0d..6bd2b04 100644
--- a/gdb/testsuite/gdb.base/corefile-exec-context.exp
+++ b/gdb/testsuite/gdb.base/corefile-exec-context.exp
@@ -108,7 +108,7 @@ gdb_test_multiple "up" "move up to main" {
pass $gdb_test_name
}
- -re -wrap "#$decimal\\s+\[^\r\n\]+ in .*" {
+ -re -wrap "#$decimal\\s+\[^\r\n\]+ (in|at) .*" {
send_gdb "up\n"
exp_continue
}
diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp
index dee046a..14f9084 100644
--- a/gdb/testsuite/gdb.base/dlmopen.exp
+++ b/gdb/testsuite/gdb.base/dlmopen.exp
@@ -77,15 +77,27 @@ if { [build_executable "build shlib" $binfile_lib2 $srcfile_lib \
return
}
-if { [prepare_for_testing "failed to prepare" $testfile $srcfile \
+if { [build_executable "failed to build" $testfile $srcfile \
[list additional_flags=-DDSO1_NAME=\"$binfile_lib1\" \
additional_flags=-DDSO2_NAME=\"$binfile_lib2\" \
shlib_load debug]] } {
- return -1
+ return
+}
+
+# Some locations needed by the tests.
+set bp_inc [gdb_get_line_number "bp.inc" $srcfile_lib]
+set bp_main [gdb_get_line_number "bp.main" $srcfile]
+
+# Figure out the file name for the dynamic linker.
+set dyln_name [section_get $binfile .interp]
+if { $dyln_name eq "" } {
+ unsupported "couldn't find dynamic linker name"
+ return
}
-if { ![runto_main] } {
- return -1
+# Return true if FILENAME is the dynamic linker. Otherwise return false.
+proc is_dyln { filename } {
+ return [expr {$filename eq $::dyln_name}]
}
# Check that 'info shared' show NUM occurrences of DSO.
@@ -158,41 +170,213 @@ proc test_dlmopen {} {
}
}
-# Remove the pause. We only need it for the attach test.
-gdb_test "print wait_for_gdb = 0" "\\\$1 = 0"
+# Setup for calling 'test_dlmopen', this is the version of the test
+# that doesn't use 'attach'.
+proc test_dlmopen_no_attach {} {
+ clean_restart $::binfile
-# Break in the to-be-loaded library and at the end of main.
-set bp_inc [gdb_get_line_number "bp.inc" $srcfile_lib]
-set bp_main [gdb_get_line_number "bp.main" $srcfile]
+ if { ![runto_main] } {
+ return
+ }
-delete_breakpoints
-gdb_breakpoint $srcfile_lib:$bp_inc allow-pending
-gdb_breakpoint $srcfile:$bp_main
+ # Remove the pause. We only need it for the attach test.
+ gdb_test "print wait_for_gdb = 0" "\\\$1 = 0"
-test_dlmopen
+ # Break in the to-be-loaded library and at the end of main.
+ delete_breakpoints
+ gdb_breakpoint $::srcfile_lib:$::bp_inc allow-pending
+ gdb_breakpoint $::srcfile:$::bp_main
-# Try the same again when attaching after dlmopen().
-require can_spawn_for_attach
+ test_dlmopen
+}
-clean_restart $binfile
+# Setup for calling 'test_dlmopen', this is the version of the test
+# that does use 'attach'.
+proc test_dlmopen_with_attach {} {
+ if { ![can_spawn_for_attach] } {
+ unsupported "cannot run attach tests"
+ return
+ }
-# Start the test program.
-set test_spawn_id [spawn_wait_for_attach $binfile]
-set testpid [spawn_id_get_pid $test_spawn_id]
+ clean_restart $::binfile
-# Attach.
-if { ![gdb_attach $testpid] } {
- return
+ # Start the test program.
+ set test_spawn_id [spawn_wait_for_attach $::binfile]
+ set testpid [spawn_id_get_pid $test_spawn_id]
+
+ # Attach.
+ if { ![gdb_attach $testpid] } {
+ return
+ }
+
+ with_test_prefix "attach" {
+ # Remove the pause. We no longer need it.
+ gdb_test "print wait_for_gdb = 0" "\\\$1 = 0"
+
+ # Set the same breakpoints again. This time, however, we do not allow the
+ # breakpoint to be pending since the library has already been loaded.
+ gdb_breakpoint $::srcfile_lib:$::bp_inc
+ gdb_breakpoint $::srcfile:$::bp_main
+
+ test_dlmopen
+ }
}
-with_test_prefix "attach" {
- # Remove the pause. We no longer need it.
- gdb_test "print wait_for_gdb = 0" "\\\$1 = 0"
+# Run 'info sharedlibrary' and count the number of mappings that look
+# like they might be the dynamic linker. This will only work for
+# Linux right now.
+proc get_dyld_info {} {
+ if { ![istarget *-linux*] } {
+ return [list 0 ""]
+ }
- # Set the same breakpoints again. This time, however, we do not allow the
- # breakpoint to be pending since the library has already been loaded.
- gdb_breakpoint $srcfile_lib:$bp_inc
- gdb_breakpoint $srcfile:$bp_main
+ set dyld_count 0
+ set dyld_start_addr ""
+ gdb_test_multiple "info sharedlibrary" "" {
+ -re "From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library\r\n" {
+ exp_continue
+ }
+ -re "^($::hex)\\s+$::hex\\s+\[^/\]+(/\[^\r\n\]+)\r\n" {
+ set addr $expect_out(1,string)
+ set lib $expect_out(2,string)
+
+ if { [is_dyln $lib] } {
+ # This looks like it might be the dynamic linker.
+ incr dyld_count
+ if { $dyld_start_addr eq "" } {
+ set dyld_start_addr $addr
+ } elseif { $dyld_start_addr ne $addr } {
+ set dyld_start_addr "MULTIPLE"
+ }
+ }
- test_dlmopen
+ exp_continue
+ }
+ -re "\\(\\*\\): Shared library is missing debugging information\\.\r\n" {
+ exp_continue
+ }
+ -re "^$::gdb_prompt $" {
+ }
+ }
+
+ if { $dyld_start_addr eq "MULTIPLE" } {
+ set dyld_start_addr ""
+ }
+
+ return [list $dyld_count $dyld_start_addr]
}
+
+# The inferior for this test causes the dynamic linker to be appear
+# multiple times in the inferior's shared library list, but (at least
+# with glibc), the dynamic linker is really only mapped in once. That
+# is, each of the dynamic linker instances that appear in the 'info
+# sharedlibrary' output, will have the same address range.
+#
+# This test creates a user breakpoint in the dynamic linker, and then
+# runs over the dlcose calls, which unmap all but one of the dynamic
+# linker instances.
+#
+# The expectation is that the user breakpoint in the dynamic linker
+# should still be active. Older versions of GDB had a bug where the
+# breakpoint would become pending.
+proc_with_prefix test_solib_unmap_events { } {
+
+ # This test relies on finding the dynamic linker library, and is
+ # currently written assuming Linux.
+ if { ![istarget *-linux*] } {
+ unsupport "cannot find dynamic linker library on this target"
+ return
+ }
+
+ clean_restart $::binfile
+
+ if { ![runto_main] } {
+ return
+ }
+
+ # Check that before any of our dlopen/dlmopen calls, we can find a
+ # single copy of the dynamic linker in the shared library list.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ if { $dyld_count != 1 } {
+ unsupported "initial dyld state appears strange"
+ return
+ }
+
+ # Continue the inferior until all solib are loaded.
+ set alarm_lineno [gdb_get_line_number "alarm" $::srcfile]
+ gdb_breakpoint ${::srcfile}:${alarm_lineno}
+ gdb_continue_to_breakpoint "all solib are now loaded"
+
+ # Check that we have multiple copies of dynamic linker loaded, and
+ # that the dynamic linker is only loaded at a single address.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ set dyld_start_addr [lindex $dyld_info 1]
+
+ # If we didn't find a suitable dynamic linker address, or we
+ # didn't find multiple copies of the dynamic linker, then
+ # something has gone wrong with the test setup.
+ if { $dyld_count < 2 } {
+ unsupported "multiple copies of the dynamic linker not found"
+ return
+ }
+ if { $dyld_start_addr eq "" } {
+ unsupported "unable to find suitable dynamic linker start address"
+ return
+ }
+
+ # Check the address we found is (likely) writable.
+ gdb_test_multiple "x/1i $dyld_start_addr" "check b/p address" {
+ -re -wrap "Cannot access memory at address \[^\r\n\]+" {
+ unsupported "dynamic linker address is not accessible"
+ return
+ }
+ -re -wrap "" {
+ }
+ }
+
+ # Create a breakpoint within the dynamic linker. It is pretty unlikely
+ # that this breakpoint will ever be hit, but just in case it is, make it
+ # conditional, with a condition that will never be true. All we really
+ # care about for this test is whether the breakpoint will be made
+ # pending or not (it should not).
+ gdb_test "break *$dyld_start_addr if (0)" \
+ "Breakpoint $::decimal at $::hex\[^\r\n\]+" \
+ "create breakpoint within dynamic linker"
+ set bpnum [get_integer_valueof "\$bpnum" INVALID "get bpnum"]
+
+ # Now continue until the 'bp.main' location, this will unload some
+ # copies, but not all copies, of the dynamic linker.
+ gdb_test "print wait_for_gdb = 0" " = 0"
+ set bp_main [gdb_get_line_number "bp.main" $::srcfile]
+
+ gdb_breakpoint $::srcfile:$bp_main
+ gdb_continue_to_breakpoint "stop at bp.main"
+
+ # At one point, GDB would incorrectly mark the breakpoints in the
+ # dynamic linker as pending when some instances of the library were
+ # unloaded, despite there really only being one copy of the dynamic
+ # linker actually loaded into the inferior's address space.
+ gdb_test_multiple "info breakpoints $bpnum" "check b/p status" {
+ -re -wrap "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+\\*$::hex\\s*\r\n\\s+stop only if \\(0\\)" {
+ fail $gdb_test_name
+ }
+
+ -re -wrap "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex\\s*\[^\r\n\]+\r\n\\s+stop only if \\(0\\)" {
+ pass $gdb_test_name
+ }
+ }
+
+ # With all the dlclose calls now complete, we should be back to a
+ # single copy of the dynamic linker.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ gdb_assert { $dyld_count == 1 } \
+ "one dynamic linker found after dlclose calls"
+}
+
+# Run the actual tests.
+test_dlmopen_no_attach
+test_dlmopen_with_attach
+test_solib_unmap_events
diff --git a/gdb/testsuite/gdb.base/frame-selection.c b/gdb/testsuite/gdb.base/frame-selection.c
index 237e155..18a58e4 100644
--- a/gdb/testsuite/gdb.base/frame-selection.c
+++ b/gdb/testsuite/gdb.base/frame-selection.c
@@ -15,6 +15,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/>. */
+#include <stdlib.h>
+
int
frame_2 (void)
{
@@ -40,6 +42,23 @@ recursive (int arg)
return v;
}
+/* A function that never returns. */
+void __attribute__((noreturn))
+func_that_never_returns (void)
+{
+ exit (0);
+}
+
+/* A function that tail calls. Calling a 'noreturn' function isn't
+ required for a tail call, but at low optimisation levels, gcc will apply
+ the tail call optimisation only for 'noreturn' calls. */
+
+void
+func_that_tail_calls (void)
+{
+ func_that_never_returns ();
+}
+
int
main (void)
{
@@ -48,5 +67,7 @@ main (void)
i = frame_1 ();
j = recursive (0);
+ func_that_tail_calls ();
+
return i + j;
}
diff --git a/gdb/testsuite/gdb.base/frame-selection.exp b/gdb/testsuite/gdb.base/frame-selection.exp
index e8d9c87..32ed92d 100644
--- a/gdb/testsuite/gdb.base/frame-selection.exp
+++ b/gdb/testsuite/gdb.base/frame-selection.exp
@@ -186,3 +186,40 @@ with_test_prefix "second frame_2 breakpoint" {
gdb_test "frame function recursive" "#1 $hex in recursive.*" \
"select frame for function recursive, third attempt"
}
+
+# At one point using the 'function' sub-command (e.g. 'frame function
+# ...') would fail to select a frame if the frame ended with a
+# tail-call, and the address within the frame was outside the bounds
+# of the function.
+with_test_prefix "stack with tail call" {
+ gdb_breakpoint func_that_never_returns
+ gdb_continue_to_breakpoint func_that_never_returns
+
+ gdb_test "bt" \
+ [multi_line \
+ "#0 func_that_never_returns \\(\\) at \[^\r\n\]+" \
+ "#1 $hex in func_that_tail_calls \\(\\) at \[^\r\n\]+" \
+ "#2 $hex in main \\(\\) at \[^\r\n\]+"] \
+ "bt from func_that_never_returns"
+
+ # Reset the frame addresses based on the new stack.
+ gdb_test "frame 0" "#0 func_that_never_returns.*"
+ set frame_0_address [ get_frame_address "frame 0" ]
+ gdb_test "frame 1" "#1 $hex in func_that_tail_calls.*"
+ set frame_1_address [ get_frame_address "frame 1" ]
+ gdb_test "frame 2" "#2 $hex in main.*"
+ set frame_2_address [ get_frame_address "frame 2" ]
+
+ # Test 'select-frame function ...' command.
+ gdb_test_no_output "select-frame function func_that_never_returns"
+ check_frame "0" "${frame_0_address}" "func_that_never_returns"
+ gdb_test_no_output "select-frame function func_that_tail_calls"
+ check_frame "1" "${frame_1_address}" "func_that_tail_calls"
+ gdb_test_no_output "select-frame function main"
+ check_frame "2" "${frame_2_address}" "main"
+
+ # Test 'frame function ...' command.
+ gdb_test "frame function func_that_never_returns" "#0 func_that_never_returns.*"
+ gdb_test "frame function func_that_tail_calls" "#1 $hex in func_that_tail_calls.*"
+ gdb_test "frame function main" "#2 $hex in main.*"
+}
diff --git a/gdb/testsuite/gdb.base/inferior-args.exp b/gdb/testsuite/gdb.base/inferior-args.exp
index b165fb8..79b73e6 100644
--- a/gdb/testsuite/gdb.base/inferior-args.exp
+++ b/gdb/testsuite/gdb.base/inferior-args.exp
@@ -25,16 +25,27 @@ if {[build_executable "failed to prepare" $testfile $srcfile \
return
}
-proc do_test { method } {
+# STARTUP_WITH_SHELL is either 'on' or 'off' and determines if the
+# inferior is started under a shell or not. INFERIOR_ARGS is the list
+# of inferior arguments. EXPECTED_RESULTS is the list of expected
+# results, one for each argument.
+#
+# When STUB_SUITABLE is true this test is suitable for use with
+# gdbserver, i.e. INFERIOR_ARGS can be passed through to
+# gdbserver_start via gdb_run_cmd. Some of the weird quoting used in
+# some of the tests doesn't seem to play well with gdbserver_start.
+# This is a TCL issue, not a gdbserver issue. Manually testing with
+# gdbserver shows no problems. It's just that when we try to invoke
+# gdbserver from TCL the argument quoting gets messed up. For tests
+# that are problematic, STUB_SUITABLE is false.
+proc do_test { method startup_with_shell inferior_args expected_results \
+ stub_suitable } {
global binfile hex
- # The second arg is an empty string on purpose. The last argument
- # must be the empty argument -- we once had a bug where that
- # wouldn't work!
- set inferior_args { "first arg" "" "third-arg" "'" "\"" " " "" }
-
clean_restart $binfile
+ gdb_test_no_output "set startup-with-shell $startup_with_shell"
+
if { $method == "start" } {
# The start command does not make sense for a stub.
if { [use_gdb_stub] } {
@@ -80,6 +91,10 @@ proc do_test { method } {
return -1
}
+ if { [use_gdb_stub] && !$stub_suitable } {
+ return
+ }
+
# The run command does not make sense for a stub, but GDB_RUN_CMD
# does the right thing when the target is a stub (start the stub,
# connect to it, and "continue").
@@ -110,18 +125,74 @@ proc do_test { method } {
error "invalid method $method"
}
+ set argc [expr [llength $expected_results] + 1]
+
# Now that we are stopped at main, inspect argc/argv.
- gdb_test "print argc" " = 8"
- gdb_test "print argv\[0\]" " = $hex \".*\""
- gdb_test "print argv\[1\]" " = $hex \"first arg\""
- gdb_test "print argv\[2\]" " = $hex \"\""
- gdb_test "print argv\[3\]" " = $hex \"third-arg\""
- gdb_test "print argv\[4\]" " = $hex \"'\""
- gdb_test "print argv\[5\]" " = $hex \"\\\\\"\""
- gdb_test "print argv\[6\]" " = $hex \" \""
- gdb_test "print argv\[7\]" " = $hex \"\""
+ gdb_test "print argc" " = $argc"
+ gdb_test "print argv\[0\]" " = $hex \"\[^\r\n\]+\""
+ for { set i 1 } { $i < $argc } { incr i } {
+ set idx [expr $i - 1]
+ gdb_test "print argv\[$i\]" " = [lindex $expected_results $idx]"
+ }
+}
+
+set test_desc_list []
+
+# test one
+# --------
+#
+# The second arg is an empty string on purpose. The last argument
+# must be the empty argument -- we once had a bug where that wouldn't
+# work!
+lappend test_desc_list [list "test one" \
+ true \
+ { "first arg" "" "third-arg" "'" "\"" " " "" } \
+ [list "$hex \"first arg\"" \
+ "$hex \"\"" \
+ "$hex \"third-arg\"" \
+ "$hex \"'\"" \
+ "$hex \"\\\\\"\"" \
+ "$hex \" \"" \
+ "$hex \"\"" ]]
+
+# test two
+# --------
+#
+# The argument being passed here is '"', that is a single double quote
+# contained within single quotes.
+#
+# I build the test descriptor using this mess of code to avoid having
+# unbalanced quotes, which messes up indentation and syntax
+# highlighting within (at least) emacs. The 'format' of ascii code 34
+# gives us the double quote character. Then I have to jump through
+# the rest of this mess in order to avoid TCL escaping the quote for
+# me. It's super important that what we send to GDB is '"' not '\"'.
+set item [list "test two" false]
+set cmd [format "lappend item \{ '%c' '\\%c' \}" 34 34]
+eval $cmd
+set bs "\\\\"
+lappend item [list "$hex \"$bs\"\"" "$hex \"$bs$bs$bs\"\""]
+lappend test_desc_list $item
+
+set startup_with_shell_modes { "on" }
+if {![gdb_protocol_is_remote]} {
+ lappend startup_with_shell_modes "off"
+} else {
+ # Due to PR gdb/28392 gdbserver doesn't currently support having
+ # startup-with-shell off, and then attempting to pass arguments
+ # containing whitespace.
+ unsupported "bug gdb/28392: gdbserver doesn't support this"
}
-foreach_with_prefix method { "start" "starti" "run" "set args" } {
- do_test $method
+
+foreach desc $test_desc_list {
+ lassign $desc name stub_suitable args re_list
+ with_test_prefix $name {
+ foreach_with_prefix set_method { "start" "starti" "run" "set args" } {
+ foreach_with_prefix startup_with_shell $startup_with_shell_modes {
+ do_test $set_method $startup_with_shell $args $re_list \
+ $stub_suitable
+ }
+ }
+ }
}
diff --git a/gdb/testsuite/gdb.base/libsegfault.exp b/gdb/testsuite/gdb.base/libsegfault.exp
index 2c16fe8..61a54ff 100644
--- a/gdb/testsuite/gdb.base/libsegfault.exp
+++ b/gdb/testsuite/gdb.base/libsegfault.exp
@@ -42,7 +42,7 @@ proc gdb_spawn_with_ld_preload {lib cmdline_opts} {
# 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.
- set_sanitizer_default ASAN_OPTIONS verify_asan_link_order 0
+ append_environment_default ASAN_OPTIONS verify_asan_link_order 0
gdb_spawn_with_cmdline_opts $cmdline_opts
}
diff --git a/gdb/testsuite/gdb.base/startup-with-shell.exp b/gdb/testsuite/gdb.base/startup-with-shell.exp
index 87a7559..495c43e 100644
--- a/gdb/testsuite/gdb.base/startup-with-shell.exp
+++ b/gdb/testsuite/gdb.base/startup-with-shell.exp
@@ -43,55 +43,134 @@ proc initial_setup_simple { startup_with_shell run_args } {
clean_restart $binfile
gdb_test_no_output "set startup-with-shell $startup_with_shell"
+ gdb_test_no_output "set print characters unlimited"
gdb_test_no_output "set args $run_args" \
"set args \$run_args"
- set test "inferior started"
- if { [runto_main] } {
- pass $test
- } else {
- fail $test
+ return [runto_main]
+}
+
+# Start GDB, set the inferior arguments to ARGS, and then run to main.
+# Once at main, read the first argument from the inferior and compare
+# it to ON_RE if startup-with-shell is on, otherwise compare to
+# OFF_RE.
+#
+# If PROBLEMATIC_ON is true then when startup-with-shell is on we
+# expect the comparison to fail, so setup an xfail.
+#
+# If PROBLEMATIC_OFF is true then when startup-with-shell is off we
+# expect the comparison to fail, so setup an xfail.
+#
+# TESTNAME is a string used in the test names.
+proc run_test { args on_re off_re testname { problematic_on false } \
+ { problematic_off false } } {
+ foreach startup_with_shell { "on" "off" } {
+ with_test_prefix "$testname, startup_with_shell: ${startup_with_shell}" {
+ if {![initial_setup_simple $startup_with_shell $args]} {
+ return -1
+ }
+
+ if { $startup_with_shell } {
+ set re $on_re
+ set problematic $problematic_on
+ } else {
+ set re $off_re
+ set problematic $problematic_off
+ }
+
+ if { $problematic } {
+ setup_xfail "*-*-*" gdb/28392
+ }
+
+ gdb_test "print argv\[1\]" "\\\$$::decimal = $::hex $re" $testname
+ }
}
}
+# This is like the run_test proc except that RE is used as the
+# expected argument regexp when startup-with-shell is both on and off.
+# For the other arguments, see run_test.
+proc run_test_same { args re testname { problematic_on false } \
+ { problematic_off false } } {
+ run_test $args $re $re $testname $problematic_on $problematic_off
+}
+
+# The regexp to match a single '\' character.
+set bs "\\\\"
+
# Are we using 'remote' or 'extended-remote' protocol?
set is_remote_p [gdb_protocol_is_remote]
## Run the actual tests
-with_test_prefix "startup_with_shell = on; run_args = *.unique-extension" {
- initial_setup_simple "on" "$unique_file_dir/*.unique-extension"
- gdb_test_no_output "set print characters unlimited"
- if { $is_remote_p } {
- setup_xfail "*-*-*" gdb/28392
- }
- gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"$unique_file\"" \
- "first argument expanded"
-}
+run_test "$unique_file_dir/*.unique-extension" \
+ "\"$unique_file\"" \
+ "\"$unique_file_dir/\\\*\.unique-extension\"" \
+ "arg is glob" \
+ $is_remote_p
-with_test_prefix "startup_with_shell = off; run_args = *.unique-extension" {
- initial_setup_simple "off" "$unique_file_dir/*.unique-extension"
- gdb_test_no_output "set print characters unlimited"
- gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"$unique_file_dir/\\\*\.unique-extension\"" \
- "first argument not expanded"
-}
+run_test_same "$unique_file_dir/\\*.unique-extension" \
+ "\"$unique_file_dir/\\\*\.unique-extension\"" \
+ "arg is escaped glob"
-with_test_prefix "startup_with_shell = on; run_args = \$TEST" {
+save_vars { env(TEST) } {
set env(TEST) "1234"
- initial_setup_simple "on" "\$TEST"
- if { $is_remote_p } {
- setup_xfail "*-*-*" gdb/28392
- }
- gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"1234\"" \
- "testing first argument"
- unset env(TEST)
+ run_test "\$TEST" \
+ "\"1234\"" \
+ "\"\\\$TEST\"" \
+ "arg is shell variable" \
+ $is_remote_p
+
+ run_test_same "\\\$TEST" \
+ "\"\\\$TEST\"" \
+ "arg is escaped shell variable"
}
-with_test_prefix "startup_with_shell = off; run_args = \$TEST" {
- set env(TEST) "1234"
- initial_setup_simple "off" "\$TEST"
- gdb_test "print argv\[1\]" "\\\$$decimal = $hex \"\\\$TEST\"" \
- "testing first argument"
- unset env(TEST)
-}
+run_test_same "\"\\a\"" \
+ "\"${bs}${bs}a\"" \
+ "retain backslash in double quote arg" \
+ false $is_remote_p
+
+run_test_same "'\\a'" \
+ "\"${bs}${bs}a\"" \
+ "retain backslash in single quote arg" \
+ false $is_remote_p
+
+run_test_same "\"\\\$\"" \
+ "\"\\\$\"" \
+ "'\$' can be escaped in double quote arg"
+
+run_test_same "'\\\$'" \
+ "\"${bs}${bs}\\\$\"" \
+ "'\$' is not escaped in single quote arg" \
+ false $is_remote_p
+
+run_test_same "\"\\`\"" \
+ "\"\\`\"" \
+ "'`' can be escaped in double quote arg"
+
+run_test_same "'\\`'" \
+ "\"${bs}${bs}`\"" \
+ "'`' is not escaped in single quote arg" \
+ false $is_remote_p
+
+run_test_same "\"\\\"\"" \
+ "\"${bs}\"\"" \
+ "'\"' can be escaped in double quote arg" \
+ false $is_remote_p
+
+run_test_same "'\\\"'" \
+ "\"${bs}${bs}${bs}\"\"" \
+ "'\"' is not escaped in single quote arg" \
+ false $is_remote_p
+
+run_test_same "\"\\\\\"" \
+ "\"${bs}${bs}\"" \
+ "'\\' can be escaped in double quote arg" \
+ false $is_remote_p
+
+run_test_same "'\\\\'" \
+ "\"${bs}${bs}${bs}${bs}\"" \
+ "'\\' is not escaped in single quote arg" \
+ false $is_remote_p
diff --git a/gdb/testsuite/gdb.dap/memory.exp b/gdb/testsuite/gdb.dap/memory.exp
index 89ae8bb..41f4f8d 100644
--- a/gdb/testsuite/gdb.dap/memory.exp
+++ b/gdb/testsuite/gdb.dap/memory.exp
@@ -29,8 +29,8 @@ save_vars { env(ASAN_OPTIONS) env(TSAN_OPTIONS) } {
# The request readMemory with count 18446744073709551615 triggers address
# sanitizer. Suppress the error, leaving us with just this warning:
# WARNING: AddressSanitizer failed to allocate 0xffffffffffffffff bytes
- set_sanitizer ASAN_OPTIONS allocator_may_return_null 1
- set_sanitizer TSAN_OPTIONS allocator_may_return_null 1
+ append_environment ASAN_OPTIONS allocator_may_return_null 1
+ append_environment TSAN_OPTIONS allocator_may_return_null 1
if {[dap_initialize] == ""} {
return
diff --git a/gdb/testsuite/gdb.mi/mi-dlmopen-lib-dep.c b/gdb/testsuite/gdb.mi/mi-dlmopen-lib-dep.c
new file mode 100644
index 0000000..f981bea
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-dlmopen-lib-dep.c
@@ -0,0 +1,21 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021-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/>.
+
+*/
+
+__attribute__((visibility ("default")))
+int gdb_dlmopen_glob = 1;
diff --git a/gdb/testsuite/gdb.mi/mi-dlmopen-lib.c b/gdb/testsuite/gdb.mi/mi-dlmopen-lib.c
new file mode 100644
index 0000000..36ccbf02
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-dlmopen-lib.c
@@ -0,0 +1,28 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021-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/>.
+
+*/
+
+extern int gdb_dlmopen_glob;
+
+__attribute__((visibility ("default")))
+int
+inc (int n)
+{
+ int amount = gdb_dlmopen_glob;
+ return n + amount; /* bp.inc. */
+}
diff --git a/gdb/testsuite/gdb.mi/mi-dlmopen.c b/gdb/testsuite/gdb.mi/mi-dlmopen.c
new file mode 100644
index 0000000..3cdee68
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-dlmopen.c
@@ -0,0 +1,59 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021-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 _GNU_SOURCE
+#include <dlfcn.h>
+#include <stddef.h>
+#include <assert.h>
+#include <unistd.h>
+
+int
+main (void)
+{
+ void *handle[4];
+ int (*fun) (int);
+ Lmid_t lmid;
+ int dl;
+
+ handle[0] = dlmopen (LM_ID_NEWLM, DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[0] != NULL);
+
+ dlinfo (handle[0], RTLD_DI_LMID, &lmid);
+
+ handle[1] = dlopen (DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[1] != NULL);
+
+ handle[2] = dlmopen (LM_ID_NEWLM, DSO1_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[2] != NULL);
+
+ handle[3] = dlmopen (lmid, DSO2_NAME, RTLD_LAZY | RTLD_LOCAL);
+ assert (handle[3] != NULL); /* bp.loaded */
+
+ for (dl = 0; dl < 4; ++dl)
+ {
+ fun = dlsym (handle[dl], "inc");
+ assert (fun != NULL);
+
+ fun (42);
+
+ dlclose (handle[dl]);
+ }
+
+ return 0; /* bp.main */
+}
diff --git a/gdb/testsuite/gdb.mi/mi-dlmopen.exp b/gdb/testsuite/gdb.mi/mi-dlmopen.exp
new file mode 100644
index 0000000..a5743f8
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/mi-dlmopen.exp
@@ -0,0 +1,222 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2024-2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# MI tests related to loading shared libraries into different namespaces
+# with dlmopen(). The source files for this test are copied (almost)
+# verbatim from the gdb.base/dlmopen.exp test.
+
+load_lib "mi-support.exp"
+
+require allow_dlmopen_tests
+
+standard_testfile .c -lib.c -lib-dep.c
+
+set basename_lib dlmopen-lib
+set srcfile_lib $srcfile2
+set binfile_lib1 [standard_output_file $basename_lib.1.so]
+set binfile_lib2 [standard_output_file $basename_lib.2.so]
+set srcfile_lib_dep $srcfile3
+set binfile_lib_dep [standard_output_file $basename_lib-dep.so]
+
+if { [build_executable "build shlib dep" $binfile_lib_dep $srcfile_lib_dep \
+ {debug shlib}] == -1 } {
+ return
+}
+
+if { [build_executable "build shlib" $binfile_lib1 $srcfile_lib \
+ [list debug shlib_load shlib libs=$binfile_lib_dep]] == -1 } {
+ return
+}
+
+if { [build_executable "build shlib" $binfile_lib2 $srcfile_lib \
+ [list debug shlib_load shlib libs=$binfile_lib_dep]] == -1 } {
+ return
+}
+
+if { [build_executable "failed to build" $testfile $srcfile \
+ [list additional_flags=-DDSO1_NAME=\"$binfile_lib1\" \
+ additional_flags=-DDSO2_NAME=\"$binfile_lib2\" \
+ shlib_load debug]] } {
+ return
+}
+
+# Figure out the file name for the dynamic linker.
+set dyln_name [section_get $binfile .interp]
+if { $dyln_name eq "" } {
+ unsupported "couldn't find dynamic linker name"
+ return
+}
+
+# Some source locations needed by the tests.
+set bp_main [gdb_get_line_number "bp.main" $srcfile]
+set bp_loaded [gdb_get_line_number "bp.loaded" $srcfile]
+
+# Return true if FILENAME is the dynamic linker. Otherwise return false.
+proc is_dyln { filename } {
+ return [expr {$filename eq $::dyln_name}]
+}
+
+# Run 'info sharedlibrary' and count the number of mappings that look
+# like they might be the dynamic linker. This will only work for
+# Linux right now.
+proc get_dyld_info {} {
+ if { ![istarget *-linux*] } {
+ return [list 0 ""]
+ }
+
+ set dyld_count 0
+ set dyld_start_addr ""
+ gdb_test_multiple "info sharedlibrary" "" {
+ -re "~\"From\\s+To\\s+Syms\\s+Read\\s+Shared Object Library\\\\n\"\r\n" {
+ exp_continue
+ }
+ -re "^~\"($::hex)\\s+$::hex\\s+\[^/\]+(/\[^\r\n\]+)\\\\n\"\r\n" {
+ set addr $expect_out(1,string)
+ set lib $expect_out(2,string)
+
+ if { [is_dyln $lib] } {
+ # This looks like it might be the dynamic linker.
+ incr dyld_count
+ if { $dyld_start_addr eq "" } {
+ set dyld_start_addr $addr
+ } elseif { $dyld_start_addr ne $addr } {
+ set dyld_start_addr "MULTIPLE"
+ }
+ }
+
+ exp_continue
+ }
+ -re "~\"\\(\\*\\): Shared library is missing debugging information\\.\\\\n\"\r\n" {
+ exp_continue
+ }
+ -re "^\\^done\r\n" {
+ exp_continue
+ }
+ -re "^$::mi_gdb_prompt$" {
+ }
+ }
+
+ if { $dyld_start_addr eq "MULTIPLE" } {
+ set dyld_start_addr ""
+ }
+
+ return [list $dyld_count $dyld_start_addr]
+}
+
+# Run the inferior over all the 'dlclose' calls and capture the
+# resulting library-unloaded events. Check that we see the expected
+# number of unload events for the libraries created for this test, and
+# additionally, check for dynamic linker unload events.
+proc check_solib_unload_events {} {
+ mi_clean_restart $::binfile
+
+ if {[mi_runto_main] == -1} {
+ return
+ }
+
+ # After starting we expect the dynamic linker to be loaded exactly
+ # once. If it is not then we'll not be able to check the dynamic
+ # linker unloaded events later in this script.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ if { $dyld_count != 1 } {
+ unsupported "dynamic linker doesn't appear to be loaded"
+ return
+ }
+
+ # Create breakpoints.
+ mi_create_breakpoint "$::srcfile:$::bp_loaded" \
+ "create b/p once libraries are loaded" \
+ -disp keep -func main -file ".*$::srcfile" -line $::bp_loaded
+ mi_create_breakpoint "$::srcfile:$::bp_main" "create b/p at dlclose" \
+ -disp keep -func main -file ".*$::srcfile" -line $::bp_main
+
+ # Run past all the dlopen and dlmopen calls.
+ mi_execute_to "exec-continue" "breakpoint-hit" main "" ".*" $::bp_loaded \
+ {"" "disp=\"keep\""} "continue until all libraries are loaded"
+
+ # Check that the dynamic linker has now been loaded multiple times.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ if { $dyld_count < 2 } {
+ unsupported "not enough instances of the dynamic linker are mapped in"
+ return
+ }
+
+ # Continue. This will run until the end of 'main', and will pass
+ # over all the dlclose calls.
+ if {[mi_send_resuming_command "exec-continue" "exec-next"] == -1} {
+ return
+ }
+
+ # As a result of all the dlclose calls we should see some library
+ # unload events. Process them now.
+ set dyld_unload_count 0
+ array set unload_counts {}
+ set still_in_use_fields_correct true
+ gdb_test_multiple "" "" -prompt $::mi_gdb_prompt {
+ -re "=library-unloaded,id=\"(\[^\"\]+)\",\[^\r\n\]+,ranges=\\\[\\{from=\"$::hex\",to=\"$::hex\"\\}\\\],still-in-use=\"(true|false)\"\r\n" {
+ set lib $expect_out(1,string)
+ set in_use $expect_out(2,string)
+ if {[is_dyln $lib]} {
+ # This is the dynamic linker being unloaded.
+ incr dyld_unload_count
+ set expected_in_use "true"
+ } else {
+ set expected_in_use "false"
+ }
+
+ if { $in_use ne $expected_in_use } {
+ set still_in_use_fields_correct false
+ }
+
+ set filename [file tail $lib]
+ incr unload_counts($filename)
+ exp_continue
+ }
+ -re "\\*stopped,reason=\"breakpoint-hit\",\[^\r\n\]+\r\n$::mi_gdb_prompt" {
+ }
+ }
+
+ # Check we saw the dynamic linker being unloaded the expected number of
+ # times.
+ gdb_assert { $dyld_unload_count == $dyld_count - 1 } \
+ "expected number of dynamic linker unloads"
+
+ gdb_assert { $still_in_use_fields_correct } \
+ "still-in-use fields were all correct"
+
+ # Check that we saw the expected number of library-unloaded events for
+ # each library. Each DESC is a list of two elements, a filename for a
+ # library, and the number of times it should have been unloaded.
+ foreach desc [list [list $::binfile_lib1 3] \
+ [list $::binfile_lib_dep 3] \
+ [list $::binfile_lib2 1]] {
+ set filename [file tail [lindex $desc 0]]
+ set count [lindex $desc 1]
+ gdb_assert { $unload_counts($filename) == $count } \
+ "check unload count for $filename"
+ }
+
+ # Check that the dynamic linker still shows as loaded exactly once.
+ set dyld_info [get_dyld_info]
+ set dyld_count [lindex $dyld_info 0]
+ gdb_assert { $dyld_count == 1 } \
+ "dynamic linker is mapped once at final b/p"
+}
+
+check_solib_unload_events
diff --git a/gdb/testsuite/gdb.mi/mi-sym-info.exp b/gdb/testsuite/gdb.mi/mi-sym-info.exp
index b8db2af..47ca515 100644
--- a/gdb/testsuite/gdb.mi/mi-sym-info.exp
+++ b/gdb/testsuite/gdb.mi/mi-sym-info.exp
@@ -245,22 +245,34 @@ mi_gdb_test "120-symbol-info-types --name _int_" \
"120\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]*$srcfile\",fullname=\"\[^\"\]+$srcfile\",symbols=\\\[$my_int_re\\\]\},\{filename=\"\[^\"\]*$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[$another_int_re\\\]\}\\\]\}" \
"List all types matching _int_"
+# Return the number of matched symbols in the last match.
+
+proc count_symbol_matches { } {
+ # `0,string`, `1,string` and `2,string` respectively contain the
+ # command + result, command and result. The symbols match is at
+ # `3,string`.
+ return [regexp -all $::fun_re $::expect_out(3,string)]
+}
+
# Test the --max-results parameter.
mi_gdb_test "121-symbol-info-functions --max-results 0" \
"121\\^done,symbols=\{\}" \
"-symbol-info-functions --max-results 0"
mi_gdb_test "122-symbol-info-functions --max-results 1 --name ^\[^_\]" \
- "122\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]*$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[(?:$f2_re|$f3_re)\\\]\}\\\]\}" \
+ "122\\^done,($debug_only_syms)" \
"-symbol-info-functions --max-results 1"
+gdb_assert {[count_symbol_matches] == 1} "-symbol-info-functions --max-results 1, result count"
mi_gdb_test "123-symbol-info-functions --max-results 2 --name ^\[^_\]" \
- "123\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]*$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[$f2_re,$f3_re\\\]\}\\\]\}" \
+ "123\\^done,($debug_only_syms)" \
"-symbol-info-functions --max-results 2"
+gdb_assert {[count_symbol_matches] == 2} "-symbol-info-functions --max-results 2, result count"
mi_gdb_test "124-symbol-info-variables --max-results 3 --name ^\[^_\]" \
- "124\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]*$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[$global_f2_re,$global_i2_re,(?:$global_i1_re|$global_f1_s2_re)\\\]\}\\\]\}" \
- "-symbol-info-types --max-results 3"
+ "124\\^done,($debug_only_syms)" \
+ "-symbol-info-variables --max-results 3"
+gdb_assert {[count_symbol_matches] == 3} "-symbol-info-variables --max-results 3, result count"
mi_gdb_test "125-symbol-info-types --max-results 4 --name another_" \
"125\\^done,symbols=\{debug=\\\[\{filename=\"\[^\"\]*$srcfile2\",fullname=\"\[^\"\]+$srcfile2\",symbols=\\\[$another_char_re,$another_float_re,$another_int_re,$another_short_re\\\]\}\\\]\}" \
diff --git a/gdb/testsuite/gdb.python/py-inferior.exp b/gdb/testsuite/gdb.python/py-inferior.exp
index 58475bd..dbde7fe 100644
--- a/gdb/testsuite/gdb.python/py-inferior.exp
+++ b/gdb/testsuite/gdb.python/py-inferior.exp
@@ -32,8 +32,8 @@ save_vars { env(ASAN_OPTIONS) env(TSAN_OPTIONS) } {
# triggers address sanitizer. Suppress the error, leaving us with just
# this warning:
# WARNING: AddressSanitizer failed to allocate 0xffffffffffffffff bytes
- set_sanitizer ASAN_OPTIONS allocator_may_return_null 1
- set_sanitizer TSAN_OPTIONS allocator_may_return_null 1
+ append_environment ASAN_OPTIONS allocator_may_return_null 1
+ append_environment TSAN_OPTIONS allocator_may_return_null 1
clean_restart ${testfile}
}
diff --git a/gdb/testsuite/gdb.python/py-symbol.exp b/gdb/testsuite/gdb.python/py-symbol.exp
index 40f9038..55cdebe 100644
--- a/gdb/testsuite/gdb.python/py-symbol.exp
+++ b/gdb/testsuite/gdb.python/py-symbol.exp
@@ -149,10 +149,12 @@ gdb_breakpoint "function_in_other_file"
gdb_continue_to_breakpoint "function_in_other_file"
gdb_test "python print (gdb.lookup_static_symbol ('rr').value ())" "99" \
"print value of rr from other file"
-gdb_test "python print (gdb.lookup_static_symbols ('rr')\[0\].value ())" "99" \
- "print value of gdb.lookup_static_symbols ('rr')\[0\], from the other file"
-gdb_test "python print (gdb.lookup_static_symbols ('rr')\[1\].value ())" "42" \
- "print value of gdb.lookup_static_symbols ('rr')\[1\], from the other file"
+# GDB doesn't really guarantee the order of these, so sort the values.
+gdb_test_no_output "python rrs = gdb.lookup_static_symbols ('rr')" \
+ "fetch all rr symbols, from the other file"
+gdb_test "python print (sorted(\[int(x.value()) for x in rrs\]))" \
+ "\\\[42, 99\\\]" \
+ "print values of all 'rr' symbols, from the other file"
# Now continue back to the first source file.
set linenum [gdb_get_line_number "Break at end."]
@@ -164,10 +166,12 @@ gdb_py_test_silent_cmd "python frame = gdb.selected_frame()" "Get Frame" 0
# static symbol from the second source file.
gdb_test "python print (gdb.lookup_static_symbol ('rr').value ())" "42" \
"print value of rr from main file"
-gdb_test "python print (gdb.lookup_static_symbols ('rr')\[0\].value ())" "99" \
- "print value of gdb.lookup_static_symbols ('rr')\[0\], from the main file"
-gdb_test "python print (gdb.lookup_static_symbols ('rr')\[1\].value ())" "42" \
- "print value of gdb.lookup_static_symbols ('rr')\[1\], from the main file"
+# This should be consistent with the first file.
+gdb_test_no_output "python rrs = gdb.lookup_static_symbols ('rr')" \
+ "fetch all rr symbols, from the main file"
+gdb_test "python print (sorted(\[int(x.value()) for x in rrs\]))" \
+ "\\\[42, 99\\\]" \
+ "print values of all 'rr' symbols, from the main file"
# Test is_variable attribute.
gdb_py_test_silent_cmd "python a = gdb.lookup_symbol(\'a\')" "Get variable a" 0
diff --git a/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp b/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
index 28d70da..942088f 100644
--- a/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
+++ b/gdb/testsuite/gdb.threads/attach-slow-waitpid.exp
@@ -83,7 +83,7 @@ proc gdb_spawn_with_ld_preload {lib} {
# 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.
- set_sanitizer_default ASAN_OPTIONS verify_asan_link_order 0
+ append_environment_default ASAN_OPTIONS verify_asan_link_order 0
gdb_start
}
diff --git a/gdb/testsuite/gdb.trace/basic-libipa.exp b/gdb/testsuite/gdb.trace/basic-libipa.exp
index dcde297..0da94af 100644
--- a/gdb/testsuite/gdb.trace/basic-libipa.exp
+++ b/gdb/testsuite/gdb.trace/basic-libipa.exp
@@ -40,7 +40,7 @@ save_vars { env(ASAN_OPTIONS) } {
# 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.
- set_sanitizer_default ASAN_OPTIONS verify_asan_link_order 0
+ append_environment_default ASAN_OPTIONS verify_asan_link_order 0
clean_restart $binfile
}
diff --git a/gdb/testsuite/gdb.tui/flush-after-run.c b/gdb/testsuite/gdb.tui/flush-after-run.c
new file mode 100644
index 0000000..f2dfded
--- /dev/null
+++ b/gdb/testsuite/gdb.tui/flush-after-run.c
@@ -0,0 +1,54 @@
+/* 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 <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+
+int
+main (int argc, char *argv[])
+{
+ /* Don't let this test stay alive forever. */
+ alarm (300);
+
+ if (argc != 2)
+ abort ();
+
+ /* Check the file doesn't already exist. */
+ const char *filename = argv[1];
+ struct stat buf;
+ if (stat (filename, &buf) == 0 || errno != ENOENT)
+ abort ();
+
+ /* Create the file, and write something into it. */
+ FILE *out = fopen (filename, "w");
+ if (out == NULL)
+ abort ();
+
+ fprintf (out, "Hello World\n");
+
+ if (fclose (out) != 0)
+ abort ();
+
+ /* Spin until the marker file is deleted. */
+ while (stat (filename, &buf) == 0)
+ sleep (1);
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.tui/flush-after-run.exp b/gdb/testsuite/gdb.tui/flush-after-run.exp
new file mode 100644
index 0000000..c222b19
--- /dev/null
+++ b/gdb/testsuite/gdb.tui/flush-after-run.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/>.
+
+# Check that the 'Starting program' message is correctly flushed to
+# the TUI terminal as soon as it is available.
+
+require allow_tui_tests
+require target_can_use_run_cmd
+
+tuiterm_env
+
+standard_testfile
+
+if {[build_executable "failed to prepare" ${testfile} ${srcfile}] == -1} {
+ return -1
+}
+
+Term::clean_restart 24 80 $testfile
+
+if {![Term::enter_tui]} {
+ unsupported "TUI not supported"
+ return
+}
+
+# Pick a name for a marker file, and ensure it doesn't exist.
+set marker_file [standard_output_file "marker"]
+file delete $marker_file
+
+# Run the inferior, which will create MARKER_FILE.
+send_gdb "run \"$marker_file\"\n"
+
+# Spin until MARKER_FILE appears.
+while { ! [file exists $marker_file] } {
+ sleep 1
+}
+
+# We now know that the inferior has started, and that the 'Starting
+# program: ' string should have been printed to the terminal. Don't
+# use Term::wait_for here as there will be no prompt after the
+# 'Starting program' message.
+gdb_assert {[Term::wait_for_region_contents 0 16 80 7 "Starting program: "]} \
+ "starting program message has appeared"
+
+# Delete MARKER_FILE. This will cause the inferior to exit.
+file delete $marker_file
+
+# Now wait for the prompt, and check that the inferior exited message
+# appeared.
+gdb_assert {[Term::wait_for ""]} \
+ "wait for prompt after inferior exits"
+Term::check_region_contents \
+ "check for inferior exited message" \
+ 0 16 80 8 \
+ "\\\[Inferior $decimal \[^\r\n\]+ exited normally\\\]"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 56dc835..d4634c8 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -45,9 +45,9 @@ proc cond_wrap { cond wrap body } {
}
}
-# Helper function for set_sanitizer/set_sanitizer_default.
+# Helper function for append_environment/append_environment_default.
-proc set_sanitizer_1 { env_var var_id val default} {
+proc append_environment_1 { env_var var_id val default} {
global env
if { ![info exists env($env_var) ]
@@ -70,17 +70,17 @@ proc set_sanitizer_1 { env_var var_id val default} {
# Add VAR_ID=VAL to ENV_VAR.
-proc set_sanitizer { env_var var_id val } {
- set_sanitizer_1 $env_var $var_id $val 0
+proc append_environment { env_var var_id val } {
+ append_environment_1 $env_var $var_id $val 0
}
# Add VAR_ID=VAL to ENV_VAR, unless ENV_VAR already contains a VAR_ID setting.
-proc set_sanitizer_default { env_var var_id val } {
- set_sanitizer_1 $env_var $var_id $val 1
+proc append_environment_default { env_var var_id val } {
+ append_environment_1 $env_var $var_id $val 1
}
-set_sanitizer_default TSAN_OPTIONS suppressions \
+append_environment_default TSAN_OPTIONS suppressions \
$srcdir/../tsan-suppressions.txt
# When using ThreadSanitizer we may run into the case that a race is detected,
@@ -89,14 +89,14 @@ set_sanitizer_default TSAN_OPTIONS suppressions \
# Try to prevent this by setting history_size to the maximum (7) by default.
# See also the ThreadSanitizer docs (
# https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags ).
-set_sanitizer_default TSAN_OPTIONS history_size 7
+append_environment_default TSAN_OPTIONS history_size 7
# If GDB is built with ASAN (and because there are leaks), it will output a
# leak report when exiting as well as exit with a non-zero (failure) status.
# This can affect tests that are sensitive to what GDB prints on stderr or its
# exit status. Add `detect_leaks=0` to the ASAN_OPTIONS environment variable
# (which will affect any spawned sub-process) to avoid this.
-set_sanitizer_default ASAN_OPTIONS detect_leaks 0
+append_environment_default ASAN_OPTIONS detect_leaks 0
# List of procs to run in gdb_finish.
set gdb_finish_hooks [list]
@@ -11016,5 +11016,38 @@ gdb_caching_proc root_user {} {
return [expr $uid == 0]
}
+# Return nul-terminated string read from section SECTION of EXEC. Return ""
+# if no such section or nul-terminated string was found. Function is useful
+# for sections ".interp" or ".gnu_debuglink".
+
+proc section_get {exec section} {
+ global subdir
+ set tmp [standard_output_file section_get.tmp]
+ set objcopy_program [gdb_find_objcopy]
+
+ set command "exec $objcopy_program -O binary --set-section-flags $section=A --change-section-address $section=0 -j $section $exec $tmp"
+ verbose -log "command is $command"
+ set result [catch $command output]
+ verbose -log "result is $result"
+ verbose -log "output is $output"
+ if {$result == 1} {
+ return ""
+ }
+ set fi [open $tmp]
+ fconfigure $fi -translation binary
+ set data [read $fi]
+ close $fi
+ file delete $tmp
+ # .interp has size $len + 1 but .gnu_debuglink contains garbage after \000.
+ set len [string first \000 $data]
+ if {$len < 0} {
+ verbose -log "section $section not found"
+ return ""
+ }
+ set retval [string range $data 0 [expr $len - 1]]
+ verbose -log "section $section is <$retval>"
+ return $retval
+}
+
# Always load compatibility stuff.
load_lib future.exp
diff --git a/gdb/testsuite/lib/gnat_debug_info_test.adb b/gdb/testsuite/lib/gnat_debug_info_test.adb
index b8f0b03..b195cb8 100644
--- a/gdb/testsuite/lib/gnat_debug_info_test.adb
+++ b/gdb/testsuite/lib/gnat_debug_info_test.adb
@@ -1,3 +1,18 @@
+-- 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/>.
+
with Ada.Text_IO;
procedure GNAT_Debug_Info_Test is
diff --git a/gdb/testsuite/lib/prelink-support.exp b/gdb/testsuite/lib/prelink-support.exp
index 894af39..3aaea0a 100644
--- a/gdb/testsuite/lib/prelink-support.exp
+++ b/gdb/testsuite/lib/prelink-support.exp
@@ -13,39 +13,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/>.
-# Return nul-terminated string read from section SECTION of EXEC. Return ""
-# if no such section or nul-terminated string was found. Function is useful
-# for sections ".interp" or ".gnu_debuglink".
-
-proc section_get {exec section} {
- global subdir
- set tmp [standard_output_file section_get.tmp]
- set objcopy_program [gdb_find_objcopy]
-
- set command "exec $objcopy_program -O binary --set-section-flags $section=A --change-section-address $section=0 -j $section $exec $tmp"
- verbose -log "command is $command"
- set result [catch $command output]
- verbose -log "result is $result"
- verbose -log "output is $output"
- if {$result == 1} {
- return ""
- }
- set fi [open $tmp]
- fconfigure $fi -translation binary
- set data [read $fi]
- close $fi
- file delete $tmp
- # .interp has size $len + 1 but .gnu_debuglink contains garbage after \000.
- set len [string first \000 $data]
- if {$len < 0} {
- verbose -log "section $section not found"
- return ""
- }
- set retval [string range $data 0 [expr $len - 1]]
- verbose -log "section $section is <$retval>"
- return $retval
-}
-
# Resolve symlinks.
proc symlink_resolve {file} {
diff --git a/gdb/tui/tui-hooks.c b/gdb/tui/tui-hooks.c
index 25358d0..69294cc 100644
--- a/gdb/tui/tui-hooks.c
+++ b/gdb/tui/tui-hooks.c
@@ -35,6 +35,7 @@
#include "tui/tui-regs.h"
#include "tui/tui-status.h"
#include "tui/tui-winsource.h"
+#include "tui/tui-wingeneral.h"
static void
tui_new_objfile_hook (struct objfile* objfile)
@@ -106,6 +107,8 @@ tui_refresh_frame_and_register_information ()
target_terminal::scoped_restore_terminal_state term_state;
target_terminal::ours_for_output ();
+ tui_batch_rendering defer;
+
if (from_stack)
{
frame_info_ptr fi;
@@ -150,6 +153,8 @@ tui_dummy_print_frame_info_listing_hook (struct symtab *s,
static void
tui_inferior_exit (struct inferior *inf)
{
+ tui_batch_rendering defer;
+
/* Leave the SingleKey mode to make sure the gdb prompt is visible. */
tui_set_key_mode (TUI_COMMAND_MODE);
tui_show_frame_info (0);
diff --git a/gdb/tui/tui-status.c b/gdb/tui/tui-status.c
index 5239415..d76c2db 100644
--- a/gdb/tui/tui-status.c
+++ b/gdb/tui/tui-status.c
@@ -253,8 +253,8 @@ tui_status_window::rerender ()
waddstr (handle.get (), string.c_str ());
wclrtoeol (handle.get ());
(void) wstandend (handle.get ());
+
refresh_window ();
- wmove (handle.get (), 0, 0);
}
/* Function to print the frame information for the TUI. The windows
diff --git a/gdb/tui/tui-win.c b/gdb/tui/tui-win.c
index 33b24d8..95639f5 100644
--- a/gdb/tui/tui-win.c
+++ b/gdb/tui/tui-win.c
@@ -969,6 +969,8 @@ tui_set_win_size (const char *arg, bool set_width_p)
new_size = curr_size + input_no;
}
+ tui_batch_rendering defer;
+
/* Now change the window's height, and adjust
all other windows around it. */
if (set_width_p)
diff --git a/gdb/tui/tui-wingeneral.c b/gdb/tui/tui-wingeneral.c
index 369d152..56e7abd 100644
--- a/gdb/tui/tui-wingeneral.c
+++ b/gdb/tui/tui-wingeneral.c
@@ -56,7 +56,12 @@ void
tui_win_info::refresh_window ()
{
if (handle != NULL)
- wnoutrefresh (handle.get ());
+ {
+ if (suppress_output)
+ wnoutrefresh (handle.get ());
+ else
+ wrefresh (handle.get ());
+ }
}
/* Draw a border around the window. */
diff --git a/gdb/tui/tui-winsource.c b/gdb/tui/tui-winsource.c
index 83d9fc0..587162b 100644
--- a/gdb/tui/tui-winsource.c
+++ b/gdb/tui/tui-winsource.c
@@ -37,6 +37,7 @@
#include "tui/tui-source.h"
#include "tui/tui-disasm.h"
#include "tui/tui-location.h"
+#include "tui/tui-wingeneral.h"
#include "gdb_curses.h"
/* Function to display the "main" routine. */
@@ -52,10 +53,10 @@ tui_display_main ()
tui_get_begin_asm_address (&gdbarch, &addr);
if (addr != (CORE_ADDR) 0)
{
- struct symtab *s;
+ tui_batch_rendering defer;
tui_update_source_windows_with_addr (gdbarch, addr);
- s = find_pc_line_symtab (addr);
+ struct symtab *s = find_pc_line_symtab (addr);
tui_location.set_location (s);
}
}
@@ -607,6 +608,8 @@ tui_source_window_base::set_is_exec_point_at (struct tui_line_or_address l)
void
tui_update_all_breakpoint_info (struct breakpoint *being_deleted)
{
+ tui_batch_rendering defer;
+
for (tui_source_window_base *win : tui_source_windows ())
{
if (win->update_breakpoint_info (being_deleted, false))
diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c
index 59aa1bc..8b79d8b 100644
--- a/gdb/tui/tui.c
+++ b/gdb/tui/tui.c
@@ -387,6 +387,8 @@ tui_enable (void)
if (tui_active)
return;
+ tui_batch_rendering defer;
+
/* To avoid to initialize curses when gdb starts, there is a deferred
curses initialization. This initialization is made only once
and the first time the curses mode is entered. */
diff --git a/gdb/unittests/array-view-selftests.c b/gdb/unittests/array-view-selftests.c
index c07b572..cc3fcfd 100644
--- a/gdb/unittests/array-view-selftests.c
+++ b/gdb/unittests/array-view-selftests.c
@@ -554,6 +554,19 @@ run_tests ()
SELF_CHECK (view[i] == data[i]);
}
+ /* gdb::make_array_view with an array. */
+ {
+ const gdb_byte data[] = {0x55, 0x66, 0x77, 0x88};
+ const auto len = sizeof (data) / sizeof (data[0]);
+ const auto view = gdb::make_array_view (data);
+
+ SELF_CHECK (view.data () == data);
+ SELF_CHECK (view.size () == len);
+
+ for (std::size_t i = 0; i < len; ++i)
+ SELF_CHECK (view[i] == data[i]);
+ }
+
/* Test slicing. */
{
gdb_byte data[] = {0x55, 0x66, 0x77, 0x88, 0x99};
diff --git a/gdb/i386-gnu-nat.c b/gdb/x86-gnu-nat.c
index 52d4a4b..da3b7e9 100644
--- a/gdb/i386-gnu-nat.c
+++ b/gdb/x86-gnu-nat.c
@@ -34,7 +34,13 @@ extern "C"
#include "floatformat.h"
#include "regcache.h"
+
+#ifdef __x86_64__
+#include "amd64-tdep.h"
+#include "amd64-nat.h"
+#else
#include "i386-tdep.h"
+#endif
#include "inf-child.h"
#include "i387-tdep.h"
@@ -42,21 +48,82 @@ extern "C"
/* Offset to the thread_state_t location where REG is stored. */
#define REG_OFFSET(reg) offsetof (struct i386_thread_state, reg)
+#ifdef __x86_64__
+
/* At REG_OFFSET[N] is the offset to the thread_state_t location where
the GDB register N is stored. */
-static int reg_offset[] =
+static int amd64_gnu_thread_state_reg_offset[] =
{
- REG_OFFSET (eax), REG_OFFSET (ecx), REG_OFFSET (edx), REG_OFFSET (ebx),
- REG_OFFSET (uesp), REG_OFFSET (ebp), REG_OFFSET (esi), REG_OFFSET (edi),
- REG_OFFSET (eip), REG_OFFSET (efl), REG_OFFSET (cs), REG_OFFSET (ss),
- REG_OFFSET (ds), REG_OFFSET (es), REG_OFFSET (fs), REG_OFFSET (gs)
+ REG_OFFSET (rax), /* %rax */
+ REG_OFFSET (rbx), /* %rbx */
+ REG_OFFSET (rcx), /* %rcx */
+ REG_OFFSET (rdx), /* %rdx */
+ REG_OFFSET (rsi), /* %rsi */
+ REG_OFFSET (rdi), /* %rdi */
+ REG_OFFSET (rbp), /* %rbp */
+ REG_OFFSET (ursp), /* %rsp */
+ REG_OFFSET (r8), /* %r8 ... */
+ REG_OFFSET (r9),
+ REG_OFFSET (r10),
+ REG_OFFSET (r11),
+ REG_OFFSET (r12),
+ REG_OFFSET (r13),
+ REG_OFFSET (r14),
+ REG_OFFSET (r15), /* ... %r15 */
+ REG_OFFSET (rip), /* %rip */
+ REG_OFFSET (rfl), /* %rflags */
+ REG_OFFSET (cs) /* %cs */
};
-#define REG_ADDR(state, regnum) ((char *)(state) + reg_offset[regnum])
+static const int amd64_gnu_thread_state_num_regs =
+ ARRAY_SIZE (amd64_gnu_thread_state_reg_offset);
+
+#define REG_ADDR(state, regnum) \
+ ((char *)(state) + amd64_gnu_thread_state_reg_offset[regnum])
+#define VALID_REGISTER(regnum) \
+ ((regnum) >= 0 && (regnum) < amd64_gnu_thread_state_num_regs)
+#define NUM_GREGS amd64_gnu_thread_state_num_regs
+#define FLAGS_REGISTER rfl
+
+#else
+
+/* At REG_OFFSET[N] is the offset to the thread_state_t location where
+ the GDB register N is stored. */
+static int i386_gnu_thread_state_reg_offset[] =
+{
+ REG_OFFSET (eax), /* %eax */
+ REG_OFFSET (ecx), /* %ecx */
+ REG_OFFSET (edx), /* %edx */
+ REG_OFFSET (ebx), /* %ebx */
+ REG_OFFSET (uesp), /* %esp */
+ REG_OFFSET (ebp), /* %ebp */
+ REG_OFFSET (esi), /* %esi */
+ REG_OFFSET (edi), /* %edi */
+ REG_OFFSET (eip), /* %eip */
+ REG_OFFSET (efl), /* %efl */
+ REG_OFFSET (cs), /* %cs */
+ REG_OFFSET (ss), /* %ss */
+ REG_OFFSET (ds), /* %ds */
+ REG_OFFSET (es), /* %es */
+ REG_OFFSET (fs), /* %fs */
+ REG_OFFSET (gs) /* gs */
+};
+
+static const int i386_gnu_thread_state_num_regs =
+ ARRAY_SIZE (i386_gnu_thread_state_reg_offset);
+
+#define REG_ADDR(state, regnum) \
+ ((char *)(state) + i386_gnu_thread_state_reg_offset[regnum])
+#define VALID_REGISTER(regnum) \
+ ((regnum) >= 0 && (regnum) < i386_gnu_thread_state_num_regs)
+#define NUM_GREGS i386_gnu_thread_state_num_regs
+#define FLAGS_REGISTER efl
+
+#endif /* __x86_64__ */
-/* The i386 GNU Hurd target. */
+/* The x86 GNU Hurd target. */
#ifdef i386_DEBUG_STATE
using gnu_base_target = x86_nat_target<gnu_nat_target>;
@@ -64,13 +131,13 @@ using gnu_base_target = x86_nat_target<gnu_nat_target>;
using gnu_base_target = gnu_nat_target;
#endif
-struct i386_gnu_nat_target final : public gnu_base_target
+struct x86_gnu_nat_target final : public gnu_base_target
{
void fetch_registers (struct regcache *, int) override;
void store_registers (struct regcache *, int) override;
};
-static i386_gnu_nat_target the_i386_gnu_nat_target;
+static x86_gnu_nat_target the_x86_gnu_nat_target;
/* Get the whole floating-point state of THREAD and record the values
of the corresponding (pseudo) registers. */
@@ -105,7 +172,7 @@ fetch_fpregs (struct regcache *regcache, struct proc *thread)
/* Fetch register REGNO, or all regs if REGNO is -1. */
void
-i386_gnu_nat_target::fetch_registers (struct regcache *regcache, int regno)
+x86_gnu_nat_target::fetch_registers (struct regcache *regcache, int regno)
{
struct proc *thread;
ptid_t ptid = regcache->ptid ();
@@ -118,7 +185,7 @@ i386_gnu_nat_target::fetch_registers (struct regcache *regcache, int regno)
error (_("Can't fetch registers from thread %s: No such thread"),
target_pid_to_str (ptid).c_str ());
- if (regno < I386_NUM_GREGS || regno == -1)
+ if (VALID_REGISTER (regno) || regno == -1)
{
thread_state_t state;
@@ -137,7 +204,7 @@ i386_gnu_nat_target::fetch_registers (struct regcache *regcache, int regno)
proc_debug (thread, "fetching all register");
- for (i = 0; i < I386_NUM_GREGS; i++)
+ for (i = 0; i < NUM_GREGS; i++)
regcache->raw_supply (i, REG_ADDR (state, i));
thread->fetched_regs = ~0;
}
@@ -152,7 +219,7 @@ i386_gnu_nat_target::fetch_registers (struct regcache *regcache, int regno)
}
}
- if (regno >= I386_NUM_GREGS || regno == -1)
+ if (!VALID_REGISTER(regno) || regno == -1)
{
proc_debug (thread, "fetching floating-point registers");
@@ -195,7 +262,7 @@ store_fpregs (const struct regcache *regcache, struct proc *thread, int regno)
/* Store at least register REGNO, or all regs if REGNO == -1. */
void
-i386_gnu_nat_target::store_registers (struct regcache *regcache, int regno)
+x86_gnu_nat_target::store_registers (struct regcache *regcache, int regno)
{
struct proc *thread;
struct gdbarch *gdbarch = regcache->arch ();
@@ -209,7 +276,7 @@ i386_gnu_nat_target::store_registers (struct regcache *regcache, int regno)
error (_("Couldn't store registers into thread %s: No such thread"),
target_pid_to_str (ptid).c_str ());
- if (regno < I386_NUM_GREGS || regno == -1)
+ if (VALID_REGISTER (regno) || regno == -1)
{
thread_state_t state;
thread_state_data_t old_state;
@@ -230,14 +297,14 @@ i386_gnu_nat_target::store_registers (struct regcache *regcache, int regno)
/* Save the T bit. We might try to restore the %eflags register
below, but changing the T bit would seriously confuse GDB. */
- trace = ((struct i386_thread_state *)state)->efl & 0x100;
+ trace = ((struct i386_thread_state *)state)->FLAGS_REGISTER & 0x100;
if (!was_aborted && was_valid)
/* See which registers have changed after aborting the thread. */
{
int check_regno;
- for (check_regno = 0; check_regno < I386_NUM_GREGS; check_regno++)
+ for (check_regno = 0; check_regno < NUM_GREGS; check_regno++)
if ((thread->fetched_regs & (1 << check_regno))
&& memcpy (REG_ADDR (&old_state, check_regno),
REG_ADDR (state, check_regno),
@@ -262,7 +329,7 @@ i386_gnu_nat_target::store_registers (struct regcache *regcache, int regno)
proc_debug (thread, "storing all registers");
- for (i = 0; i < I386_NUM_GREGS; i++)
+ for (i = 0; i < NUM_GREGS; i++)
if (REG_VALID == regcache->get_register_status (i))
regcache->raw_collect (i, REG_ADDR (state, i));
}
@@ -276,11 +343,11 @@ i386_gnu_nat_target::store_registers (struct regcache *regcache, int regno)
}
/* Restore the T bit. */
- ((struct i386_thread_state *)state)->efl &= ~0x100;
- ((struct i386_thread_state *)state)->efl |= trace;
+ ((struct i386_thread_state *)state)->FLAGS_REGISTER &= ~0x100;
+ ((struct i386_thread_state *)state)->FLAGS_REGISTER |= trace;
}
- if (regno >= I386_NUM_GREGS || regno == -1)
+ if (!VALID_REGISTER (regno) || regno == -1)
{
proc_debug (thread, "storing floating-point registers");
@@ -295,7 +362,7 @@ i386_gnu_nat_target::store_registers (struct regcache *regcache, int regno)
/* Get debug registers for thread THREAD. */
static void
-i386_gnu_dr_get (struct i386_debug_state *regs, struct proc *thread)
+x86_gnu_dr_get (struct i386_debug_state *regs, struct proc *thread)
{
mach_msg_type_number_t count = i386_DEBUG_STATE_COUNT;
kern_return_t err;
@@ -310,7 +377,7 @@ i386_gnu_dr_get (struct i386_debug_state *regs, struct proc *thread)
/* Set debug registers for thread THREAD. */
static void
-i386_gnu_dr_set (const struct i386_debug_state *regs, struct proc *thread)
+x86_gnu_dr_set (const struct i386_debug_state *regs, struct proc *thread)
{
kern_return_t err;
@@ -324,23 +391,23 @@ i386_gnu_dr_set (const struct i386_debug_state *regs, struct proc *thread)
/* Set DR_CONTROL in THREAD. */
static void
-i386_gnu_dr_set_control_one (struct proc *thread, void *arg)
+x86_gnu_dr_set_control_one (struct proc *thread, void *arg)
{
unsigned long *control = (unsigned long *) arg;
struct i386_debug_state regs;
- i386_gnu_dr_get (&regs, thread);
+ x86_gnu_dr_get (&regs, thread);
regs.dr[DR_CONTROL] = *control;
- i386_gnu_dr_set (&regs, thread);
+ x86_gnu_dr_set (&regs, thread);
}
/* Set DR_CONTROL to CONTROL in all threads. */
static void
-i386_gnu_dr_set_control (unsigned long control)
+x86_gnu_dr_set_control (unsigned long control)
{
inf_update_procs (gnu_current_inf);
- inf_threads (gnu_current_inf, i386_gnu_dr_set_control_one, &control);
+ inf_threads (gnu_current_inf, x86_gnu_dr_set_control_one, &control);
}
/* Parameters to set a debugging address. */
@@ -354,20 +421,20 @@ struct reg_addr
/* Set address REGNUM (zero based) to ADDR in THREAD. */
static void
-i386_gnu_dr_set_addr_one (struct proc *thread, void *arg)
+x86_gnu_dr_set_addr_one (struct proc *thread, void *arg)
{
struct reg_addr *reg_addr = (struct reg_addr *) arg;
struct i386_debug_state regs;
- i386_gnu_dr_get (&regs, thread);
+ x86_gnu_dr_get (&regs, thread);
regs.dr[reg_addr->regnum] = reg_addr->addr;
- i386_gnu_dr_set (&regs, thread);
+ x86_gnu_dr_set (&regs, thread);
}
/* Set address REGNUM (zero based) to ADDR in all threads. */
static void
-i386_gnu_dr_set_addr (int regnum, CORE_ADDR addr)
+x86_gnu_dr_set_addr (int regnum, CORE_ADDR addr)
{
struct reg_addr reg_addr;
@@ -377,13 +444,13 @@ i386_gnu_dr_set_addr (int regnum, CORE_ADDR addr)
reg_addr.addr = addr;
inf_update_procs (gnu_current_inf);
- inf_threads (gnu_current_inf, i386_gnu_dr_set_addr_one, &reg_addr);
+ inf_threads (gnu_current_inf, x86_gnu_dr_set_addr_one, &reg_addr);
}
/* Get debug register REGNUM value from only the one LWP of PTID. */
static unsigned long
-i386_gnu_dr_get_reg (ptid_t ptid, int regnum)
+x86_gnu_dr_get_reg (ptid_t ptid, int regnum)
{
struct i386_debug_state regs;
struct proc *thread;
@@ -392,7 +459,7 @@ i386_gnu_dr_get_reg (ptid_t ptid, int regnum)
inf_update_procs (gnu_current_inf);
thread = inf_tid_to_thread (gnu_current_inf, ptid.lwp ());
- i386_gnu_dr_get (&regs, thread);
+ x86_gnu_dr_get (&regs, thread);
return regs.dr[regnum];
}
@@ -400,46 +467,50 @@ i386_gnu_dr_get_reg (ptid_t ptid, int regnum)
/* Return the inferior's debug register REGNUM. */
static CORE_ADDR
-i386_gnu_dr_get_addr (int regnum)
+x86_gnu_dr_get_addr (int regnum)
{
gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
- return i386_gnu_dr_get_reg (inferior_ptid, regnum);
+ return x86_gnu_dr_get_reg (inferior_ptid, regnum);
}
/* Get DR_STATUS from only the one thread of INFERIOR_PTID. */
static unsigned long
-i386_gnu_dr_get_status (void)
+x86_gnu_dr_get_status (void)
{
- return i386_gnu_dr_get_reg (inferior_ptid, DR_STATUS);
+ return x86_gnu_dr_get_reg (inferior_ptid, DR_STATUS);
}
/* Return the inferior's DR7 debug control register. */
static unsigned long
-i386_gnu_dr_get_control (void)
+x86_gnu_dr_get_control (void)
{
- return i386_gnu_dr_get_reg (inferior_ptid, DR_CONTROL);
+ return x86_gnu_dr_get_reg (inferior_ptid, DR_CONTROL);
}
#endif /* i386_DEBUG_STATE */
-void _initialize_i386gnu_nat ();
+void _initialize_x86_gnu_nat ();
void
-_initialize_i386gnu_nat ()
+_initialize_x86_gnu_nat ()
{
#ifdef i386_DEBUG_STATE
- x86_dr_low.set_control = i386_gnu_dr_set_control;
+ x86_dr_low.set_control = x86_gnu_dr_set_control;
gdb_assert (DR_FIRSTADDR == 0 && DR_LASTADDR < i386_DEBUG_STATE_COUNT);
- x86_dr_low.set_addr = i386_gnu_dr_set_addr;
- x86_dr_low.get_addr = i386_gnu_dr_get_addr;
- x86_dr_low.get_status = i386_gnu_dr_get_status;
- x86_dr_low.get_control = i386_gnu_dr_get_control;
+ x86_dr_low.set_addr = x86_gnu_dr_set_addr;
+ x86_dr_low.get_addr = x86_gnu_dr_get_addr;
+ x86_dr_low.get_status = x86_gnu_dr_get_status;
+ x86_dr_low.get_control = x86_gnu_dr_get_control;
+#ifdef __x86_64__
+ x86_set_debug_register_length (8);
+#else
x86_set_debug_register_length (4);
+#endif
#endif /* i386_DEBUG_STATE */
- gnu_target = &the_i386_gnu_nat_target;
+ gnu_target = &the_x86_gnu_nat_target;
/* Register the target. */
- add_inf_child_target (&the_i386_gnu_nat_target);
+ add_inf_child_target (&the_x86_gnu_nat_target);
}