| 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,
|
|
|