aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/amd64-linux-nat.c25
-rw-r--r--gdb/i386-linux-nat.c24
-rw-r--r--gdb/i386-linux-tdep.c30
-rw-r--r--gdb/linux-tdep.c5
-rw-r--r--gdb/linux-tdep.h10
-rw-r--r--gdb/target.h2
-rw-r--r--gdb/x86-linux-nat.c86
-rw-r--r--gdb/x86-linux-nat.h13
8 files changed, 192 insertions, 3 deletions
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index 75e63c6..932edc0 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -52,6 +52,14 @@ struct amd64_linux_nat_target final : public x86_linux_nat_target
bool low_siginfo_fixup (siginfo_t *ptrace, gdb_byte *inf, int direction)
override;
+
+ /* Override default xfer_partial, adding support for x86 specific OBJECT
+ types. */
+ enum target_xfer_status xfer_partial (enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len) override;
};
static amd64_linux_nat_target the_amd64_linux_nat_target;
@@ -330,6 +338,23 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
}
+/* See class declaration above. */
+
+enum target_xfer_status
+amd64_linux_nat_target::xfer_partial (enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
+{
+ if (object == TARGET_OBJECT_X86_LINUX_TLS_DESC)
+ return x86_linux_nat_target::xfer_tls_desc (readbuf, writebuf, offset,
+ len, xfered_len, 12);
+
+ return x86_linux_nat_target::xfer_partial (object, annex, readbuf, writebuf,
+ offset, len, xfered_len);
+}
+
+
/* This function is called by libthread_db as part of its handling of
a request for a thread's local storage address. */
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index a5d5582..00dc4d1 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -44,6 +44,14 @@ struct i386_linux_nat_target final : public x86_linux_nat_target
/* Override the default ptrace resume method. */
void low_resume (ptid_t ptid, int step, enum gdb_signal sig) override;
+
+ /* Override default xfer_partial, adding support for x86 specific OBJECT
+ types. */
+ enum target_xfer_status xfer_partial (enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len) override;
};
static i386_linux_nat_target the_i386_linux_nat_target;
@@ -696,6 +704,22 @@ i386_linux_nat_target::low_resume (ptid_t ptid, int step, enum gdb_signal signal
perror_with_name (("ptrace"));
}
+/* See class declaration above. */
+
+enum target_xfer_status
+i386_linux_nat_target::xfer_partial (enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
+{
+ if (object == TARGET_OBJECT_X86_LINUX_TLS_DESC)
+ return x86_linux_nat_target::xfer_tls_desc (readbuf, writebuf, offset,
+ len, xfered_len, 6);
+
+ return x86_linux_nat_target::xfer_partial (object, annex, readbuf, writebuf,
+ offset, len, xfered_len);
+}
+
void _initialize_i386_linux_nat ();
void
_initialize_i386_linux_nat ()
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 4b05cc6..929eef0 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -37,6 +37,8 @@
#include "arch-utils.h"
#include "xml-syscall.h"
#include "infrun.h"
+#include "elf/common.h"
+#include "elf-bfd.h"
#include "i387-tdep.h"
#include "gdbsupport/x86-xstate.h"
@@ -1237,6 +1239,31 @@ i386_linux_displaced_step_copy_insn (struct gdbarch *gdbarch,
return closure_;
}
+/* Wrap around linux_make_corefile_notes, this adds the TLS related note. */
+
+static gdb::unique_xmalloc_ptr<char>
+i386_linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
+{
+ gdb::unique_xmalloc_ptr<char> note_data
+ = linux_make_corefile_notes (gdbarch, obfd, note_size);
+
+ /* Fetch the TLS data. */
+ std::optional<gdb::byte_vector> tls =
+ target_read_alloc (current_inferior ()->top_target (),
+ TARGET_OBJECT_X86_LINUX_TLS_DESC, nullptr);
+ if (tls.has_value () && !tls->empty ())
+ {
+ note_data.reset (elfcore_write_note (obfd, note_data.release (),
+ note_size, "LINUX", NT_386_TLS,
+ tls->data (), tls->size ()));
+
+ if (!note_data)
+ return nullptr;
+ }
+
+ return note_data;
+}
+
static void
i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
@@ -1488,6 +1515,9 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_I386);
set_gdbarch_get_syscall_number (gdbarch,
i386_linux_get_syscall_number);
+
+ /* Custom core file notes function adds TLS descriptors. */
+ set_gdbarch_make_corefile_notes (gdbarch, i386_linux_make_corefile_notes);
}
void _initialize_i386_linux_tdep ();
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 141c119..4dbe67b 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2343,10 +2343,9 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
return 1;
}
-/* Build the note section for a corefile, and return it in a malloc
- buffer. */
+/* See linux-tdep.h. */
-static gdb::unique_xmalloc_ptr<char>
+gdb::unique_xmalloc_ptr<char>
linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
{
struct elf_internal_linux_prpsinfo prpsinfo;
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index 7485fc1..b45535e 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -117,4 +117,14 @@ extern CORE_ADDR linux_get_hwcap2 ();
extern struct link_map_offsets *linux_ilp32_fetch_link_map_offsets ();
extern struct link_map_offsets *linux_lp64_fetch_link_map_offsets ();
+/* Build the note section for a corefile, and return it in an allocated
+ buffer. OBFD is the bfd object the notes are being written into,
+ GDBARCH is the architecture of the notes to be written. *NOTE_SIZE will
+ be updated with the size of the allocated buffer. If something goes
+ wrong then this function returns NULL. *NOTE_SIZE can be updated even
+ if NULL is returned, but the updated value is meaningless. */
+
+extern gdb::unique_xmalloc_ptr<char>
+linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size);
+
#endif /* GDB_LINUX_TDEP_H */
diff --git a/gdb/target.h b/gdb/target.h
index 004494d..202ddf4 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -208,6 +208,8 @@ enum target_object
TARGET_OBJECT_FREEBSD_VMMAP,
/* FreeBSD process strings. */
TARGET_OBJECT_FREEBSD_PS_STRINGS,
+ /* The x86/Linux GDT entries for TLS regions. */
+ TARGET_OBJECT_X86_LINUX_TLS_DESC,
/* Possible future objects: TARGET_OBJECT_FILE, ... */
};
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index fc7c5f6..22c7e47 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -43,6 +43,13 @@
#include "nat/linux-ptrace.h"
#include "nat/x86-linux-tdesc.h"
+#include <asm/ldt.h>
+#include <linux/unistd.h>
+#include <linux/elf.h>
+#include <sys/user.h>
+#include <sys/procfs.h>
+#include <sys/uio.h>
+
/* linux_nat_target::low_new_fork implementation. */
void
@@ -166,6 +173,85 @@ x86_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
return linux_btrace_conf (btinfo);
}
+/* ... */
+
+static int
+get_thread_area (pid_t pid, unsigned int idx, struct user_desc *ud)
+{
+ void *addr = (void *) (uintptr_t) idx;
+
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+ if (ptrace (PTRACE_GET_THREAD_AREA, pid, addr, ud) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Get all TLS area data. */
+
+static gdb::byte_vector
+x86_linux_get_tls_desc (unsigned int base_gdt_idx)
+{
+ static_assert (sizeof (unsigned int) == 4);
+
+ struct user_desc tls_desc[3];
+ memset (tls_desc, 0, sizeof (tls_desc));
+
+ pid_t pid = inferior_ptid.pid ();
+
+ /* Set true if we see a non-empty GDT entry. */
+ bool seen_non_empty = false;
+
+ /* Linux reserves 3 GDT entries for TLS. */
+ for (unsigned int pos = 0; pos < 3; ++pos)
+ {
+ if (get_thread_area (pid, base_gdt_idx + pos, &tls_desc[pos]) != 0)
+ return {};
+ seen_non_empty |= (tls_desc[pos].base_addr != 0 || tls_desc[pos].limit != 0);
+ }
+
+ /* The kernel doesn't emit NT_I386_TLS if all the descriptors are empty. */
+ if (!seen_non_empty)
+ return {};
+
+ /* Copy the descriptors into a byte vector for return. */
+ gdb::byte_vector buf;
+ buf.resize (sizeof (tls_desc));
+ memcpy (buf.data (), tls_desc, sizeof (tls_desc));
+ return buf;
+}
+
+/* See x86-linux-nat.h. */
+
+enum target_xfer_status
+x86_linux_nat_target::xfer_tls_desc (gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len, ULONGEST *xfered_len,
+ uint32_t gdt_base_idx)
+{
+ /* Don't allow writting to the TLS GDT descriptors. */
+ if (writebuf != nullptr)
+ return TARGET_XFER_E_IO;
+
+ gdb::byte_vector buf = x86_linux_get_tls_desc (gdt_base_idx);
+ if (buf.empty ())
+ return TARGET_XFER_E_IO;
+
+ ssize_t buf_size = buf.size ();
+ if (offset >= buf_size)
+ return TARGET_XFER_EOF;
+ buf_size -= offset;
+
+ if (buf_size > len)
+ buf_size = len;
+
+ memcpy (readbuf, buf.data () + offset, buf_size);
+ *xfered_len = buf_size;
+ return TARGET_XFER_OK;
+}
+
/* Helper for ps_get_thread_area. Sets BASE_ADDR to a pointer to
diff --git a/gdb/x86-linux-nat.h b/gdb/x86-linux-nat.h
index a62cc4d..9ef1784 100644
--- a/gdb/x86-linux-nat.h
+++ b/gdb/x86-linux-nat.h
@@ -79,6 +79,19 @@ protected:
/* Override the GNU/Linux inferior startup hook. */
void post_startup_inferior (ptid_t) override;
+ /* Read TLS descriptor information from the target. Writing is TLS
+ descriptor information is not supported. The GDB_BASE_IDX is the
+ array index into the Linux kernel get_thread_area data; the index
+ differs between i386 and amd64, but is a fixed value for each. All
+ other arguments are as for xfer_partial.
+
+ If there is no TLS descriptor information then this function will
+ return TARGET_XFER_E_IO. */
+ enum target_xfer_status xfer_tls_desc
+ (gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, ULONGEST len, ULONGEST *xfered_len,
+ uint32_t gdt_base_idx);
+
private:
x86_xsave_layout m_xsave_layout;
};