m_gdbserver/signals.c \
m_gdbserver/target.c \
m_gdbserver/utils.c \
- m_gdbserver/valgrind-low.c \
m_gdbserver/valgrind-low-x86.c \
m_gdbserver/valgrind-low-amd64.c \
m_gdbserver/valgrind-low-arm.c \
static void call_gdbserver ( ThreadId tid , CallReason reason);
-/* convert from CORE_ADDR to void* */
-static
-void* C2v(CORE_ADDR addr)
-{
- return (void*) addr;
-}
-
/* Describes the address addr (for debugging/printing purposes).
Last two results are kept. A third call will replace the
oldest result. */
/* FIXME - fetch registers for INF */
if (fetch && regcache->registers_valid == 0) {
- fetch_inferior_registers (0);
+ valgrind_fetch_registers (0);
regcache->registers_valid = 1;
}
struct thread_info *saved_inferior = current_inferior;
current_inferior = thread;
- store_inferior_registers (-1);
+ valgrind_store_registers (-1);
current_inferior = saved_inferior;
}
return i;
}
+/* builds an image of bin according to byte order of the architecture
+ Useful for register and int image */
+char* heximage (char *buf, char *bin, int count)
+{
+#if defined(VGA_x86) || defined(VGA_amd64)
+ char rev[count];
+ /* note: no need for trailing \0, length is known with count */
+ int i;
+ for (i = 0; i < count; i++)
+ rev[i] = bin[count - i - 1];
+ hexify (buf, rev, count);
+#else
+ hexify (buf, bin, count);
+#endif
+ return buf;
+}
+
+void* C2v(CORE_ADDR addr)
+{
+ return (void*) addr;
+}
+
+
/* Convert BUFFER, binary data at least LEN bytes long, into escaped
binary data in OUT_BUF. Set *OUT_LEN to the length of the data
encoded in OUT_BUF, and return the number of bytes in OUT_BUF
/* Check for an input interrupt while we're here. */
if (cc == '\003')
- (*the_target->send_signal) (VKI_SIGINT);
+ dlog(1, "Received 0x03 character (SIGINT)\n");
}
while (cc != '+');
if (status == 'T') {
const char **regp = gdbserver_expedite_regs;
- if (the_target->stopped_by_watchpoint != NULL
- && (*the_target->stopped_by_watchpoint) ()) {
+ if (valgrind_stopped_by_watchpoint()) {
CORE_ADDR addr;
int i;
strncpy (buf, "watch:", 6);
buf += 6;
- addr = (*the_target->stopped_data_address) ();
+ addr = valgrind_stopped_data_address ();
/* Convert each byte of the address into two hexadecimal chars.
Note that we take sizeof (void *) instead of sizeof (addr);
/* for a gdbserver integrated in valgrind, resuming the process consists
in returning the control to valgrind.
+ The guess process resumes its execution.
Then at the next error or break or ..., valgrind calls gdbserver again.
- A resume packet must then be built.
- resume_packet_needed records the fact that the next call to gdbserver
+ A resume reply packet must then be built to inform GDB that the
+ resume request is finished.
+ resume_reply_packet_needed records the fact that the next call to gdbserver
must send a resume packet to gdb. */
-static Bool resume_packet_needed = False;
+static Bool resume_reply_packet_needed = False;
VG_MINIMAL_JMP_BUF(toplevel);
}
}
- if ( ((*the_target->target_xml)() != NULL
- || (*the_target->shadow_target_xml)() != NULL)
+ if ( (valgrind_target_xml() != NULL
+ || valgrind_shadow_target_xml() != NULL)
&& strncmp ("qXfer:features:read:", arg_own_buf, 20) == 0) {
CORE_ADDR ofs;
unsigned int len, doc_len;
shadow_target_xml. Fallback to target_xml
if not defined. */
if (VG_(clo_vgdb_shadow_registers)) {
- annex = (*the_target->shadow_target_xml)();
+ annex = valgrind_shadow_target_xml();
if (annex != NULL)
/* Ensure the shadow registers are initialized. */
initialize_shadow_low(True);
}
if (annex == NULL)
- annex = (*the_target->target_xml)();
+ annex = valgrind_target_xml();
if (annex == NULL) {
strcpy (arg_own_buf, "E00");
return;
if (VG_(client_auxv))
strcat (arg_own_buf, ";qXfer:auxv:read+");
- if ((*the_target->target_xml)() != NULL
- || (*the_target->shadow_target_xml)() != NULL) {
+ if (valgrind_target_xml() != NULL
+ || valgrind_shadow_target_xml() != NULL) {
strcat (arg_own_buf, ";qXfer:features:read+");
/* if a new gdb connects to us, we have to reset the register
set to the normal register sets to allow this new gdb to
struct thread_resume resume_info[2];
int n = 0;
- if (step || sig || (cont_thread != 0 && cont_thread != -1)) {
- resume_info[0].thread
- = ((struct inferior_list_entry *) current_inferior)->id;
+ if (step || sig) {
resume_info[0].step = step;
resume_info[0].sig = sig;
- resume_info[0].leave_stopped = 0;
n++;
}
- resume_info[n].thread = -1;
resume_info[n].step = 0;
resume_info[n].sig = 0;
- resume_info[n].leave_stopped = (cont_thread != 0 && cont_thread != -1);
- resume_packet_needed = True;
- (*the_target->resume) (resume_info);
+ resume_reply_packet_needed = True;
+ valgrind_resume (resume_info);
}
/* server_main global variables */
{
dlog(1, "gdbserver_init gdbserver embedded in valgrind: %s\n", version);
noack_mode = False;
- initialize_low ();
+ valgrind_initialize_target ();
// After a fork, gdbserver_init can be called again.
// We do not have to re-malloc the buffers in such a case.
if (own_buf == NULL)
unsigned int len;
CORE_ADDR mem_addr;
- zignal = mywait (&status);
+ zignal = valgrind_wait (&status);
if (VG_MINIMAL_SETJMP(toplevel)) {
dlog(0, "error caused VG_MINIMAL_LONGJMP to server_main\n");
}
int packet_len;
int new_packet_len = -1;
- if (resume_packet_needed) {
- resume_packet_needed = False;
+ if (resume_reply_packet_needed) {
+ /* Send the resume reply to reply to last GDB resume
+ request. */
+ resume_reply_packet_needed = False;
prepare_resume_reply (own_buf, status, zignal);
putpkt (own_buf);
}
remote_finish (reset_after_error);
remote_open (VG_(clo_vgdb_prefix));
myresume (0, 0);
- resume_packet_needed = False;
+ resume_reply_packet_needed = False;
return;
case '!':
/* We can not use the extended protocol with valgrind,
}
case 'm':
decode_m_packet (&own_buf[1], &mem_addr, &len);
- if (read_inferior_memory (mem_addr, mem_buf, len) == 0)
+ if (valgrind_read_memory (mem_addr, mem_buf, len) == 0)
convert_int_to_ascii (mem_buf, own_buf, len);
else
write_enn (own_buf);
break;
case 'M':
decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
- if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
+ if (valgrind_write_memory (mem_addr, mem_buf, len) == 0)
write_ok (own_buf);
else
write_enn (own_buf);
case 'X':
if (decode_X_packet (&own_buf[1], packet_len - 1,
&mem_addr, &len, mem_buf) < 0
- || write_inferior_memory (mem_addr, mem_buf, len) != 0)
+ || valgrind_write_memory (mem_addr, mem_buf, len) != 0)
write_enn (own_buf);
else
write_ok (own_buf);
int zlen = strtol (lenptr + 1, &dataptr, 16);
char type = own_buf[1];
- if (the_target->insert_watchpoint == NULL
- || (type < '0' || type > '4')) {
- /* No watchpoint support or not a watchpoint command;
- unrecognized either way. */
+ if (type < '0' || type > '4') {
+ /* Watchpoint command type unrecognized. */
own_buf[0] = '\0';
} else {
int res;
- res = (*the_target->insert_watchpoint) (type, addr, zlen);
+ res = valgrind_insert_watchpoint (type, addr, zlen);
if (res == 0)
write_ok (own_buf);
else if (res == 1)
int zlen = strtol (lenptr + 1, &dataptr, 16);
char type = own_buf[1];
- if (the_target->remove_watchpoint == NULL
- || (type < '0' || type > '4')) {
- /* No watchpoint support or not a watchpoint command;
- unrecognized either way. */
+ if (type < '0' || type > '4') {
+ /* Watchpoint command type unrecognized. */
own_buf[0] = '\0';
} else {
int res;
- res = (*the_target->remove_watchpoint) (type, addr, zlen);
+ res = valgrind_remove_watchpoint (type, addr, zlen);
if (res == 0)
write_ok (own_buf);
else if (res == 1)
break;
}
- if (mythread_alive (thread_id))
+ if (valgrind_thread_alive (thread_id))
write_ok (own_buf);
else
write_enn (own_buf);
remote_finish (reset_after_error);
remote_open (VG_(clo_vgdb_prefix));
myresume (0, 0);
- resume_packet_needed = False;
+ resume_reply_packet_needed = False;
return;
}
If debug info not found for this pc, assumes arm */
extern Addr thumb_pc (Addr pc);
-/* True if gdbserver is single stepping the valgrind process */
-extern Bool valgrind_single_stepping(void);
-
-/* Set Valgrind in single stepping mode or not according to Bool. */
-extern void valgrind_set_single_stepping(Bool);
-
-/* gets the addr at which a (possible) break must be ignored once.
- If there is no such break to be ignored once, 0 is returned.
- This is needed for the following case:
- The user sets a break at address AAA.
- The break is encountered. Then the user does stepi
- (i.e. step one instruction).
- In such a case, the already encountered break must be ignored
- to ensure the stepi will advance by one instruction: a "break"
- is implemented in valgrind by some helper code just after the
- instruction mark at which the break is set. This helper code
- verifies if either there is a break at the current PC
- or if we are in stepping mode. If we are in stepping mode,
- the already encountered break must be ignored once to advance
- to the next instruction.
- ??? need to check if this is *really* needed. */
-extern Addr valgrind_get_ignore_break_once(void);
-
-/* When addr > 0, ensures the next stop reply packet informs
- gdb about the encountered watchpoint.
- Use addr 0x0 to reset. */
-extern void VG_(set_watchpoint_stop_address) (Addr addr);
-
/* when invoked by vgdb using ptrace, contains the tid chosen
by vgdb (if vgdb gives a tid different of 0: a 0 tid by
vgdb means use the running_tid if there is one running
/* Target-specific functions */
-void initialize_low (void);
-
-/* initialize or re-initialize the register set of the low target.
- if shadow_mode, then (re-)define the normal and valgrind shadow registers
- else (re-)define only the normal registers. */
-void initialize_shadow_low (Bool shadow_mode);
-
/* From inferiors.c. */
extern struct inferior_list all_threads;
int unhexify (char *bin, const char *hex, int count);
int hexify (char *hex, const char *bin, int count);
+/* heximage builds an image of bin according to byte order of the architecture
+ Useful for register and int image */
+char* heximage (char *buf, char *bin, int count);
+
+/* convert from CORE_ADDR to void* */
+void* C2v(CORE_ADDR addr);
+
+
int remote_escape_output (const gdb_byte *buffer, int len,
gdb_byte *out_buf, int *out_len,
int out_maxlen);
Boston, MA 02110-1301, USA. */
#include "server.h"
+#include "target.h"
+#include "regdef.h"
+#include "regcache.h"
+#include "valgrind_low.h"
+#include "gdb/signals.h"
+#include "pub_core_aspacemgr.h"
+#include "pub_tool_machine.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_transtab.h"
+#include "pub_core_gdbserver.h"
+#include "pub_tool_debuginfo.h"
-struct target_ops *the_target;
+
+/* the_low_target defines the architecture specific aspects depending
+ on the cpu */
+static struct valgrind_target_ops the_low_target;
+
+static
+char *image_ptid(unsigned long ptid)
+{
+ static char result[100];
+ VG_(sprintf) (result, "id %ld", ptid);
+ return result;
+}
+#define get_thread(inf) ((struct thread_info *)(inf))
+static
+void remove_thread_if_not_in_vg_threads (struct inferior_list_entry *inf)
+{
+ struct thread_info *thread = get_thread (inf);
+ if (!VG_(lwpid_to_vgtid)(thread_to_gdb_id(thread))) {
+ dlog(1, "removing gdb ptid %s\n",
+ image_ptid(thread_to_gdb_id(thread)));
+ remove_thread (thread);
+ }
+}
+
+/* synchronize threads known by valgrind and threads known by gdbserver */
+static
+void valgrind_update_threads (int pid)
+{
+ ThreadId tid;
+ ThreadState *ts;
+ unsigned long ptid;
+ struct thread_info *ti;
+
+ /* call remove_thread for all gdb threads not in valgrind threads */
+ for_each_inferior (&all_threads, remove_thread_if_not_in_vg_threads);
+
+ /* call add_thread for all valgrind threads not known in gdb all_threads */
+ for (tid = 1; tid < VG_N_THREADS; tid++) {
+
+#define LOCAL_THREAD_TRACE " ti* %p vgtid %d status %s as gdb ptid %s lwpid %d\n", \
+ ti, tid, VG_(name_of_ThreadStatus) (ts->status), \
+ image_ptid (ptid), ts->os_state.lwpid
+
+ if (VG_(is_valid_tid) (tid)) {
+ ts = VG_(get_ThreadState) (tid);
+ ptid = ts->os_state.lwpid;
+ ti = gdb_id_to_thread (ptid);
+ if (!ti) {
+ /* we do not report the threads which are not yet fully
+ initialized otherwise this creates duplicated threads
+ in gdb: once with pid xxx lwpid 0, then after that
+ with pid xxx lwpid yyy. */
+ if (ts->status != VgTs_Init) {
+ dlog(1, "adding_thread" LOCAL_THREAD_TRACE);
+ add_thread (ptid, ts, ptid);
+ }
+ } else {
+ dlog(2, "(known thread)" LOCAL_THREAD_TRACE);
+ }
+ }
+#undef LOCAL_THREAD_TRACE
+ }
+}
+
+static
+struct reg* build_shadow_arch (struct reg *reg_defs, int n) {
+ int i, r;
+ static char *postfix[3] = { "", "s1", "s2" };
+ struct reg *new_regs = malloc(3 * n * sizeof(reg_defs[0]));
+ int reg_set_len = reg_defs[n-1].offset + reg_defs[n-1].size;
+
+ for (i = 0; i < 3; i++) {
+ for (r = 0; r < n; r++) {
+ new_regs[i*n + r].name = malloc(strlen(reg_defs[r].name)
+ + strlen (postfix[i]) + 1);
+ strcpy (new_regs[i*n + r].name, reg_defs[r].name);
+ strcat (new_regs[i*n + r].name, postfix[i]);
+ new_regs[i*n + r].offset = i*reg_set_len + reg_defs[r].offset;
+ new_regs[i*n + r].size = reg_defs[r].size;
+ dlog(1,
+ "%10s Nr %d offset(bit) %d offset(byte) %d size(bit) %d\n",
+ new_regs[i*n + r].name, i*n + r, new_regs[i*n + r].offset,
+ (new_regs[i*n + r].offset) / 8, new_regs[i*n + r].size);
+ }
+ }
+
+ return new_regs;
+}
+
+
+static CORE_ADDR stopped_data_address = 0;
+void VG_(set_watchpoint_stop_address) (Addr addr)
+{
+ stopped_data_address = addr;
+}
+
+int valgrind_stopped_by_watchpoint (void)
+{
+ return stopped_data_address != 0;
+}
+
+CORE_ADDR valgrind_stopped_data_address (void)
+{
+ return stopped_data_address;
+}
+
+/* pc at which we last stopped */
+static CORE_ADDR stop_pc;
+
+/* pc at which we resume.
+ If stop_pc != resume_pc, it means
+ gdb/gdbserver has changed the pc so as to have either
+ a "continue by jumping at that address"
+ or a "continue at that address to call some code from gdb".
+*/
+static CORE_ADDR resume_pc;
+
+static int vki_signal_to_report;
+
+void gdbserver_signal_encountered (Int vki_sigNo)
+{
+ vki_signal_to_report = vki_sigNo;
+}
+
+static int vki_signal_to_deliver;
+Bool gdbserver_deliver_signal (Int vki_sigNo)
+{
+ return vki_sigNo == vki_signal_to_deliver;
+}
+
+static
+char* sym (Addr addr)
+{
+ static char buf[200];
+ VG_(describe_IP) (addr, buf, 200);
+ return buf;
+}
+
+ThreadId vgdb_interrupted_tid = 0;
+
+/* 0 => not single stepping.
+ 1 => single stepping asked by gdb
+ 2 => single stepping asked by valgrind (watchpoint) */
+static int stepping = 0;
+
+Addr valgrind_get_ignore_break_once(void)
+{
+ if (valgrind_single_stepping())
+ return resume_pc;
+ else
+ return 0;
+}
+
+void valgrind_set_single_stepping(Bool set)
+{
+ if (set)
+ stepping = 2;
+ else
+ stepping = 0;
+}
+
+Bool valgrind_single_stepping(void)
+{
+ if (stepping)
+ return True;
+ else
+ return False;
+}
+
+int valgrind_thread_alive (unsigned long tid)
+{
+ struct thread_info *ti = gdb_id_to_thread(tid);
+ ThreadState *tst;
+
+ if (ti != NULL) {
+ tst = (ThreadState *) inferior_target_data (ti);
+ return tst->status != VgTs_Zombie;
+ }
+ else {
+ return 0;
+ }
+}
+
+void valgrind_resume (struct thread_resume *resume_info)
+{
+ dlog(1,
+ "resume_info step %d sig %d stepping %d\n",
+ resume_info->step,
+ resume_info->sig,
+ stepping);
+ if (valgrind_stopped_by_watchpoint()) {
+ dlog(1, "clearing watchpoint stopped_data_address %p\n",
+ C2v(stopped_data_address));
+ VG_(set_watchpoint_stop_address) ((Addr) 0);
+ }
+ vki_signal_to_deliver = resume_info->sig;
+
+ stepping = resume_info->step;
+ resume_pc = (*the_low_target.get_pc) ();
+ if (resume_pc != stop_pc) {
+ dlog(1,
+ "stop_pc %p changed to be resume_pc %s\n",
+ C2v(stop_pc), sym(resume_pc));
+ }
+ regcache_invalidate();
+}
+
+unsigned char valgrind_wait (char *ourstatus)
+{
+ int pid;
+ unsigned long wptid;
+ ThreadState *tst;
+ enum target_signal sig;
+
+ pid = VG_(getpid) ();
+ dlog(1, "enter valgrind_wait pid %d\n", pid);
+
+ regcache_invalidate();
+ valgrind_update_threads(pid);
+
+ /* in valgrind, we consider that a wait always succeeds with STOPPED 'T'
+ and with a signal TRAP (i.e. a breakpoint), unless there is
+ a signal to report. */
+ *ourstatus = 'T';
+ if (vki_signal_to_report == 0)
+ sig = TARGET_SIGNAL_TRAP;
+ else {
+ sig = target_signal_from_host(vki_signal_to_report);
+ vki_signal_to_report = 0;
+ }
+
+ if (vgdb_interrupted_tid != 0)
+ tst = VG_(get_ThreadState) (vgdb_interrupted_tid);
+ else
+ tst = VG_(get_ThreadState) (VG_(running_tid));
+ wptid = tst->os_state.lwpid;
+ /* we can only change the current_inferior when the wptid references
+ an existing thread. Otherwise, we are still in the init phase.
+ (hack similar to main thread hack in valgrind_update_threads) */
+ if (tst->os_state.lwpid)
+ current_inferior = gdb_id_to_thread (wptid);
+ stop_pc = (*the_low_target.get_pc) ();
+
+ dlog(1,
+ "exit valgrind_wait returns ptid %s stop_pc %s signal %d\n",
+ image_ptid (wptid), sym (stop_pc), sig);
+ return sig;
+}
+
+/* Fetch one register from valgrind VEX guest state. */
+static
+void fetch_register (int regno)
+{
+ int size;
+ ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
+ ThreadId tid = tst->tid;
+
+ if (regno >= the_low_target.num_regs) {
+ dlog(0, "error fetch_register regno %d max %d\n",
+ regno, the_low_target.num_regs);
+ return;
+ }
+ size = register_size (regno);
+ if (size > 0) {
+ Bool mod;
+ char buf [size];
+ VG_(memset) (buf, 0, size); // registers not fetched will be seen as 0.
+ (*the_low_target.transfer_register) (tid, regno, buf,
+ valgrind_to_gdbserver, size, &mod);
+ // Note: the *mod received from transfer_register is not interesting.
+ // We are interested to see if the register data in the register cache is modified.
+ supply_register (regno, buf, &mod);
+ if (mod && VG_(debugLog_getLevel)() > 1) {
+ char bufimage [2*size + 1];
+ heximage (bufimage, buf, size);
+ dlog(2, "fetched register %d size %d name %s value %s tid %d status %s\n",
+ regno, size, the_low_target.reg_defs[regno].name, bufimage,
+ tid, VG_(name_of_ThreadStatus) (tst->status));
+ }
+ }
+}
+
+/* Fetch all registers, or just one, from the child process. */
+static
+void usr_fetch_inferior_registers (int regno)
+{
+ if (regno == -1 || regno == 0)
+ for (regno = 0; regno < the_low_target.num_regs; regno++)
+ fetch_register (regno);
+ else
+ fetch_register (regno);
+}
+
+/* Store our register values back into the inferior.
+ If REGNO is -1, do this for all registers.
+ Otherwise, REGNO specifies which register (so we can save time). */
+static
+void usr_store_inferior_registers (int regno)
+{
+ int size;
+ ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
+ ThreadId tid = tst->tid;
+
+ if (regno >= 0) {
+
+ if (regno >= the_low_target.num_regs) {
+ dlog(0, "error store_register regno %d max %d\n",
+ regno, the_low_target.num_regs);
+ return;
+ }
+
+ size = register_size (regno);
+ if (size > 0) {
+ Bool mod;
+ Addr old_SP, new_SP;
+ char buf[size];
+
+ if (regno == the_low_target.stack_pointer_regno) {
+ /* When the stack pointer register is changed such that
+ the stack is extended, we better inform the tool of the
+ stack increase. This is needed in particular to avoid
+ spurious Memcheck errors during Inferior calls. So, we
+ save in old_SP the SP before the change. A change of
+ stack pointer is also assumed to have initialised this
+ new stack space. For the typical example of an inferior
+ call, gdb writes arguments on the stack, and then
+ changes the stack pointer. As the stack increase tool
+ function might mark it as undefined, we have to call it
+ at the good moment. */
+ VG_(memset) ((void *) &old_SP, 0, size);
+ (*the_low_target.transfer_register) (tid, regno, (void *) &old_SP,
+ valgrind_to_gdbserver, size, &mod);
+ }
+
+ VG_(memset) (buf, 0, size);
+ collect_register (regno, buf);
+ (*the_low_target.transfer_register) (tid, regno, buf,
+ gdbserver_to_valgrind, size, &mod);
+ if (mod && VG_(debugLog_getLevel)() > 1) {
+ char bufimage [2*size + 1];
+ heximage (bufimage, buf, size);
+ dlog(2,
+ "stored register %d size %d name %s value %s "
+ "tid %d status %s\n",
+ regno, size, the_low_target.reg_defs[regno].name, bufimage,
+ tid, VG_(name_of_ThreadStatus) (tst->status));
+ }
+ if (regno == the_low_target.stack_pointer_regno) {
+ VG_(memcpy) (&new_SP, buf, size);
+ if (old_SP > new_SP) {
+ Word delta = (Word)new_SP - (Word)old_SP;
+ dlog(1,
+ " stack increase by stack pointer changed from %p to %p "
+ "delta %ld\n",
+ (void*) old_SP, (void *) new_SP,
+ delta);
+ VG_TRACK( new_mem_stack_w_ECU, new_SP, -delta, 0 );
+ VG_TRACK( new_mem_stack, new_SP, -delta );
+ if (VG_(tdict).track_post_mem_write) {
+ VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid,
+ new_SP, -delta);
+ }
+ }
+ }
+ }
+ }
+ else {
+ for (regno = 0; regno < the_low_target.num_regs; regno++)
+ usr_store_inferior_registers (regno);
+ }
+}
+
+void valgrind_fetch_registers (int regno)
+{
+ usr_fetch_inferior_registers (regno);
+}
+
+void valgrind_store_registers (int regno)
+{
+ usr_store_inferior_registers (regno);
+}
+
+int valgrind_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ const void *sourceaddr = C2v (memaddr);
+ dlog(2, "reading memory %p size %d\n", sourceaddr, len);
+ if (!VG_(am_is_valid_for_client_or_free_or_resvn) ((Addr) sourceaddr,
+ len, VKI_PROT_READ)) {
+ dlog(1, "error reading memory %p size %d\n", sourceaddr, len);
+ return -1;
+ }
+ VG_(memcpy) (myaddr, sourceaddr, len);
+ return 0;
+}
+
+int valgrind_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+ void *targetaddr = C2v (memaddr);
+ dlog(2, "writing memory %p size %d\n", targetaddr, len);
+ if (!VG_(am_is_valid_for_client_or_free_or_resvn) ((Addr)targetaddr,
+ len, VKI_PROT_WRITE)) {
+ dlog(1, "error writing memory %p size %d\n", targetaddr, len);
+ return -1;
+ }
+ if (len > 0) {
+ VG_(memcpy) (targetaddr, myaddr, len);
+ if (VG_(tdict).track_post_mem_write) {
+ /* Inform the tool of the post memwrite. Note that we do the
+ minimum necessary to avoid complains from e.g.
+ memcheck. The idea is that the debugger is as least
+ intrusive as possible. So, we do not inform of the pre
+ mem write (and in any case, this would cause problems with
+ memcheck that does not like our CorePart in
+ pre_mem_write. */
+ ThreadState *tst =
+ (ThreadState *) inferior_target_data (current_inferior);
+ ThreadId tid = tst->tid;
+ VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid,
+ (Addr) targetaddr, len );
+ }
+ }
+ return 0;
+}
+
+/* insert or remove a breakpoint */
+static
+int valgrind_point (Bool insert, char type, CORE_ADDR addr, int len)
+{
+ PointKind kind;
+ switch (type) {
+ case '0': /* implemented by inserting checks at each instruction in sb */
+ kind = software_breakpoint;
+ break;
+ case '1': /* hw breakpoint, same implementation as sw breakpoint */
+ kind = hardware_breakpoint;
+ break;
+ case '2':
+ kind = write_watchpoint;
+ break;
+ case '3':
+ kind = read_watchpoint;
+ break;
+ case '4':
+ kind = access_watchpoint;
+ break;
+ default:
+ vg_assert (0);
+ }
+
+ /* Attention: gdbserver convention differs: 0 means ok; 1 means not ok */
+ if (VG_(gdbserver_point) (kind, insert, addr, len))
+ return 0;
+ else
+ return 1; /* error or unsupported */
+}
+
+char* valgrind_target_xml (void)
+{
+ return (char *) the_low_target.target_xml;
+}
+
+char* valgrind_shadow_target_xml (void)
+{
+ return (char *) the_low_target.shadow_target_xml;
+}
+
+int valgrind_insert_watchpoint (char type, CORE_ADDR addr, int len)
+{
+ return valgrind_point (/* insert */ True, type, addr, len);
+}
+
+int valgrind_remove_watchpoint (char type, CORE_ADDR addr, int len)
+{
+ return valgrind_point (/* insert*/ False, type, addr, len);
+}
+
+/* returns a pointer to the architecture state corresponding to
+ the provided register set: 0 => normal guest registers,
+ 1 => shadow1
+ 2 => shadow2
+*/
+VexGuestArchState* get_arch (int set, ThreadState* tst)
+{
+ switch (set) {
+ case 0: return &tst->arch.vex;
+ case 1: return &tst->arch.vex_shadow1;
+ case 2: return &tst->arch.vex_shadow2;
+ default: vg_assert(0);
+ }
+}
+
+static int non_shadow_num_regs = 0;
+static struct reg *non_shadow_reg_defs = NULL;
+void initialize_shadow_low(Bool shadow_mode)
+{
+ if (non_shadow_reg_defs == NULL) {
+ non_shadow_reg_defs = the_low_target.reg_defs;
+ non_shadow_num_regs = the_low_target.num_regs;
+ }
+
+ regcache_invalidate();
+ if (the_low_target.reg_defs != non_shadow_reg_defs) {
+ free (the_low_target.reg_defs);
+ }
+ if (shadow_mode) {
+ the_low_target.num_regs = 3 * non_shadow_num_regs;
+ the_low_target.reg_defs = build_shadow_arch (non_shadow_reg_defs, non_shadow_num_regs);
+ } else {
+ the_low_target.num_regs = non_shadow_num_regs;
+ the_low_target.reg_defs = non_shadow_reg_defs;
+ }
+ set_register_cache (the_low_target.reg_defs, the_low_target.num_regs);
+}
void set_desired_inferior (int use_general)
{
}
}
-int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- int res;
- res = (*the_target->read_memory) (memaddr, myaddr, len);
- return res;
-}
-
-int write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
- int len)
-{
- /* Lacking cleanups, there is some potential for a memory leak if the
- write fails and we go through error(). Make sure that no more than
- one buffer is ever pending by making BUFFER static. */
- static unsigned char *buffer = 0;
- int res;
-
- if (buffer != NULL)
- free (buffer);
-
- buffer = malloc (len);
- VG_(memcpy) (buffer, myaddr, len);
- res = (*the_target->write_memory) (memaddr, buffer, len);
- free (buffer);
- buffer = NULL;
-
- return res;
-}
-
-void set_target_ops (struct target_ops *target)
-{
- // Can be called again after a fork => do not re-malloc the_target.
- if (the_target == NULL)
- the_target = (struct target_ops *) malloc (sizeof (*the_target));
- VG_(memcpy) (the_target, target, sizeof (*the_target));
-}
-
void* VG_(dmemcpy) ( void *d, const void *s, SizeT sz, Bool *mod )
{
if (VG_(memcmp) (d, s, sz)) {
else
vg_assert (0);
}
+
+void valgrind_initialize_target(void)
+{
+#if defined(VGA_x86)
+ x86_init_architecture(&the_low_target);
+#elif defined(VGA_amd64)
+ amd64_init_architecture(&the_low_target);
+#elif defined(VGA_arm)
+ arm_init_architecture(&the_low_target);
+#elif defined(VGA_ppc32)
+ ppc32_init_architecture(&the_low_target);
+#elif defined(VGA_ppc64)
+ ppc64_init_architecture(&the_low_target);
+#elif defined(VGA_s390x)
+ s390x_init_architecture(&the_low_target);
+#else
+ architecture missing in target.c valgrind_initialize_target
+#endif
+}
-/* Target operations for the remote server for GDB.
- Copyright (C) 2002, 2003, 2004, 2005
+/* Target operations for the Valgrind remote server for GDB.
+ Copyright (C) 2002, 2003, 2004, 2005, 2012
Free Software Foundation, Inc.
+ Philippe Waroquiers.
Contributed by MontaVista Software.
#ifndef TARGET_H
#define TARGET_H
-/* This structure describes how to resume a particular thread (or
- all threads) based on the client's request. If thread is -1, then
- this entry applies to all threads. These are generally passed around
- as an array, and terminated by a thread == -1 entry. */
+/* This file defines the architecture independent Valgrind gdbserver
+ high level operations such as read memory, get/set registers, ...
-struct thread_resume
-{
- unsigned long thread;
-
- /* If non-zero, leave this thread stopped. */
- int leave_stopped;
-
- /* If non-zero, we want to single-step. */
- int step;
-
- /* If non-zero, send this signal when we resume. */
- int sig;
-};
-
-struct target_ops
-{
- /* Return 1 iff the thread with process ID PID is alive. */
-
- int (*thread_alive) (unsigned long pid);
-
- /* Resume the inferior process. */
-
- void (*resume) (struct thread_resume *resume_info);
-
- /* Wait for the inferior process to change state.
-
- STATUS will be filled in with a response code to send to GDB.
-
- Returns the signal which caused the process to stop, in the
- remote protocol numbering (e.g. TARGET_SIGNAL_STOP), or the
- exit code as an integer if *STATUS is 'W'. */
-
- unsigned char (*wait) (char *status);
-
- /* Fetch registers from the inferior process.
-
- If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */
-
- void (*fetch_registers) (int regno);
-
- /* Store registers to the inferior process.
-
- If REGNO is -1, store all registers; otherwise, store at least REGNO. */
-
- void (*store_registers) (int regno);
+ These high level operations are called by the gdbserver
+ protocol implementation (e.g. typically server.c).
+
+ For some of these high level operations, target.c will call
+ low level operations dependent on the architecture.
+
+ For example, getting or setting the registers will work on a
+ register cache. The exact details of the registers (how much,
+ their size, etc) is not defined by target.c or the register cache.
- /* Read memory from the inferior process. This should generally be
- called through read_inferior_memory, which handles breakpoint shadowing.
+ Such architecture dependent information is defined by
+ valgrind_low.h/valgrind-low-xxxxx.c providing 'low level operations'
+ specific to the xxxxx architecture (for example,
+ valgrind-low-x86.c, valgrind-low-armc.c). */
+
+/* -------------------------------------------------------------------------- */
+/* ------------------------ Initialisation ---------------------------------- */
+/* -------------------------------------------------------------------------- */
- Read LEN bytes at MEMADDR into a buffer at MYADDR.
-
- Returns 0 on success and errno on failure. */
+/* Initialize the Valgrind high target. This will in turn
+ initialise the low (architecture specific) target. */
+extern void valgrind_initialize_target(void);
- int (*read_memory) (CORE_ADDR memaddr, unsigned char *myaddr, int len);
+/* initialize or re-initialize the register set of the low target.
+ if shadow_mode, then (re-)define the normal and valgrind shadow registers
+ else (re-)define only the normal registers. */
+extern void initialize_shadow_low (Bool shadow_mode);
- /* Write memory to the inferior process. This should generally be
- called through write_inferior_memory, which handles breakpoint shadowing.
+/* Returns the name of the xml target description file.
+ returns NULL if no xml target description available. */
+extern char* valgrind_target_xml (void);
- Write LEN bytes from the buffer at MYADDR to MEMADDR.
+/* Same but describes also the shadow registers. */
+extern char* valgrind_shadow_target_xml (void);
- Returns 0 on success and errno on failure. */
- int (*write_memory) (CORE_ADDR memaddr, const unsigned char *myaddr,
- int len);
- /* Send a signal to the inferior process, however is appropriate. */
- void (*send_signal) (int);
+/* -------------------------------------------------------------------------- */
+/* --------------------------- Execution control ---------------------------- */
+/* -------------------------------------------------------------------------- */
- /* Returns the name of the xml target description file.
- returns NULL if no xml target description available. */
- char* (*target_xml)(void);
-
- /* Same but describes also the shadow registers. */
- char* (*shadow_target_xml)(void);
-
- /* Insert and remove a hardware watchpoint.
- Returns 0 on success, -1 on failure and 1 on unsupported.
- The type is coded as follows:
- 2 = write watchpoint
- 3 = read watchpoint
- 4 = access watchpoint
- */
-
- int (*insert_watchpoint) (char type, CORE_ADDR addr, int len);
- int (*remove_watchpoint) (char type, CORE_ADDR addr, int len);
-
- /* Returns 1 if target was stopped due to a watchpoint hit, 0 otherwise. */
-
- int (*stopped_by_watchpoint) (void);
-
- /* Returns the address associated with the watchpoint that hit, if any;
- returns 0 otherwise. */
-
- CORE_ADDR (*stopped_data_address) (void);
+/* This structure describes how to resume the execution.
+ Currently, there is no way to resume only a specific thread. */
+struct thread_resume
+{
+ /* If non-zero, we want to single-step. */
+ int step;
+ /* If non-zero, send this signal when we resume. */
+ int sig;
};
-extern struct target_ops *the_target;
-
-void set_target_ops (struct target_ops *);
-
-#define detach_inferior() \
- (*the_target->detach) ()
-
-#define mythread_alive(pid) \
- (*the_target->thread_alive) (pid)
-
-#define fetch_inferior_registers(regno) \
- (*the_target->fetch_registers) (regno)
-
-#define store_inferior_registers(regno) \
- (*the_target->store_registers) (regno)
-
-#define mywait(statusp) \
- (*the_target->wait) (statusp)
-
-int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len);
-
-int write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
- int len);
-
-void set_desired_inferior (int id);
+/* Prepare to Resume (i.e. restart) the guest.
+ The resume info indicates how the resume will be done.
+ In case GDB has changed the program counter, valgrind_resume
+ will also ensure that the execution will be resumed at this
+ new program counter.
+ The Resume is really only executed once the gdbserver
+ returns (giving back the control to Valgrind). */
+extern void valgrind_resume (struct thread_resume *resume_info);
+
+/* When Valgrind gets the control, it will execute the guest
+ process till there is a reason to call the gdbserver
+ again (e.g. because a breakpoint is encountered or the
+ tool reports an error).
+ In such case, the executionof guest code stops, and the
+ control is given to gdbserver. Gdbserver will send a resume
+ reply packet to GDB.
+
+ valgrind_wait gets from Valgrind data structures the
+ information needed produce the resume reply for GDB:
+ a.o. OURSTATUS will be filled in with a response code to send to GDB.
+
+ Returns the signal which caused the process to stop, in the
+ remote protocol numbering (e.g. TARGET_SIGNAL_STOP), or the
+ exit code as an integer if *OURSTATUS is 'W'. */
+extern unsigned char valgrind_wait (char *outstatus);
+
+/* When execution is stopped and gdbserver has control, more
+ info about the stop reason can be retrieved using the following
+ functions. */
+
+/* gets the addr at which a (possible) break must be ignored once.
+ If there is no such break to be ignored once, 0 is returned.
+ This is needed for the following case:
+ The user sets a break at address AAA.
+ The break is encountered. Then the user does stepi
+ (i.e. step one instruction).
+ In such a case, the already encountered break must be ignored
+ to ensure the stepi will advance by one instruction: a "break"
+ is implemented in valgrind by some helper code just after the
+ instruction mark at which the break is set. This helper code
+ verifies if either there is a break at the current PC
+ or if we are in stepping mode. If we are in stepping mode,
+ the already encountered break must be ignored once to advance
+ to the next instruction.
+ ??? need to check if this is *really* needed. */
+extern Addr valgrind_get_ignore_break_once(void);
+
+/* When addr > 0, ensures the next resume reply packet informs
+ gdb about the encountered watchpoint.
+ valgrind_stopped_by_watchpoint() will return 1 till reset.
+ Use addr 0x0 to reset. */
+extern void VG_(set_watchpoint_stop_address) (Addr addr);
+
+/* Returns 1 if target was stopped due to a watchpoint hit, 0 otherwise. */
+extern int valgrind_stopped_by_watchpoint (void);
+
+/* Returns the address associated with the watchpoint that hit, if any;
+ returns 0 otherwise. */
+extern CORE_ADDR valgrind_stopped_data_address (void);
+
+/* True if gdbserver is single stepping the valgrind process */
+extern Bool valgrind_single_stepping(void);
+
+/* Set Valgrind in single stepping mode or not according to Bool. */
+extern void valgrind_set_single_stepping(Bool);
+
+/* -------------------------------------------------------------------------- */
+/* ----------------- Examining/modifying data while stopped ----------------- */
+/* -------------------------------------------------------------------------- */
+
+/* Return 1 iff the thread with ID tid is alive. */
+extern int valgrind_thread_alive (unsigned long tid);
+
+/* Allows to controls the thread (current_inferior) used for following
+ valgrind_(fetch|store)_registers calls.
+ If USE_GENERAL,
+ current_inferior is set to general_thread
+ else
+ current_inferior is set to step_thread or else cont_thread.
+ If the above gives no valid thread, then current_inferior is
+ set to the first valid thread. */
+extern void set_desired_inferior (int use_general);
+
+/* Fetch registers from the current_inferior thread.
+ If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */
+extern void valgrind_fetch_registers (int regno);
+
+/* Store registers to the current_inferior thread.
+ If REGNO is -1, store all registers; otherwise, store at least REGNO. */
+extern void valgrind_store_registers (int regno);
+
+
+
+/* Read memory from the inferior process.
+ Read LEN bytes at MEMADDR into a buffer at MYADDR.
+ Returns 0 on success and errno on failure. */
+extern int valgrind_read_memory (CORE_ADDR memaddr,
+ unsigned char *myaddr, int len);
+
+/* Write memory to the inferior process.
+ Write LEN bytes from the buffer at MYADDR to MEMADDR.
+ Returns 0 on success and errno on failure. */
+extern int valgrind_write_memory (CORE_ADDR memaddr,
+ const unsigned char *myaddr, int len);
+
+
+/* Insert and remove a hardware watchpoint.
+ Returns 0 on success, -1 on failure and 1 on unsupported.
+ The type is coded as follows:
+ 2 = write watchpoint
+ 3 = read watchpoint
+ 4 = access watchpoint
+*/
+extern int valgrind_insert_watchpoint (char type, CORE_ADDR addr, int len);
+extern int valgrind_remove_watchpoint (char type, CORE_ADDR addr, int len);
+
+
+/* -------------------------------------------------------------------------- */
+/* ----------- Utils functions for low level arch specific files ------------ */
+/* -------------------------------------------------------------------------- */
+
+/* returns a pointer to the architecture state corresponding to
+ the provided register set: 0 => normal guest registers,
+ 1 => shadow1
+ 2 => shadow2
+*/
+extern VexGuestArchState* get_arch (int set, ThreadState* tst);
/* like memcpy but first check if content of destination and source
differs. If no difference, no copy is done, *mod set to False.
SizeT sz,
Bool *mod);
+
+
#endif /* TARGET_H */
case 13: VG_(transfer) (&amd64->guest_R13, buf, dir, size, mod); break;
case 14: VG_(transfer) (&amd64->guest_R14, buf, dir, size, mod); break;
case 15: VG_(transfer) (&amd64->guest_R15, buf, dir, size, mod); break;
- case 16:
- VG_(transfer) (&amd64->guest_RIP, buf, dir, size, mod);
- if (*mod && VG_(debugLog_getLevel)() > 2) {
- char bufimage [2*sizeof(amd64->guest_IP_AT_SYSCALL) + 1];
- heximage (bufimage,
- (char *) &amd64->guest_IP_AT_SYSCALL,
- sizeof(amd64->guest_IP_AT_SYSCALL));
- dlog(3, "guest_IP_AT_SYSCALL %s\n", bufimage);
- }
- break;
+ case 16: VG_(transfer) (&amd64->guest_RIP, buf, dir, size, mod); break;
case 17:
if (dir == valgrind_to_gdbserver) {
ULong rflags;
case 5: VG_(transfer) (&x86->guest_EBP, buf, dir, size, mod); break;
case 6: VG_(transfer) (&x86->guest_ESI, buf, dir, size, mod); break;
case 7: VG_(transfer) (&x86->guest_EDI, buf, dir, size, mod); break;
- case 8:
- VG_(transfer) (&x86->guest_EIP, buf, dir, size, mod);
- if (*mod && VG_(debugLog_getLevel)() > 2) {
- char bufimage [2*sizeof(x86->guest_IP_AT_SYSCALL) + 1];
- heximage (bufimage,
- (char *) &x86->guest_IP_AT_SYSCALL,
- sizeof(x86->guest_IP_AT_SYSCALL));
- dlog(3, "guest_IP_AT_SYSCALL %s\n", bufimage);
- }
- break;
+ case 8: VG_(transfer) (&x86->guest_EIP, buf, dir, size, mod); break;
case 9:
if (dir == valgrind_to_gdbserver) {
UInt eflags;
+++ /dev/null
-/* Low level interface to valgrind, for the remote server for GDB integrated
- in valgrind.
- Copyright (C) 2011
- Free Software Foundation, Inc.
-
- This file is part of VALGRIND.
- It has been inspired from a file from gdbserver in gdb 6.6.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
-
-#include "server.h"
-#include "target.h"
-#include "regdef.h"
-#include "regcache.h"
-#include "valgrind_low.h"
-#include "gdb/signals.h"
-#include "pub_core_aspacemgr.h"
-#include "pub_tool_machine.h"
-#include "pub_core_threadstate.h"
-#include "pub_core_transtab.h"
-#include "pub_core_gdbserver.h"
-#include "pub_tool_debuginfo.h"
-
-/* the_low_target defines the architecture specific aspects depending
- on the cpu */
-static struct valgrind_target_ops the_low_target;
-
-/* builds an image of bin according to byte order of the architecture
- Useful for register and int image */
-char* heximage (char *buf, char *bin, int count)
-{
-#if defined(VGA_x86) || defined(VGA_amd64)
- char rev[count];
- /* note: no need for trailing \0, length is known with count */
- int i;
- for (i = 0; i < count; i++)
- rev[i] = bin[count - i - 1];
- hexify (buf, rev, count);
-#else
- hexify (buf, bin, count);
-#endif
- return buf;
-}
-
-void* C2v(CORE_ADDR addr)
-{
- return (void*) addr;
-}
-
-static
-char *image_ptid(unsigned long ptid)
-{
- static char result[100];
- VG_(sprintf) (result, "id %ld", ptid);
- return result;
-}
-#define get_thread(inf) ((struct thread_info *)(inf))
-static
-void remove_thread_if_not_in_vg_threads (struct inferior_list_entry *inf)
-{
- struct thread_info *thread = get_thread (inf);
- if (!VG_(lwpid_to_vgtid)(thread_to_gdb_id(thread))) {
- dlog(1, "removing gdb ptid %s\n",
- image_ptid(thread_to_gdb_id(thread)));
- remove_thread (thread);
- }
-}
-
-/* synchronize threads known by valgrind and threads known by gdbserver */
-static
-void valgrind_update_threads (int pid)
-{
- ThreadId tid;
- ThreadState *ts;
- unsigned long ptid;
- struct thread_info *ti;
-
- /* call remove_thread for all gdb threads not in valgrind threads */
- for_each_inferior (&all_threads, remove_thread_if_not_in_vg_threads);
-
- /* call add_thread for all valgrind threads not known in gdb all_threads */
- for (tid = 1; tid < VG_N_THREADS; tid++) {
-
-#define LOCAL_THREAD_TRACE " ti* %p vgtid %d status %s as gdb ptid %s lwpid %d\n", \
- ti, tid, VG_(name_of_ThreadStatus) (ts->status), \
- image_ptid (ptid), ts->os_state.lwpid
-
- if (VG_(is_valid_tid) (tid)) {
- ts = VG_(get_ThreadState) (tid);
- ptid = ts->os_state.lwpid;
- ti = gdb_id_to_thread (ptid);
- if (!ti) {
- /* we do not report the threads which are not yet fully
- initialized otherwise this creates duplicated threads
- in gdb: once with pid xxx lwpid 0, then after that
- with pid xxx lwpid yyy. */
- if (ts->status != VgTs_Init) {
- dlog(1, "adding_thread" LOCAL_THREAD_TRACE);
- add_thread (ptid, ts, ptid);
- }
- } else {
- dlog(2, "(known thread)" LOCAL_THREAD_TRACE);
- }
- }
-#undef LOCAL_THREAD_TRACE
- }
-}
-
-/* Return nonzero if the given thread is still alive. */
-static
-int valgrind_thread_alive (unsigned long tid)
-{
- struct thread_info *ti = gdb_id_to_thread(tid);
- ThreadState *tst;
-
- if (ti != NULL) {
- tst = (ThreadState *) inferior_target_data (ti);
- return tst->status != VgTs_Zombie;
- }
- else {
- return 0;
- }
-}
-
-/* allocate and build a register structure containing the shadow registers.
- reg_defs is the normal registers, n is their numbers */
-static
-struct reg* build_shadow_arch (struct reg *reg_defs, int n) {
- int i, r;
- static char *postfix[3] = { "", "s1", "s2" };
- struct reg *new_regs = malloc(3 * n * sizeof(reg_defs[0]));
- int reg_set_len = reg_defs[n-1].offset + reg_defs[n-1].size;
-
- for (i = 0; i < 3; i++) {
- for (r = 0; r < n; r++) {
- new_regs[i*n + r].name = malloc(strlen(reg_defs[r].name)
- + strlen (postfix[i]) + 1);
- strcpy (new_regs[i*n + r].name, reg_defs[r].name);
- strcat (new_regs[i*n + r].name, postfix[i]);
- new_regs[i*n + r].offset = i*reg_set_len + reg_defs[r].offset;
- new_regs[i*n + r].size = reg_defs[r].size;
- dlog(1,
- "%10s Nr %d offset(bit) %d offset(byte) %d size(bit) %d\n",
- new_regs[i*n + r].name, i*n + r, new_regs[i*n + r].offset,
- (new_regs[i*n + r].offset) / 8, new_regs[i*n + r].size);
- }
- }
-
- return new_regs;
-}
-
-/* Fetch one register from valgrind VEX guest state. */
-static
-void fetch_register (int regno)
-{
- int size;
- ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
- ThreadId tid = tst->tid;
-
- if (regno >= the_low_target.num_regs) {
- dlog(0, "error fetch_register regno %d max %d\n",
- regno, the_low_target.num_regs);
- return;
- }
- size = register_size (regno);
- if (size > 0) {
- Bool mod;
- char buf [size];
- VG_(memset) (buf, 0, size); // registers not fetched will be seen as 0.
- (*the_low_target.transfer_register) (tid, regno, buf,
- valgrind_to_gdbserver, size, &mod);
- // Note: the *mod received from transfer_register is not interesting.
- // We are interested to see if the register data in the register cache is modified.
- supply_register (regno, buf, &mod);
- if (mod && VG_(debugLog_getLevel)() > 1) {
- char bufimage [2*size + 1];
- heximage (bufimage, buf, size);
- dlog(2, "fetched register %d size %d name %s value %s tid %d status %s\n",
- regno, size, the_low_target.reg_defs[regno].name, bufimage,
- tid, VG_(name_of_ThreadStatus) (tst->status));
- }
- }
-}
-
-/* Fetch all registers, or just one, from the child process. */
-static
-void usr_fetch_inferior_registers (int regno)
-{
- if (regno == -1 || regno == 0)
- for (regno = 0; regno < the_low_target.num_regs; regno++)
- fetch_register (regno);
- else
- fetch_register (regno);
-}
-
-/* Store our register values back into the inferior.
- If REGNO is -1, do this for all registers.
- Otherwise, REGNO specifies which register (so we can save time). */
-static
-void usr_store_inferior_registers (int regno)
-{
- int size;
- ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
- ThreadId tid = tst->tid;
-
- if (regno >= 0) {
-
- if (regno >= the_low_target.num_regs) {
- dlog(0, "error store_register regno %d max %d\n",
- regno, the_low_target.num_regs);
- return;
- }
-
- size = register_size (regno);
- if (size > 0) {
- Bool mod;
- Addr old_SP, new_SP;
- char buf[size];
-
- if (regno == the_low_target.stack_pointer_regno) {
- /* When the stack pointer register is changed such that
- the stack is extended, we better inform the tool of the
- stack increase. This is needed in particular to avoid
- spurious Memcheck errors during Inferior calls. So, we
- save in old_SP the SP before the change. A change of
- stack pointer is also assumed to have initialised this
- new stack space. For the typical example of an inferior
- call, gdb writes arguments on the stack, and then
- changes the stack pointer. As the stack increase tool
- function might mark it as undefined, we have to call it
- at the good moment. */
- VG_(memset) ((void *) &old_SP, 0, size);
- (*the_low_target.transfer_register) (tid, regno, (void *) &old_SP,
- valgrind_to_gdbserver, size, &mod);
- }
-
- VG_(memset) (buf, 0, size);
- collect_register (regno, buf);
- (*the_low_target.transfer_register) (tid, regno, buf,
- gdbserver_to_valgrind, size, &mod);
- if (mod && VG_(debugLog_getLevel)() > 1) {
- char bufimage [2*size + 1];
- heximage (bufimage, buf, size);
- dlog(2,
- "stored register %d size %d name %s value %s "
- "tid %d status %s\n",
- regno, size, the_low_target.reg_defs[regno].name, bufimage,
- tid, VG_(name_of_ThreadStatus) (tst->status));
- }
- if (regno == the_low_target.stack_pointer_regno) {
- VG_(memcpy) (&new_SP, buf, size);
- if (old_SP > new_SP) {
- Word delta = (Word)new_SP - (Word)old_SP;
- dlog(1,
- " stack increase by stack pointer changed from %p to %p "
- "delta %ld\n",
- (void*) old_SP, (void *) new_SP,
- delta);
- VG_TRACK( new_mem_stack_w_ECU, new_SP, -delta, 0 );
- VG_TRACK( new_mem_stack, new_SP, -delta );
- if (VG_(tdict).track_post_mem_write) {
- VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid,
- new_SP, -delta);
- }
- }
- }
- }
- }
- else {
- for (regno = 0; regno < the_low_target.num_regs; regno++)
- usr_store_inferior_registers (regno);
- }
-}
-
-static
-void valgrind_fetch_registers (int regno)
-{
- usr_fetch_inferior_registers (regno);
-}
-
-static
-void valgrind_store_registers (int regno)
-{
- usr_store_inferior_registers (regno);
-}
-
-/* Copy LEN bytes from inferior's memory starting at MEMADDR
- to debugger memory starting at MYADDR. */
-
-static
-int valgrind_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
-{
- const void *sourceaddr = C2v (memaddr);
- dlog(2, "reading memory %p size %d\n", sourceaddr, len);
- if (!VG_(am_is_valid_for_client_or_free_or_resvn) ((Addr) sourceaddr,
- len, VKI_PROT_READ)) {
- dlog(1, "error reading memory %p size %d\n", sourceaddr, len);
- return -1;
- }
- VG_(memcpy) (myaddr, sourceaddr, len);
- return 0;
-}
-
-/* Copy LEN bytes of data from debugger memory at MYADDR
- to inferior's memory at MEMADDR.
- On failure (cannot write the inferior)
- returns the value of errno. */
-
-static
-int valgrind_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
-{
- void *targetaddr = C2v (memaddr);
- dlog(2, "writing memory %p size %d\n", targetaddr, len);
- if (!VG_(am_is_valid_for_client_or_free_or_resvn) ((Addr)targetaddr,
- len, VKI_PROT_WRITE)) {
- dlog(1, "error writing memory %p size %d\n", targetaddr, len);
- return -1;
- }
- if (len > 0) {
- VG_(memcpy) (targetaddr, myaddr, len);
- if (VG_(tdict).track_post_mem_write) {
- /* Inform the tool of the post memwrite. Note that we do the
- minimum necessary to avoid complains from e.g.
- memcheck. The idea is that the debugger is as least
- intrusive as possible. So, we do not inform of the pre
- mem write (and in any case, this would cause problems with
- memcheck that does not like our CorePart in
- pre_mem_write. */
- ThreadState *tst = (ThreadState *) inferior_target_data (current_inferior);
- ThreadId tid = tst->tid;
- VG_(tdict).track_post_mem_write( Vg_CoreClientReq, tid, (Addr) targetaddr, len );
- }
- }
- return 0;
-}
-
-/* insert or remove a breakpoint */
-static
-int valgrind_point (Bool insert, char type, CORE_ADDR addr, int len)
-{
- PointKind kind;
- switch (type) {
- case '0': /* implemented by inserting checks at each instruction in sb */
- kind = software_breakpoint;
- break;
- case '1': /* hw breakpoint, same implementation as sw breakpoint */
- kind = hardware_breakpoint;
- break;
- case '2':
- kind = write_watchpoint;
- break;
- case '3':
- kind = read_watchpoint;
- break;
- case '4':
- kind = access_watchpoint;
- break;
- default:
- vg_assert (0);
- }
-
- /* Attention: gdbserver convention differs: 0 means ok; 1 means not ok */
- if (VG_(gdbserver_point) (kind, insert, addr, len))
- return 0;
- else
- return 1; /* error or unsupported */
-}
-
-static
-void valgrind_send_signal (int sig)
-{
- dlog(1, "valgrind_send_signal %d called ????\n", sig);
-}
-
-static
-char* valgrind_target_xml (void)
-{
- return (char *) the_low_target.target_xml;
-}
-
-static
-char* valgrind_shadow_target_xml (void)
-{
- return (char *) the_low_target.shadow_target_xml;
-}
-
-static
-int valgrind_insert_point (char type, CORE_ADDR addr, int len)
-{
- return valgrind_point (/* insert */ True, type, addr, len);
-}
-
-static
-int valgrind_remove_point (char type, CORE_ADDR addr, int len)
-{
- return valgrind_point (/* insert*/ False, type, addr, len);
-}
-
-static CORE_ADDR stopped_data_address = 0;
-void VG_(set_watchpoint_stop_address) (Addr addr)
-{
- stopped_data_address = addr;
-}
-
-static
-int valgrind_stopped_by_watchpoint (void)
-{
- return stopped_data_address != 0;
-}
-
-static
-CORE_ADDR valgrind_stopped_data_address (void)
-{
- return stopped_data_address;
-}
-
-/* pc at which we last stopped */
-static CORE_ADDR stop_pc;
-
-/* pc at which we resume.
- If stop_pc != resume_pc, it means
- gdb/gdbserver has changed the pc so as to have either
- a "continue by jumping at that address"
- or a "continue at that address to call some code from gdb".
-*/
-static CORE_ADDR resume_pc;
-
-static int vki_signal_to_report;
-
-void gdbserver_signal_encountered (Int vki_sigNo)
-{
- vki_signal_to_report = vki_sigNo;
-}
-
-static int vki_signal_to_deliver;
-Bool gdbserver_deliver_signal (Int vki_sigNo)
-{
- return vki_sigNo == vki_signal_to_deliver;
-}
-
-static
-char* sym (Addr addr)
-{
- static char buf[200];
- VG_(describe_IP) (addr, buf, 200);
- return buf;
-}
-
-ThreadId vgdb_interrupted_tid = 0;
-/* called to wait for the process to stop */
-static
-unsigned char valgrind_wait (char *ourstatus)
-{
- int pid;
- unsigned long wptid;
- ThreadState *tst;
- enum target_signal sig;
-
- pid = VG_(getpid) ();
- dlog(1, "enter valgrind_wait pid %d\n", pid);
-
- regcache_invalidate();
- valgrind_update_threads(pid);
-
- /* in valgrind, we consider that a wait always succeeds with STOPPED 'T'
- and with a signal TRAP (i.e. a breakpoint), unless there is
- a signal to report. */
- *ourstatus = 'T';
- if (vki_signal_to_report == 0)
- sig = TARGET_SIGNAL_TRAP;
- else {
- sig = target_signal_from_host(vki_signal_to_report);
- vki_signal_to_report = 0;
- }
-
- if (vgdb_interrupted_tid != 0)
- tst = VG_(get_ThreadState) (vgdb_interrupted_tid);
- else
- tst = VG_(get_ThreadState) (VG_(running_tid));
- wptid = tst->os_state.lwpid;
- /* we can only change the current_inferior when the wptid references
- an existing thread. Otherwise, we are still in the init phase.
- (hack similar to main thread hack in valgrind_update_threads) */
- if (tst->os_state.lwpid)
- current_inferior = gdb_id_to_thread (wptid);
- stop_pc = (*the_low_target.get_pc) ();
-
- dlog(1,
- "exit valgrind_wait returns ptid %s stop_pc %s signal %d\n",
- image_ptid (wptid), sym (stop_pc), sig);
- return sig;
-}
-
-/* 0 => not single stepping.
- 1 => single stepping asked by gdb
- 2 => single stepping asked by valgrind (watchpoint) */
-static int stepping = 0;
-
-/* called when the process is to be resumed */
-static
-void valgrind_resume (struct thread_resume *resume_info)
-{
- dlog(1,
- "resume_info thread %ld leave_stopped %d step %d sig %d stepping %d\n",
- resume_info->thread,
- resume_info->leave_stopped,
- resume_info->step,
- resume_info->sig,
- stepping);
- if (valgrind_stopped_by_watchpoint()) {
- dlog(1, "clearing watchpoint stopped_data_address %p\n",
- C2v(stopped_data_address));
- VG_(set_watchpoint_stop_address) ((Addr) 0);
- }
- vki_signal_to_deliver = resume_info->sig;
-
- stepping = resume_info->step;
- resume_pc = (*the_low_target.get_pc) ();
- if (resume_pc != stop_pc) {
- dlog(1,
- "stop_pc %p changed to be resume_pc %s\n",
- C2v(stop_pc), sym(resume_pc));
- }
- regcache_invalidate();
-}
-
-Addr valgrind_get_ignore_break_once(void)
-{
- if (valgrind_single_stepping())
- return resume_pc;
- else
- return 0;
-}
-
-
-void valgrind_set_single_stepping(Bool set)
-{
- if (set)
- stepping = 2;
- else
- stepping = 0;
-}
-
-Bool valgrind_single_stepping(void)
-{
- if (stepping)
- return True;
- else
- return False;
-}
-
-static struct target_ops valgrind_target_ops = {
- valgrind_thread_alive,
- valgrind_resume,
- valgrind_wait,
- valgrind_fetch_registers,
- valgrind_store_registers,
- valgrind_read_memory,
- valgrind_write_memory,
- valgrind_send_signal,
- valgrind_target_xml,
- valgrind_shadow_target_xml,
- valgrind_insert_point,
- valgrind_remove_point,
- valgrind_stopped_by_watchpoint,
- valgrind_stopped_data_address,
-};
-
-
-/* returns a pointer to the architecture state corresponding to
- the provided register set: 0 => normal guest registers,
- 1 => shadow1
- 2 => shadow2
-*/
-VexGuestArchState* get_arch (int set, ThreadState* tst)
-{
- switch (set) {
- case 0: return &tst->arch.vex;
- case 1: return &tst->arch.vex_shadow1;
- case 2: return &tst->arch.vex_shadow2;
- default: vg_assert(0);
- }
-}
-
-static int non_shadow_num_regs = 0;
-static struct reg *non_shadow_reg_defs = NULL;
-void initialize_shadow_low(Bool shadow_mode)
-{
- if (non_shadow_reg_defs == NULL) {
- non_shadow_reg_defs = the_low_target.reg_defs;
- non_shadow_num_regs = the_low_target.num_regs;
- }
-
- regcache_invalidate();
- if (the_low_target.reg_defs != non_shadow_reg_defs) {
- free (the_low_target.reg_defs);
- }
- if (shadow_mode) {
- the_low_target.num_regs = 3 * non_shadow_num_regs;
- the_low_target.reg_defs = build_shadow_arch (non_shadow_reg_defs, non_shadow_num_regs);
- } else {
- the_low_target.num_regs = non_shadow_num_regs;
- the_low_target.reg_defs = non_shadow_reg_defs;
- }
- set_register_cache (the_low_target.reg_defs, the_low_target.num_regs);
-}
-
-void initialize_low(void)
-{
- set_target_ops (&valgrind_target_ops);
-
-#if defined(VGA_x86)
- x86_init_architecture(&the_low_target);
-#elif defined(VGA_amd64)
- amd64_init_architecture(&the_low_target);
-#elif defined(VGA_arm)
- arm_init_architecture(&the_low_target);
-#elif defined(VGA_ppc32)
- ppc32_init_architecture(&the_low_target);
-#elif defined(VGA_ppc64)
- ppc64_init_architecture(&the_low_target);
-#elif defined(VGA_s390x)
- s390x_init_architecture(&the_low_target);
-#else
- architecture missing in valgrind-low.c
-#endif
-
-}
const char *shadow_target_xml;
};
-
-/* convert from CORE_ADDR to void* */
-extern void* C2v(CORE_ADDR addr);
-
-/* builds an image of bin according to byte order of the architecture
- Useful for register and int image */
-extern char* heximage (char *buf, char *bin, int count);
-
-/* returns a pointer to the architecture state corresponding to
- the provided register set: 0 => normal guest registers,
- 1 => shadow1
- 2 => shadow2 */
-VexGuestArchState* get_arch (int set, ThreadState* tst);
-
extern void x86_init_architecture (struct valgrind_target_ops *target);
extern void amd64_init_architecture (struct valgrind_target_ops *target);
extern void arm_init_architecture (struct valgrind_target_ops *target);