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.si_signo = resume_info->sig; /* signal was reported to GDB, GDB told us to resume execution. So, reset the signal to report to 0. */ VG_(memset) (&vki_signal_to_report, 0, sizeof(vki_signal_to_report)); 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(); }
Bool VG_(gdbserver_point) (PointKind kind, Bool insert, CORE_ADDR addr, int len) { Bool res; GS_Watch *g; Word g_ix; Bool is_code = kind == software_breakpoint || kind == hardware_breakpoint; dlog(1, "%s %s at addr %p %s\n", (insert ? "insert" : "remove"), VG_(ppPointKind) (kind), C2v(addr), sym(addr, is_code)); if (is_code) { breakpoint (insert, addr); return True; } vg_assert (kind == access_watchpoint || kind == read_watchpoint || kind == write_watchpoint); if (tool_watchpoint == NULL) return False; res = (*tool_watchpoint) (kind, insert, addr, len); if (!res) return False; /* error or unsupported */ // Protocol says insert/remove must be idempotent. // So, we just ignore double insert or (supposed) double delete. g = lookup_gs_watch (addr, len, kind, &g_ix); if (insert) { if (g == NULL) { g = VG_(arena_malloc)(VG_AR_CORE, "gdbserver_point watchpoint", sizeof(GS_Watch)); g->addr = addr; g->len = len; g->kind = kind; VG_(addToXA)(gs_watches, &g); } else { dlog(1, "VG_(gdbserver_point) addr %p len %d kind %s already inserted\n", C2v(addr), len, VG_(ppPointKind) (kind)); } } else { if (g != NULL) { VG_(removeIndexXA) (gs_watches, g_ix); VG_(arena_free) (VG_AR_CORE, g); } else { dlog(1, "VG_(gdbserver_point) addr %p len %d kind %s already deleted?\n", C2v(addr), len, VG_(ppPointKind) (kind)); } } return True; }
static void set_pc ( CORE_ADDR newpc ) { Bool mod; supply_register_by_name ("pc", &newpc, &mod); if (mod) dlog(1, "set pc to %p\n", C2v (newpc)); else dlog(1, "set pc not changed %p\n", C2v (newpc)); }
/* protocol spec tells the below must be idempotent. */ static void breakpoint (Bool insert, CORE_ADDR addr) { GS_Address *g; g = VG_(HT_lookup) (gs_addresses, (UWord)HT_addr(addr)); if (insert) { /* insert a breakpoint at addr or upgrade its kind */ if (g == NULL) { add_gs_address (addr, GS_break, "m_gdbserver breakpoint insert"); } else { /* already gdbserved. Normally, it must be because of a jump. However, due to idempotent or if connection with gdb was lost (kept breaks from the previous gdb), if already existing, we just upgrade its kind. */ g->kind = GS_break; } } else { /* delete a breakpoint at addr or downgrade its kind */ if (g != NULL && g->kind == GS_break) { if (valgrind_single_stepping()) { /* keep gdbserved instrumentation while single stepping */ g->kind = GS_jump; } else { remove_gs_address (g, "m_gdbserver breakpoint remove"); } } else { dlog (1, "remove break addr %p %s\n", C2v(addr), (g == NULL ? "NULL" : (g->kind == GS_jump ? "GS_jump" : "GS_break"))); } } }
int valgrind_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) { Bool is_valid_client_memory; void *targetaddr = C2v (memaddr); dlog(2, "writing memory %p size %d\n", targetaddr, len); is_valid_client_memory = VG_(am_is_valid_for_client) ((Addr)targetaddr, len, VKI_PROT_WRITE); if (is_valid_client_memory || (hostvisibility && VG_(am_is_valid_for_valgrind) ((Addr) targetaddr, len, VKI_PROT_READ))) { if (len > 0) { VG_(memcpy) (targetaddr, myaddr, len); if (is_valid_client_memory && 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; } else { dlog(1, "error writing memory %p size %d\n", targetaddr, len); return -1; } }
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(); }
Bool VG_(gdbserver_point) (PointKind kind, Bool insert, CORE_ADDR addr, int len) { Bool res; GS_Watch *g; Bool is_code = kind == software_breakpoint || kind == hardware_breakpoint; dlog(1, "%s %s at addr %p %s\n", (insert ? "insert" : "remove"), VG_(ppPointKind) (kind), C2v(addr), sym(addr, is_code)); if (is_code) { breakpoint (insert, addr); return True; } vg_assert (kind == access_watchpoint || kind == read_watchpoint || kind == write_watchpoint); if (tool_watchpoint == NULL) return False; res = (*tool_watchpoint) (kind, insert, addr, len); if (!res) return False; /* error or unsupported */ g = VG_(HT_lookup) (gs_watches, (UWord)addr); if (insert) { if (g == NULL) { g = VG_(arena_malloc)(VG_AR_CORE, "gdbserver_point watchpoint", sizeof(GS_Watch)); g->addr = addr; g->len = len; g->kind = kind; VG_(HT_add_node)(gs_watches, g); } else { g->kind = kind; } } else { vg_assert (g != NULL); VG_(HT_remove) (gs_watches, g->addr); VG_(arena_free) (VG_AR_CORE, g); } return True; }
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) ((Addr) sourceaddr, len, VKI_PROT_READ) || (hostvisibility && VG_(am_is_valid_for_valgrind) ((Addr) sourceaddr, len, VKI_PROT_READ))) { VG_(memcpy) (myaddr, sourceaddr, len); return 0; } else { dlog(1, "error reading memory %p size %d\n", sourceaddr, len); return -1; } }
/* Returns the reason for which gdbserver instrumentation is needed */ static VgVgdb VG_(gdbserver_instrumentation_needed) (VexGuestExtents* vge) { GS_Address* g; int e; if (!gdbserver_called) return Vg_VgdbNo; if (valgrind_single_stepping()) { dlog(2, "gdbserver_instrumentation_needed due to single stepping\n"); return Vg_VgdbYes; } if (VG_(clo_vgdb) == Vg_VgdbYes && VG_(HT_count_nodes) (gs_addresses) == 0) return Vg_VgdbNo; /* We assume we do not have a huge nr of breakpoints. Otherwise, we need something more efficient e.g. a sorted list of breakpoints or associate extents to it or ... */ VG_(HT_ResetIter) (gs_addresses); while ((g = VG_(HT_Next) (gs_addresses))) { for (e = 0; e < vge->n_used; e++) { if (g->addr >= HT_addr(vge->base[e]) && g->addr < HT_addr(vge->base[e]) + vge->len[e]) { dlog(2, "gdbserver_instrumentation_needed %p %s reason %s\n", C2v(g->addr), sym(g->addr, /* is_code */ True), (g->kind == GS_jump ? "GS_jump" : "GS_break")); return Vg_VgdbYes; } } } if (VG_(clo_vgdb) == Vg_VgdbFull) { dlog(4, "gdbserver_instrumentation_needed" " due to VG_(clo_vgdb) == Vg_VgdbFull\n"); return Vg_VgdbFull; } return Vg_VgdbNo; }
Addr thumb_pc (Addr pc) { if (pc & 1) { dlog (1, "%p = thumb (bit0 is set)\n", C2v (pc)); return pc; } if (pc & 2) { dlog (1, "bit0 not set, bit1 set => %p = thumb\n", C2v (pc)); return pc | 1; } { Char fnname[200]; Addr entrypoint; Addr ptoc; if (VG_(get_fnname_raw) (pc | 1, fnname, 200)) { if (VG_(lookup_symbol_SLOW)( "*", fnname, &entrypoint, &ptoc )) { dlog (1, "fnname %s lookupsym %p => %p %s.\n", fnname, C2v(entrypoint), C2v(pc), (entrypoint & 1 ? "thumb" : "arm")); if (entrypoint & 1) return pc | 1; else return pc; } else { dlog (1, "%p fnname %s lookupsym failed?. Assume arm\n", C2v (pc), fnname); return pc; } } else { dlog (1, "%p unknown fnname?. Assume arm\n", C2v (pc)); return pc; } } }
Addr thumb_pc (Addr pc) { // If the thumb bit (bit 0) is already set, we trust it. if (pc & 1) { dlog (1, "%p = thumb (bit0 is set)\n", C2v (pc)); return pc; } // Here, bit 0 is not set. // For a pc aligned on 4 bytes, we have to use the debug // info to determine the thumb-ness. // else (aligned on 2 bytes), we trust this is a thumb // address and we set the thumb bit. if (pc & 2) { dlog (1, "bit0 not set, bit1 set => %p = thumb\n", C2v (pc)); return pc | 1; } // pc aligned on 4 bytes. We need to use debug info. { HChar fnname[200]; // ??? max size SymAVMAs avmas; // If this is a thumb instruction, we need to ask // the debug info with the bit0 set // (why can't debug info do that for us ???) // (why if this is a 4 bytes thumb instruction ???) if (VG_(get_fnname_raw) (pc | 1, fnname, 200)) { if (VG_(lookup_symbol_SLOW)( "*", fnname, &avmas )) { dlog (1, "fnname %s lookupsym %p => %p %s.\n", fnname, C2v(avmas.main), C2v(pc), (avmas.main & 1 ? "thumb" : "arm")); if (avmas.main & 1) return pc | 1; else return pc; } else { dlog (1, "%p fnname %s lookupsym failed?. Assume arm\n", C2v (pc), fnname); return pc; } } else { // Can't find function name. We assume this is arm dlog (1, "%p unknown fnname?. Assume arm\n", C2v (pc)); return pc; } } }
Bool VG_(is_watched)(PointKind kind, Addr addr, Int szB) { Word n_elems; GS_Watch* g; Word i; Bool watched = False; const ThreadId tid = VG_(running_tid); if (!gdbserver_called) return False; n_elems = VG_(sizeXA) (gs_watches); Addr to = addr + szB; // semi-open interval [addr, to[ vg_assert (kind == access_watchpoint || kind == read_watchpoint || kind == write_watchpoint); dlog(1, "tid %d VG_(is_watched) %s addr %p szB %d\n", tid, VG_(ppPointKind) (kind), C2v(addr), szB); for (i = 0; i < n_elems; i++) { g = index_gs_watches(i); switch (g->kind) { case software_breakpoint: case hardware_breakpoint: break; case access_watchpoint: case read_watchpoint: case write_watchpoint: if (to <= g->addr || addr >= (g->addr + g->len)) /* If no overlap, examine next watchpoint: */ continue; watched = True; /* We have an overlap */ /* call gdbserver if access kind reported by the tool matches the watchpoint kind. */ if (kind == access_watchpoint || g->kind == access_watchpoint || g->kind == kind) { /* Watchpoint encountered. If this is a read watchpoint, we directly call gdbserver to report it to gdb. Otherwise, for a write watchpoint, we have to finish the instruction so as to modify the value. If we do not finish the instruction, then gdb sees no value change and continues. For a read watchpoint, we better call gdbserver directly: in case the current block is not gdbserved, Valgrind will execute instructions till the next block. */ /* set the watchpoint stop address to the first read or written. */ if (g->addr <= addr) { VG_(set_watchpoint_stop_address) (addr); } else { VG_(set_watchpoint_stop_address) (g->addr); } if (kind == write_watchpoint) { /* Let Valgrind stop as early as possible after this instruction by switching to Single Stepping mode. */ valgrind_set_single_stepping (True); invalidate_current_ip (tid, "m_gdbserver write watchpoint"); } else { call_gdbserver (tid, watch_reason); VG_(set_watchpoint_stop_address) ((Addr) 0); } return True; // we are watched here. } break; default: vg_assert (0); } } return watched; }