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;
}
\f
+/* 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);
+}
+\f
+
/* This function is called by libthread_db as part of its handling of
a request for a thread's local storage address. */
/* 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;
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 ()
#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"
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)
{
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 ();
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;
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 */
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, ... */
};
#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
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;
+}
+
\f
/* Helper for ps_get_thread_area. Sets BASE_ADDR to a pointer to
/* 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;
};