X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Flinux-x86-low.c;h=4ea284eebafd16fea56c1ea5165dbe203ece2e30;hb=b09846a918b74f0371149f730f8b391548b11a8d;hp=cf00f7c9830bb5174281dd1dd93b36b0517ab33d;hpb=6a271cae85843023ce9c046c040b643158d85054;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index cf00f7c983..4ea284eeba 100644 --- a/gdb/gdbserver/linux-x86-low.c +++ b/gdb/gdbserver/linux-x86-low.c @@ -1,7 +1,6 @@ /* GNU/Linux/x86-64 specific low level interface, for the remote server for GDB. - Copyright (C) 2002, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - Free Software Foundation, Inc. + Copyright (C) 2002, 2004-2012 Free Software Foundation, Inc. This file is part of GDB. @@ -21,6 +20,7 @@ #include #include #include +#include #include "server.h" #include "linux-low.h" #include "i387-fp.h" @@ -29,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); @@ -40,8 +41,13 @@ 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 }; /* Backward compatibility for gdb without XML support. */ @@ -232,7 +238,8 @@ x86_get_thread_area (int lwpid, CORE_ADDR *addr) idx = gs >> reg_thread_area; if (ptrace (PTRACE_GET_THREAD_AREA, - lwpid_of (lwp), (void *) (long) idx, (unsigned long) &desc) < 0) + lwpid_of (lwp), + (void *) (long) idx, (unsigned long) &desc) < 0) return -1; *addr = desc[1]; @@ -461,30 +468,55 @@ x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) error ("Couldn't write debug register"); } +static int +update_debug_registers_callback (struct inferior_list_entry *entry, + void *pid_p) +{ + struct lwp_info *lwp = (struct lwp_info *) entry; + int pid = *(int *) pid_p; + + /* Only update the threads of this process. */ + if (pid_of (lwp) == pid) + { + /* The actual update is done later just before resuming the lwp, + we just mark that the registers need updating. */ + lwp->arch_private->debug_registers_changed = 1; + + /* If the lwp isn't stopped, force it to momentarily pause, so + we can update its debug registers. */ + if (!lwp->stopped) + linux_stop_lwp (lwp); + } + + return 0; +} + /* Update the inferior's debug register REGNUM from STATE. */ void i386_dr_low_set_addr (const struct i386_debug_reg_state *state, int regnum) { - struct inferior_list_entry *lp; - CORE_ADDR addr; - /* Only need to update the threads of this process. */ + /* Only update the threads of this process. */ int pid = pid_of (get_thread_lwp (current_inferior)); if (! (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR)) fatal ("Invalid debug register %d", regnum); - addr = state->dr_mirror[regnum]; + find_inferior (&all_lwps, update_debug_registers_callback, &pid); +} - for (lp = all_lwps.head; lp; lp = lp->next) - { - struct lwp_info *lwp = (struct lwp_info *) lp; +/* Return the inferior's debug register REGNUM. */ - /* The actual update is done later, we just mark that the register - needs updating. */ - if (pid_of (lwp) == pid) - lwp->arch_private->debug_registers_changed = 1; - } +CORE_ADDR +i386_dr_low_get_addr (int regnum) +{ + struct lwp_info *lwp = get_thread_lwp (current_inferior); + ptid_t ptid = ptid_of (lwp); + + /* DR6 and DR7 are retrieved with some other way. */ + gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR); + + return x86_linux_dr_get (ptid, regnum); } /* Update the inferior's DR7 debug control register from STATE. */ @@ -492,34 +524,36 @@ i386_dr_low_set_addr (const struct i386_debug_reg_state *state, int regnum) void i386_dr_low_set_control (const struct i386_debug_reg_state *state) { - struct inferior_list_entry *lp; - /* Only need to update the threads of this process. */ + /* Only update the threads of this process. */ int pid = pid_of (get_thread_lwp (current_inferior)); - for (lp = all_lwps.head; lp; lp = lp->next) - { - struct lwp_info *lwp = (struct lwp_info *) lp; + find_inferior (&all_lwps, update_debug_registers_callback, &pid); +} - /* The actual update is done later, we just mark that the register - needs updating. */ - if (pid_of (lwp) == pid) - lwp->arch_private->debug_registers_changed = 1; - } +/* Return the inferior's DR7 debug control register. */ + +unsigned +i386_dr_low_get_control (void) +{ + struct lwp_info *lwp = get_thread_lwp (current_inferior); + ptid_t ptid = ptid_of (lwp); + + return x86_linux_dr_get (ptid, DR_CONTROL); } /* Get the value of the DR6 debug status register from the inferior and record it in STATE. */ -void -i386_dr_low_get_status (struct i386_debug_reg_state *state) +unsigned +i386_dr_low_get_status (void) { struct lwp_info *lwp = get_thread_lwp (current_inferior); ptid_t ptid = ptid_of (lwp); - state->dr_status_mirror = x86_linux_dr_get (ptid, DR_STATUS); + return x86_linux_dr_get (ptid, DR_STATUS); } -/* Watchpoint support. */ +/* Breakpoint/Watchpoint support. */ static int x86_insert_point (char type, CORE_ADDR addr, int len) @@ -528,7 +562,16 @@ x86_insert_point (char type, CORE_ADDR addr, int len) switch (type) { case '0': - return set_gdb_breakpoint_at (addr); + { + int ret; + + ret = prepare_to_access_memory (); + if (ret) + return -1; + ret = set_gdb_breakpoint_at (addr); + done_accessing_memory (); + return ret; + } case '2': case '3': case '4': @@ -547,7 +590,16 @@ x86_remove_point (char type, CORE_ADDR addr, int len) switch (type) { case '0': - return delete_gdb_breakpoint_at (addr); + { + int ret; + + ret = prepare_to_access_memory (); + if (ret) + return -1; + ret = delete_gdb_breakpoint_at (addr); + done_accessing_memory (); + return ret; + } case '2': case '3': case '4': @@ -608,23 +660,34 @@ 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) { int i; int pid = ptid_get_pid (ptid); struct process_info *proc = find_process_pid (pid); - struct i386_debug_reg_state *state = &proc->private->arch_private->debug_reg_state; + struct i386_debug_reg_state *state + = &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); } @@ -717,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 @@ -747,8 +871,10 @@ compat_siginfo_from_siginfo (compat_siginfo_t *to, siginfo_t *from) to->si_errno = from->si_errno; to->si_code = from->si_code; - if (to->si_code < 0) + 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) @@ -756,10 +882,10 @@ compat_siginfo_from_siginfo (compat_siginfo_t *to, siginfo_t *from) to->cpt_si_pid = from->si_pid; to->cpt_si_uid = from->si_uid; } - else if (to->si_code == SI_TIMER) + else if (to->si_code < 0) { - to->cpt_si_timerid = from->si_timerid; - to->cpt_si_overrun = from->si_overrun; + to->cpt_si_pid = from->si_pid; + to->cpt_si_uid = from->si_uid; to->cpt_si_ptr = (intptr_t) from->si_ptr; } else @@ -801,8 +927,10 @@ siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from) to->si_errno = from->si_errno; to->si_code = from->si_code; - if (to->si_code < 0) + 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) @@ -810,12 +938,126 @@ siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from) to->si_pid = from->cpt_si_pid; to->si_uid = from->cpt_si_uid; } - else if (to->si_code == SI_TIMER) + 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; + } + } +} + +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) @@ -846,6 +1088,8 @@ siginfo_from_compat_siginfo (siginfo_t *to, compat_siginfo_t *from) } } +/* 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 @@ -855,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) @@ -869,6 +1113,21 @@ x86_siginfo_fixup (struct siginfo *native, void *inf, int direction) else siginfo_from_compat_siginfo (native, (struct compat_siginfo *) inf); + 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 @@ -903,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 @@ -999,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 @@ -1043,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//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; @@ -1067,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. */ @@ -1131,13 +1405,18 @@ amd64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, CORE_ADDR lockaddr, ULONGEST orig_size, CORE_ADDR *jump_entry, + CORE_ADDR *trampoline, + ULONGEST *trampoline_size, unsigned char *jjump_pad_insn, ULONGEST *jjump_pad_insn_size, CORE_ADDR *adjusted_insn_addr, - CORE_ADDR *adjusted_insn_addr_end) + CORE_ADDR *adjusted_insn_addr_end, + char *err) { unsigned char buf[40]; int i, offset; + int64_t loffset; + CORE_ADDR buildaddr = *jump_entry; /* Build the jump pad. */ @@ -1261,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); @@ -1270,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)); @@ -1295,10 +1594,13 @@ i386_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, CORE_ADDR lockaddr, ULONGEST orig_size, CORE_ADDR *jump_entry, + CORE_ADDR *trampoline, + ULONGEST *trampoline_size, unsigned char *jjump_pad_insn, ULONGEST *jjump_pad_insn_size, CORE_ADDR *adjusted_insn_addr, - CORE_ADDR *adjusted_insn_addr_end) + CORE_ADDR *adjusted_insn_addr_end, + char *err) { unsigned char buf[0x100]; int i, offset; @@ -1404,7 +1706,7 @@ i386_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, buf[i++] = 0x0f; /* pop %fs */ buf[i++] = 0xa1; buf[i++] = 0x07; /* pop %es */ - buf[i++] = 0x1f; /* pop %de */ + buf[i++] = 0x1f; /* pop %ds */ buf[i++] = 0x9d; /* popf */ buf[i++] = 0x83; /* add $0x4,%esp (pop of tpaddr aka $pc) */ buf[i++] = 0xc4; @@ -1428,11 +1730,40 @@ i386_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)); - memcpy (buf, jump_insn, sizeof (jump_insn)); - memcpy (buf + 1, &offset, 4); - memcpy (jjump_pad_insn, buf, sizeof (jump_insn)); - *jjump_pad_insn_size = sizeof (jump_insn); + if (orig_size == 4) + { + /* Create a trampoline. */ + *trampoline_size = sizeof (jump_insn); + if (!claim_trampoline_space (*trampoline_size, trampoline)) + { + /* No trampoline space available. */ + strcpy (err, + "E.Cannot allocate trampoline space needed for fast " + "tracepoints on 4-byte instructions."); + return 1; + } + + offset = *jump_entry - (*trampoline + sizeof (jump_insn)); + memcpy (buf, jump_insn, sizeof (jump_insn)); + memcpy (buf + 1, &offset, 4); + write_inferior_memory (*trampoline, buf, sizeof (jump_insn)); + + /* Use a 16-bit relative jump instruction to jump to the trampoline. */ + offset = (*trampoline - (tpaddr + sizeof (small_jump_insn))) & 0xffff; + memcpy (buf, small_jump_insn, sizeof (small_jump_insn)); + memcpy (buf + 2, &offset, 2); + memcpy (jjump_pad_insn, buf, sizeof (small_jump_insn)); + *jjump_pad_insn_size = sizeof (small_jump_insn); + } + else + { + /* Else use a 32-bit relative jump instruction. */ + offset = *jump_entry - (tpaddr + sizeof (jump_insn)); + memcpy (buf, jump_insn, sizeof (jump_insn)); + memcpy (buf + 1, &offset, 4); + memcpy (jjump_pad_insn, buf, sizeof (jump_insn)); + *jjump_pad_insn_size = sizeof (jump_insn); + } /* Return the end address of our pad. */ *jump_entry = buildaddr; @@ -1446,29 +1777,83 @@ x86_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr, CORE_ADDR lockaddr, ULONGEST orig_size, CORE_ADDR *jump_entry, + CORE_ADDR *trampoline, + ULONGEST *trampoline_size, unsigned char *jjump_pad_insn, ULONGEST *jjump_pad_insn_size, CORE_ADDR *adjusted_insn_addr, - CORE_ADDR *adjusted_insn_addr_end) + CORE_ADDR *adjusted_insn_addr_end, + char *err) { #ifdef __x86_64__ if (register_size (0) == 8) return amd64_install_fast_tracepoint_jump_pad (tpoint, tpaddr, collector, lockaddr, orig_size, jump_entry, + trampoline, trampoline_size, jjump_pad_insn, jjump_pad_insn_size, adjusted_insn_addr, - adjusted_insn_addr_end); + adjusted_insn_addr_end, + err); #endif return i386_install_fast_tracepoint_jump_pad (tpoint, tpaddr, collector, lockaddr, orig_size, jump_entry, + trampoline, trampoline_size, jjump_pad_insn, jjump_pad_insn_size, adjusted_insn_addr, - adjusted_insn_addr_end); + adjusted_insn_addr_end, + err); +} + +/* Return the minimum instruction length for fast tracepoints on x86/x86-64 + architectures. */ + +static int +x86_get_min_fast_tracepoint_insn_len (void) +{ + static int warned_about_fast_tracepoints = 0; + +#ifdef __x86_64__ + /* On x86-64, 5-byte jump instructions with a 4-byte offset are always + used for fast tracepoints. */ + if (register_size (0) == 8) + return 5; +#endif + + if (agent_loaded_p ()) + { + char errbuf[IPA_BUFSIZ]; + + errbuf[0] = '\0'; + + /* On x86, if trampolines are available, then 4-byte jump instructions + with a 2-byte offset may be used, otherwise 5-byte jump instructions + with a 4-byte offset are used instead. */ + if (have_fast_tracepoint_trampoline_buffer (errbuf)) + return 4; + else + { + /* GDB has no channel to explain to user why a shorter fast + tracepoint is not possible, but at least make GDBserver + mention that something has gone awry. */ + if (!warned_about_fast_tracepoints) + { + warning ("4-byte fast tracepoints not available; %s\n", errbuf); + warned_about_fast_tracepoints = 1; + } + return 5; + } + } + else + { + /* Indicate that the minimum length is currently unknown since the IPA + has not loaded yet. */ + return 0; + } } static void @@ -1484,41 +1869,37 @@ add_insns (unsigned char *start, int len) current_insn_ptr = buildaddr; } -/* A function used to trick optimizers. */ - -int -always_true (void) -{ - return 1; -} - /* Our general strategy for emitting code is to avoid specifying raw bytes whenever possible, and instead copy a block of inline asm that is embedded in the function. This is a little messy, because we need to keep the compiler from discarding what looks like dead code, plus suppress various warnings. */ -#define EMIT_ASM(NAME,INSNS) \ - { extern unsigned char start_ ## NAME, end_ ## NAME; \ - add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \ - if (always_true ()) \ - goto skipover ## NAME; \ - __asm__ ("start_" #NAME ":\n\t" INSNS "\n\tend_" #NAME ":\n\t"); \ - skipover ## NAME: \ - ; } - +#define EMIT_ASM(NAME, INSNS) \ + do \ + { \ + extern unsigned char start_ ## NAME, end_ ## NAME; \ + add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \ + __asm__ ("jmp end_" #NAME "\n" \ + "\t" "start_" #NAME ":" \ + "\t" INSNS "\n" \ + "\t" "end_" #NAME ":"); \ + } while (0) #ifdef __x86_64__ #define EMIT_ASM32(NAME,INSNS) \ - { extern unsigned char start_ ## NAME, end_ ## NAME; \ - add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \ - if (always_true ()) \ - goto skipover ## NAME; \ - __asm__ (".code32\n\tstart_" #NAME ":\n\t" INSNS "\n\tend_" #NAME ":\n" \ - "\t.code64\n\t"); \ - skipover ## NAME: \ - ; } + do \ + { \ + extern unsigned char start_ ## NAME, end_ ## NAME; \ + add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \ + __asm__ (".code32\n" \ + "\t" "jmp end_" #NAME "\n" \ + "\t" "start_" #NAME ":\n" \ + "\t" INSNS "\n" \ + "\t" "end_" #NAME ":\n" \ + ".code64\n"); \ + } while (0) #else @@ -1764,7 +2145,7 @@ amd64_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size) } static void -amd64_emit_const (int64_t num) +amd64_emit_const (LONGEST num) { unsigned char buf[16]; int i; @@ -1772,7 +2153,7 @@ amd64_emit_const (int64_t num) i = 0; buf[i++] = 0x48; buf[i++] = 0xb8; /* mov $,%rax */ - *((int64_t *) (&buf[i])) = num; + memcpy (&buf[i], &num, sizeof (num)); i += 8; append_insns (&buildaddr, i, buf); current_insn_ptr = buildaddr; @@ -1784,7 +2165,7 @@ amd64_emit_call (CORE_ADDR fn) unsigned char buf[16]; int i; CORE_ADDR buildaddr; - int64_t offset64; + LONGEST offset64; /* The destination function being in the shared library, may be >31-bits away off the compiled code pad. */ @@ -1829,7 +2210,7 @@ amd64_emit_reg (int reg) buildaddr = current_insn_ptr; i = 0; buf[i++] = 0xbe; /* mov $,%esi */ - *((int *) (&buf[i])) = reg; + memcpy (&buf[i], ®, sizeof (reg)); i += 4; append_insns (&buildaddr, i, buf); current_insn_ptr = buildaddr; @@ -1912,14 +2293,14 @@ amd64_emit_int_call_1 (CORE_ADDR fn, int arg1) buildaddr = current_insn_ptr; i = 0; buf[i++] = 0xbf; /* movl $,%edi */ - *((int *) (&buf[i])) = arg1; + memcpy (&buf[i], &arg1, sizeof (arg1)); i += 4; append_insns (&buildaddr, i, buf); current_insn_ptr = buildaddr; amd64_emit_call (fn); } -/* FN's prototype is `void(*fn)(int,int64_t)'. */ +/* FN's prototype is `void(*fn)(int,LONGEST)'. */ static void amd64_emit_void_call_2 (CORE_ADDR fn, int arg1) @@ -1931,7 +2312,7 @@ amd64_emit_void_call_2 (CORE_ADDR fn, int arg1) buildaddr = current_insn_ptr; i = 0; buf[i++] = 0xbf; /* movl $,%edi */ - *((int *) (&buf[i])) = arg1; + memcpy (&buf[i], &arg1, sizeof (arg1)); i += 4; append_insns (&buildaddr, i, buf); current_insn_ptr = buildaddr; @@ -1946,6 +2327,127 @@ amd64_emit_void_call_2 (CORE_ADDR fn, int arg1) "pop %rax"); } +void +amd64_emit_eq_goto (int *offset_p, int *size_p) +{ + EMIT_ASM (amd64_eq, + "cmp %rax,(%rsp)\n\t" + "jne .Lamd64_eq_fallthru\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lamd64_eq_fallthru:\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax"); + + if (offset_p) + *offset_p = 13; + if (size_p) + *size_p = 4; +} + +void +amd64_emit_ne_goto (int *offset_p, int *size_p) +{ + EMIT_ASM (amd64_ne, + "cmp %rax,(%rsp)\n\t" + "je .Lamd64_ne_fallthru\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lamd64_ne_fallthru:\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax"); + + if (offset_p) + *offset_p = 13; + if (size_p) + *size_p = 4; +} + +void +amd64_emit_lt_goto (int *offset_p, int *size_p) +{ + EMIT_ASM (amd64_lt, + "cmp %rax,(%rsp)\n\t" + "jnl .Lamd64_lt_fallthru\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lamd64_lt_fallthru:\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax"); + + if (offset_p) + *offset_p = 13; + if (size_p) + *size_p = 4; +} + +void +amd64_emit_le_goto (int *offset_p, int *size_p) +{ + EMIT_ASM (amd64_le, + "cmp %rax,(%rsp)\n\t" + "jnle .Lamd64_le_fallthru\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lamd64_le_fallthru:\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax"); + + if (offset_p) + *offset_p = 13; + if (size_p) + *size_p = 4; +} + +void +amd64_emit_gt_goto (int *offset_p, int *size_p) +{ + EMIT_ASM (amd64_gt, + "cmp %rax,(%rsp)\n\t" + "jng .Lamd64_gt_fallthru\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lamd64_gt_fallthru:\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax"); + + if (offset_p) + *offset_p = 13; + if (size_p) + *size_p = 4; +} + +void +amd64_emit_ge_goto (int *offset_p, int *size_p) +{ + EMIT_ASM (amd64_ge, + "cmp %rax,(%rsp)\n\t" + "jnge .Lamd64_ge_fallthru\n\t" + ".Lamd64_ge_jump:\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lamd64_ge_fallthru:\n\t" + "lea 0x8(%rsp),%rsp\n\t" + "pop %rax"); + + if (offset_p) + *offset_p = 13; + if (size_p) + *size_p = 4; +} + struct emit_ops amd64_emit_ops = { amd64_emit_prologue, @@ -1978,7 +2480,13 @@ struct emit_ops amd64_emit_ops = amd64_emit_swap, amd64_emit_stack_adjust, amd64_emit_int_call_1, - amd64_emit_void_call_2 + amd64_emit_void_call_2, + amd64_emit_eq_goto, + amd64_emit_ne_goto, + amd64_emit_lt_goto, + amd64_emit_le_goto, + amd64_emit_gt_goto, + amd64_emit_ge_goto }; #endif /* __x86_64__ */ @@ -1988,7 +2496,8 @@ i386_emit_prologue (void) { EMIT_ASM32 (i386_prologue, "push %ebp\n\t" - "mov %esp,%ebp"); + "mov %esp,%ebp\n\t" + "push %ebx"); /* At this point, the raw regs base address is at 8(%ebp), and the value pointer is at 12(%ebp). */ } @@ -2001,6 +2510,7 @@ i386_emit_epilogue (void) "mov %eax,(%ecx)\n\t" "mov %ebx,0x4(%ecx)\n\t" "xor %eax,%eax\n\t" + "pop %ebx\n\t" "pop %ebp\n\t" "ret"); } @@ -2251,21 +2761,22 @@ i386_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size) } static void -i386_emit_const (int64_t num) +i386_emit_const (LONGEST num) { unsigned char buf[16]; - int i, hi; + int i, hi, lo; CORE_ADDR buildaddr = current_insn_ptr; i = 0; buf[i++] = 0xb8; /* mov $,%eax */ - *((int *) (&buf[i])) = (num & 0xffffffff); + lo = num & 0xffffffff; + memcpy (&buf[i], &lo, sizeof (lo)); i += 4; hi = ((num >> 32) & 0xffffffff); if (hi) { buf[i++] = 0xbb; /* mov $,%ebx */ - *((int *) (&buf[i])) = hi; + memcpy (&buf[i], &hi, sizeof (hi)); i += 4; } else @@ -2304,7 +2815,7 @@ i386_emit_reg (int reg) buildaddr = current_insn_ptr; i = 0; buf[i++] = 0xb8; /* mov $,%eax */ - *((int *) (&buf[i])) = reg; + memcpy (&buf[i], ®, sizeof (reg)); i += 4; append_insns (&buildaddr, i, buf); current_insn_ptr = buildaddr; @@ -2404,7 +2915,7 @@ i386_emit_int_call_1 (CORE_ADDR fn, int arg1) buf[i++] = 0xc7; /* movl $,(%esp) */ buf[i++] = 0x04; buf[i++] = 0x24; - *((int *) (&buf[i])) = arg1; + memcpy (&buf[i], &arg1, sizeof (arg1)); i += 4; append_insns (&buildaddr, i, buf); current_insn_ptr = buildaddr; @@ -2414,7 +2925,7 @@ i386_emit_int_call_1 (CORE_ADDR fn, int arg1) "lea 0x8(%esp),%esp"); } -/* FN's prototype is `void(*fn)(int,int64_t)'. */ +/* FN's prototype is `void(*fn)(int,LONGEST)'. */ static void i386_emit_void_call_2 (CORE_ADDR fn, int arg1) @@ -2439,7 +2950,7 @@ i386_emit_void_call_2 (CORE_ADDR fn, int arg1) buf[i++] = 0xc7; /* movl $,(%esp) */ buf[i++] = 0x04; buf[i++] = 0x24; - *((int *) (&buf[i])) = arg1; + memcpy (&buf[i], &arg1, sizeof (arg1)); i += 4; append_insns (&buildaddr, i, buf); current_insn_ptr = buildaddr; @@ -2450,6 +2961,162 @@ i386_emit_void_call_2 (CORE_ADDR fn, int arg1) "pop %eax"); } + +void +i386_emit_eq_goto (int *offset_p, int *size_p) +{ + EMIT_ASM32 (eq, + /* Check low half first, more likely to be decider */ + "cmpl %eax,(%esp)\n\t" + "jne .Leq_fallthru\n\t" + "cmpl %ebx,4(%esp)\n\t" + "jne .Leq_fallthru\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Leq_fallthru:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx"); + + if (offset_p) + *offset_p = 18; + if (size_p) + *size_p = 4; +} + +void +i386_emit_ne_goto (int *offset_p, int *size_p) +{ + EMIT_ASM32 (ne, + /* Check low half first, more likely to be decider */ + "cmpl %eax,(%esp)\n\t" + "jne .Lne_jump\n\t" + "cmpl %ebx,4(%esp)\n\t" + "je .Lne_fallthru\n\t" + ".Lne_jump:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lne_fallthru:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx"); + + if (offset_p) + *offset_p = 18; + if (size_p) + *size_p = 4; +} + +void +i386_emit_lt_goto (int *offset_p, int *size_p) +{ + EMIT_ASM32 (lt, + "cmpl %ebx,4(%esp)\n\t" + "jl .Llt_jump\n\t" + "jne .Llt_fallthru\n\t" + "cmpl %eax,(%esp)\n\t" + "jnl .Llt_fallthru\n\t" + ".Llt_jump:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Llt_fallthru:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx"); + + if (offset_p) + *offset_p = 20; + if (size_p) + *size_p = 4; +} + +void +i386_emit_le_goto (int *offset_p, int *size_p) +{ + EMIT_ASM32 (le, + "cmpl %ebx,4(%esp)\n\t" + "jle .Lle_jump\n\t" + "jne .Lle_fallthru\n\t" + "cmpl %eax,(%esp)\n\t" + "jnle .Lle_fallthru\n\t" + ".Lle_jump:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lle_fallthru:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx"); + + if (offset_p) + *offset_p = 20; + if (size_p) + *size_p = 4; +} + +void +i386_emit_gt_goto (int *offset_p, int *size_p) +{ + EMIT_ASM32 (gt, + "cmpl %ebx,4(%esp)\n\t" + "jg .Lgt_jump\n\t" + "jne .Lgt_fallthru\n\t" + "cmpl %eax,(%esp)\n\t" + "jng .Lgt_fallthru\n\t" + ".Lgt_jump:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lgt_fallthru:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx"); + + if (offset_p) + *offset_p = 20; + if (size_p) + *size_p = 4; +} + +void +i386_emit_ge_goto (int *offset_p, int *size_p) +{ + EMIT_ASM32 (ge, + "cmpl %ebx,4(%esp)\n\t" + "jge .Lge_jump\n\t" + "jne .Lge_fallthru\n\t" + "cmpl %eax,(%esp)\n\t" + "jnge .Lge_fallthru\n\t" + ".Lge_jump:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx\n\t" + /* jmp, but don't trust the assembler to choose the right jump */ + ".byte 0xe9, 0x0, 0x0, 0x0, 0x0\n\t" + ".Lge_fallthru:\n\t" + "lea 0x8(%esp),%esp\n\t" + "pop %eax\n\t" + "pop %ebx"); + + if (offset_p) + *offset_p = 20; + if (size_p) + *size_p = 4; +} + struct emit_ops i386_emit_ops = { i386_emit_prologue, @@ -2482,7 +3149,13 @@ struct emit_ops i386_emit_ops = i386_emit_swap, i386_emit_stack_adjust, i386_emit_int_call_1, - i386_emit_void_call_2 + i386_emit_void_call_2, + i386_emit_eq_goto, + i386_emit_ne_goto, + i386_emit_lt_goto, + i386_emit_le_goto, + i386_emit_gt_goto, + i386_emit_ge_goto }; @@ -2509,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, @@ -2534,5 +3209,6 @@ struct linux_target_ops the_low_target = x86_supports_tracepoints, x86_get_thread_area, x86_install_fast_tracepoint_jump_pad, - x86_emit_ops + x86_emit_ops, + x86_get_min_fast_tracepoint_insn_len, };