Index: gdb/gdbserver/linux-x86-low.c |
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c |
index 85ffa526964177a0231c7390f400f922e096e63b..4ea284eebafd16fea56c1ea5165dbe203ece2e30 100644 |
--- a/gdb/gdbserver/linux-x86-low.c |
+++ b/gdb/gdbserver/linux-x86-low.c |
@@ -20,6 +20,7 @@ |
#include <stddef.h> |
#include <signal.h> |
#include <limits.h> |
+#include <inttypes.h> |
#include "server.h" |
#include "linux-low.h" |
#include "i387-fp.h" |
@@ -28,6 +29,7 @@ |
#include "elf/common.h" |
#include "gdb_proc_service.h" |
+#include "agent.h" |
/* Defined in auto-generated file i386-linux.c. */ |
void init_registers_i386_linux (void); |
@@ -39,6 +41,10 @@ void init_registers_i386_avx_linux (void); |
void init_registers_amd64_avx_linux (void); |
/* Defined in auto-generated file i386-mmx-linux.c. */ |
void init_registers_i386_mmx_linux (void); |
+/* Defined in auto-generated file x32-linux.c. */ |
+void init_registers_x32_linux (void); |
+/* Defined in auto-generated file x32-avx-linux.c. */ |
+void init_registers_x32_avx_linux (void); |
static unsigned char jump_insn[] = { 0xe9, 0, 0, 0, 0 }; |
static unsigned char small_jump_insn[] = { 0x66, 0xe9, 0, 0 }; |
@@ -654,6 +660,7 @@ static void |
x86_linux_prepare_to_resume (struct lwp_info *lwp) |
{ |
ptid_t ptid = ptid_of (lwp); |
+ int clear_status = 0; |
if (lwp->arch_private->debug_registers_changed) |
{ |
@@ -664,14 +671,23 @@ x86_linux_prepare_to_resume (struct lwp_info *lwp) |
= &proc->private->arch_private->debug_reg_state; |
for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) |
- x86_linux_dr_set (ptid, i, state->dr_mirror[i]); |
+ if (state->dr_ref_count[i] > 0) |
+ { |
+ x86_linux_dr_set (ptid, i, state->dr_mirror[i]); |
+ |
+ /* If we're setting a watchpoint, any change the inferior |
+ had done itself to the debug registers needs to be |
+ discarded, otherwise, i386_low_stopped_data_address can |
+ get confused. */ |
+ clear_status = 1; |
+ } |
x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); |
lwp->arch_private->debug_registers_changed = 0; |
} |
- if (lwp->stopped_by_watchpoint) |
+ if (clear_status || lwp->stopped_by_watchpoint) |
x86_linux_dr_set (ptid, DR_STATUS, 0); |
} |
@@ -764,6 +780,67 @@ typedef struct compat_siginfo |
} _sifields; |
} compat_siginfo_t; |
+/* For x32, clock_t in _sigchld is 64bit aligned at 4 bytes. */ |
+typedef long __attribute__ ((__aligned__ (4))) compat_x32_clock_t; |
+ |
+typedef struct compat_x32_siginfo |
+{ |
+ int si_signo; |
+ int si_errno; |
+ int si_code; |
+ |
+ union |
+ { |
+ int _pad[((128 / sizeof (int)) - 3)]; |
+ |
+ /* kill() */ |
+ struct |
+ { |
+ unsigned int _pid; |
+ unsigned int _uid; |
+ } _kill; |
+ |
+ /* POSIX.1b timers */ |
+ struct |
+ { |
+ compat_timer_t _tid; |
+ int _overrun; |
+ compat_sigval_t _sigval; |
+ } _timer; |
+ |
+ /* POSIX.1b signals */ |
+ struct |
+ { |
+ unsigned int _pid; |
+ unsigned int _uid; |
+ compat_sigval_t _sigval; |
+ } _rt; |
+ |
+ /* SIGCHLD */ |
+ struct |
+ { |
+ unsigned int _pid; |
+ unsigned int _uid; |
+ int _status; |
+ compat_x32_clock_t _utime; |
+ compat_x32_clock_t _stime; |
+ } _sigchld; |
+ |
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ |
+ struct |
+ { |
+ unsigned int _addr; |
+ } _sigfault; |
+ |
+ /* SIGPOLL */ |
+ struct |
+ { |
+ int _band; |
+ int _fd; |
+ } _sigpoll; |
+ } _sifields; |
+} compat_x32_siginfo_t __attribute__ ((__aligned__ (8))); |
+ |
#define cpt_si_pid _sifields._kill._pid |
#define cpt_si_uid _sifields._kill._uid |
#define cpt_si_timerid _sifields._timer._tid |
@@ -897,6 +974,122 @@ siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from) |
} |
} |
+static void |
+compat_x32_siginfo_from_siginfo (compat_x32_siginfo_t *to, |
+ siginfo_t *from) |
+{ |
+ memset (to, 0, sizeof (*to)); |
+ |
+ to->si_signo = from->si_signo; |
+ to->si_errno = from->si_errno; |
+ to->si_code = from->si_code; |
+ |
+ if (to->si_code == SI_TIMER) |
+ { |
+ to->cpt_si_timerid = from->si_timerid; |
+ to->cpt_si_overrun = from->si_overrun; |
+ to->cpt_si_ptr = (intptr_t) from->si_ptr; |
+ } |
+ else if (to->si_code == SI_USER) |
+ { |
+ to->cpt_si_pid = from->si_pid; |
+ to->cpt_si_uid = from->si_uid; |
+ } |
+ else if (to->si_code < 0) |
+ { |
+ to->cpt_si_pid = from->si_pid; |
+ to->cpt_si_uid = from->si_uid; |
+ to->cpt_si_ptr = (intptr_t) from->si_ptr; |
+ } |
+ else |
+ { |
+ switch (to->si_signo) |
+ { |
+ case SIGCHLD: |
+ to->cpt_si_pid = from->si_pid; |
+ to->cpt_si_uid = from->si_uid; |
+ to->cpt_si_status = from->si_status; |
+ to->cpt_si_utime = from->si_utime; |
+ to->cpt_si_stime = from->si_stime; |
+ break; |
+ case SIGILL: |
+ case SIGFPE: |
+ case SIGSEGV: |
+ case SIGBUS: |
+ to->cpt_si_addr = (intptr_t) from->si_addr; |
+ break; |
+ case SIGPOLL: |
+ to->cpt_si_band = from->si_band; |
+ to->cpt_si_fd = from->si_fd; |
+ break; |
+ default: |
+ to->cpt_si_pid = from->si_pid; |
+ to->cpt_si_uid = from->si_uid; |
+ to->cpt_si_ptr = (intptr_t) from->si_ptr; |
+ break; |
+ } |
+ } |
+} |
+ |
+static void |
+siginfo_from_compat_x32_siginfo (siginfo_t *to, |
+ compat_x32_siginfo_t *from) |
+{ |
+ memset (to, 0, sizeof (*to)); |
+ |
+ to->si_signo = from->si_signo; |
+ to->si_errno = from->si_errno; |
+ to->si_code = from->si_code; |
+ |
+ if (to->si_code == SI_TIMER) |
+ { |
+ to->si_timerid = from->cpt_si_timerid; |
+ to->si_overrun = from->cpt_si_overrun; |
+ to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr; |
+ } |
+ else if (to->si_code == SI_USER) |
+ { |
+ to->si_pid = from->cpt_si_pid; |
+ to->si_uid = from->cpt_si_uid; |
+ } |
+ else if (to->si_code < 0) |
+ { |
+ to->si_pid = from->cpt_si_pid; |
+ to->si_uid = from->cpt_si_uid; |
+ to->si_ptr = (void *) (intptr_t) from->cpt_si_ptr; |
+ } |
+ else |
+ { |
+ switch (to->si_signo) |
+ { |
+ case SIGCHLD: |
+ to->si_pid = from->cpt_si_pid; |
+ to->si_uid = from->cpt_si_uid; |
+ to->si_status = from->cpt_si_status; |
+ to->si_utime = from->cpt_si_utime; |
+ to->si_stime = from->cpt_si_stime; |
+ break; |
+ case SIGILL: |
+ case SIGFPE: |
+ case SIGSEGV: |
+ case SIGBUS: |
+ to->si_addr = (void *) (intptr_t) from->cpt_si_addr; |
+ break; |
+ case SIGPOLL: |
+ to->si_band = from->cpt_si_band; |
+ to->si_fd = from->cpt_si_fd; |
+ break; |
+ default: |
+ to->si_pid = from->cpt_si_pid; |
+ to->si_uid = from->cpt_si_uid; |
+ to->si_ptr = (void* ) (intptr_t) from->cpt_si_ptr; |
+ break; |
+ } |
+ } |
+} |
+ |
+/* Is this process 64-bit? */ |
+static int linux_is_elf64; |
#endif /* __x86_64__ */ |
/* Convert a native/host siginfo object, into/from the siginfo in the |
@@ -906,13 +1099,13 @@ siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from) |
INF. */ |
static int |
-x86_siginfo_fixup (struct siginfo *native, void *inf, int direction) |
+x86_siginfo_fixup (siginfo_t *native, void *inf, int direction) |
{ |
#ifdef __x86_64__ |
/* Is the inferior 32-bit? If so, then fixup the siginfo object. */ |
if (register_size (0) == 4) |
{ |
- if (sizeof (struct siginfo) != sizeof (compat_siginfo_t)) |
+ if (sizeof (siginfo_t) != sizeof (compat_siginfo_t)) |
fatal ("unexpected difference in siginfo"); |
if (direction == 0) |
@@ -922,6 +1115,21 @@ x86_siginfo_fixup (struct siginfo *native, void *inf, int direction) |
return 1; |
} |
+ /* No fixup for native x32 GDB. */ |
+ else if (!linux_is_elf64 && sizeof (void *) == 8) |
+ { |
+ if (sizeof (siginfo_t) != sizeof (compat_x32_siginfo_t)) |
+ fatal ("unexpected difference in siginfo"); |
+ |
+ if (direction == 0) |
+ compat_x32_siginfo_from_siginfo ((struct compat_x32_siginfo *) inf, |
+ native); |
+ else |
+ siginfo_from_compat_x32_siginfo (native, |
+ (struct compat_x32_siginfo *) inf); |
+ |
+ return 1; |
+ } |
#endif |
return 0; |
@@ -954,8 +1162,10 @@ x86_linux_update_xmltarget (void) |
#ifdef __x86_64__ |
if (num_xmm_registers == 8) |
init_registers_i386_linux (); |
- else |
+ else if (linux_is_elf64) |
init_registers_amd64_linux (); |
+ else |
+ init_registers_x32_linux (); |
#else |
{ |
# ifdef HAVE_PTRACE_GETFPXREGS |
@@ -1050,8 +1260,10 @@ x86_linux_update_xmltarget (void) |
/* I386 has 8 xmm regs. */ |
if (num_xmm_registers == 8) |
init_registers_i386_avx_linux (); |
- else |
+ else if (linux_is_elf64) |
init_registers_amd64_avx_linux (); |
+ else |
+ init_registers_x32_avx_linux (); |
#else |
init_registers_i386_avx_linux (); |
#endif |
@@ -1094,20 +1306,28 @@ x86_linux_process_qsupported (const char *query) |
static void |
x86_arch_setup (void) |
{ |
-#ifdef __x86_64__ |
int pid = pid_of (get_thread_lwp (current_inferior)); |
- char *file = linux_child_pid_to_exec_file (pid); |
- int use_64bit = elf_64_file_p (file); |
+ unsigned int machine; |
+ int is_elf64 = linux_pid_exe_is_elf_64_file (pid, &machine); |
- free (file); |
+ if (sizeof (void *) == 4) |
+ { |
+ if (is_elf64 > 0) |
+ error (_("Can't debug 64-bit process with 32-bit GDBserver")); |
+#ifndef __x86_64__ |
+ else if (machine == EM_X86_64) |
+ error (_("Can't debug x86-64 process with 32-bit GDBserver")); |
+#endif |
+ } |
- if (use_64bit < 0) |
+#ifdef __x86_64__ |
+ if (is_elf64 < 0) |
{ |
/* This can only happen if /proc/<pid>/exe is unreadable, |
but "that can't happen" if we've gotten this far. |
Fall through and assume this is a 32-bit program. */ |
} |
- else if (use_64bit) |
+ else if (machine == EM_X86_64) |
{ |
/* Amd64 doesn't have HAVE_LINUX_USRREGS. */ |
the_low_target.num_regs = -1; |
@@ -1118,9 +1338,12 @@ x86_arch_setup (void) |
/* Amd64 has 16 xmm regs. */ |
num_xmm_registers = 16; |
+ linux_is_elf64 = is_elf64; |
x86_linux_update_xmltarget (); |
return; |
} |
+ |
+ linux_is_elf64 = 0; |
#endif |
/* Ok we have a 32-bit inferior. */ |
@@ -1192,6 +1415,8 @@ amd64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, |
{ |
unsigned char buf[40]; |
int i, offset; |
+ int64_t loffset; |
+ |
CORE_ADDR buildaddr = *jump_entry; |
/* Build the jump pad. */ |
@@ -1315,7 +1540,17 @@ amd64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, |
*adjusted_insn_addr_end = buildaddr; |
/* Finally, write a jump back to the program. */ |
- offset = (tpaddr + orig_size) - (buildaddr + sizeof (jump_insn)); |
+ |
+ loffset = (tpaddr + orig_size) - (buildaddr + sizeof (jump_insn)); |
+ if (loffset > INT_MAX || loffset < INT_MIN) |
+ { |
+ sprintf (err, |
+ "E.Jump back from jump pad too far from tracepoint " |
+ "(offset 0x%" PRIx64 " > int32).", loffset); |
+ return 1; |
+ } |
+ |
+ offset = (int) loffset; |
memcpy (buf, jump_insn, sizeof (jump_insn)); |
memcpy (buf + 1, &offset, 4); |
append_insns (&buildaddr, sizeof (jump_insn), buf); |
@@ -1324,7 +1559,17 @@ amd64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, |
is always done last (by our caller actually), so that we can |
install fast tracepoints with threads running. This relies on |
the agent's atomic write support. */ |
- offset = *jump_entry - (tpaddr + sizeof (jump_insn)); |
+ loffset = *jump_entry - (tpaddr + sizeof (jump_insn)); |
+ if (loffset > INT_MAX || loffset < INT_MIN) |
+ { |
+ sprintf (err, |
+ "E.Jump pad too far from tracepoint " |
+ "(offset 0x%" PRIx64 " > int32).", loffset); |
+ return 1; |
+ } |
+ |
+ offset = (int) loffset; |
+ |
memcpy (buf, jump_insn, sizeof (jump_insn)); |
memcpy (buf + 1, &offset, 4); |
memcpy (jjump_pad_insn, buf, sizeof (jump_insn)); |
@@ -1579,7 +1824,7 @@ x86_get_min_fast_tracepoint_insn_len (void) |
return 5; |
#endif |
- if (in_process_agent_loaded ()) |
+ if (agent_loaded_p ()) |
{ |
char errbuf[IPA_BUFSIZ]; |
@@ -2937,6 +3182,8 @@ struct linux_target_ops the_low_target = |
NULL, |
NULL, |
NULL, |
+ NULL, |
+ NULL, /* fetch_register */ |
x86_get_pc, |
x86_set_pc, |
x86_breakpoint, |