From b03ba0370e7115df2233646aef08613dc5531411 Mon Sep 17 00:00:00 2001 From: gaasedelen Date: Thu, 15 Jul 2021 12:39:52 -0400 Subject: [PATCH 1/8] fix build for VS 2019 --- src/wtf/utils.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wtf/utils.cc b/src/wtf/utils.cc index fc60035..510fe5e 100644 --- a/src/wtf/utils.cc +++ b/src/wtf/utils.cc @@ -244,8 +244,12 @@ std::string Blake3HexDigest(const uint8_t *Data, const size_t DataSize) { return Hexdigest; } +inline uint64_t rotr64(uint64_t w, uint64_t c) { + return (w >> c) | (w << (64 - c)); +} + const Gva_t DecodePointer(const uint64_t Cookie, const uint64_t Value) { - return Gva_t(std::rotr(Value, 0x40 - (Cookie & 0x3F)) ^ Cookie); + return Gva_t(rotr64(Value, 0x40 - (Cookie & 0x3F)) ^ Cookie); } std::string u16stringToString(const std::u16string &S) { From 9843de83b7204448534720c322907b19cc5df6d4 Mon Sep 17 00:00:00 2001 From: gaasedelen Date: Wed, 21 Jul 2021 08:24:56 -0400 Subject: [PATCH 2/8] Add experimental support for generating Tenet traces --- src/wtf/bochscpu_backend.cc | 129 ++++++++++++++++++++++++++++++++++-- src/wtf/bochscpu_backend.h | 29 ++++++++ src/wtf/globals.h | 9 ++- src/wtf/wtf.cc | 5 +- 4 files changed, 165 insertions(+), 7 deletions(-) diff --git a/src/wtf/bochscpu_backend.cc b/src/wtf/bochscpu_backend.cc index a885cf2..35500ff 100644 --- a/src/wtf/bochscpu_backend.cc +++ b/src/wtf/bochscpu_backend.cc @@ -274,6 +274,7 @@ BochscpuBackend_t::Run(const uint8_t *Buffer, const uint64_t BufferSize) { TestcaseBuffer_ = Buffer; TestcaseBufferSize_ = BufferSize; LastNewCoverage_.clear(); + bochscpu_cpu_state(Cpu_, &CpuStatePrev_); // // Reset some of the stats. @@ -343,6 +344,14 @@ void BochscpuBackend_t::AfterExecutionHook(/*void *Context, */ uint32_t, RunStats_.NumberInstructionsExecuted++; + // + // Dump register + mem changes if generating Tenet traces. + // + + if (TraceFile_ && TraceType_ == TraceType_t::Tenet) { + DumpDelta(); + } + // // Check the instruction limit. // @@ -389,15 +398,27 @@ __declspec(safebuffers) if (TraceFile_) { const bool RipTrace = TraceType_ == TraceType_t::Rip; const bool UniqueRipTrace = TraceType_ == TraceType_t::UniqueRip; + const bool TenetTrace = TraceType_ == TraceType_t::Tenet; const bool NewRip = Res.second; - // - // On Linux we don't have access to dbgeng so just write the plain address - // for both Windows & Linux. - // - if (RipTrace || (UniqueRipTrace && NewRip)) { + + // + // On Linux we don't have access to dbgeng so just write the plain + // address for both Windows & Linux. + // + fmt::print(TraceFile_, "{:#x}\n", Rip); + + } else if (TenetTrace) { + + // + // Save a complete copy of the Cpu registers so that we can diff + // them against the next step when taking Tenet traces + // + + bochscpu_cpu_state(Cpu_, &CpuStatePrev_); + } } @@ -436,6 +457,14 @@ void BochscpuBackend_t::LinAccessHook(/*void *Context, */ uint32_t, RunStats_.NumberMemoryAccesses += Len; + // + // Log explicit details about the memory access if taking a full-trace + // + + if (TraceFile_ && TraceType_ == TraceType_t::Tenet) { + MemAccesses_.push_back(BochscpuMemAccess_t(VirtualAddress, Len, MemAccess)); + } + // // If this is not a write access, we don't care to go further. // @@ -1016,3 +1045,93 @@ uint64_t BochscpuBackend_t::SetReg(const Registers_t Reg, Setter(Cpu_, Value); return Value; } + +void BochscpuBackend_t::DumpDelta() { + + uint8_t buffer_raw[1024] = {}; + char buffer_printed[2048 + 1] = {}; // +1 for null term + + // + // dump register delta + // + + if (bochscpu_cpu_rax(Cpu_) != CpuStatePrev_.rax) + fmt::print(TraceFile_, "rax={:#x},", bochscpu_cpu_rax(Cpu_)); + if (bochscpu_cpu_rbx(Cpu_) != CpuStatePrev_.rbx) + fmt::print(TraceFile_, "rbx={:#x},", bochscpu_cpu_rbx(Cpu_)); + if (bochscpu_cpu_rcx(Cpu_) != CpuStatePrev_.rcx) + fmt::print(TraceFile_, "rcx={:#x},", bochscpu_cpu_rcx(Cpu_)); + if (bochscpu_cpu_rdx(Cpu_) != CpuStatePrev_.rdx) + fmt::print(TraceFile_, "rdx={:#x},", bochscpu_cpu_rdx(Cpu_)); + if (bochscpu_cpu_rbp(Cpu_) != CpuStatePrev_.rbp) + fmt::print(TraceFile_, "rbp={:#x},", bochscpu_cpu_rbp(Cpu_)); + if (bochscpu_cpu_rsp(Cpu_) != CpuStatePrev_.rsp) + fmt::print(TraceFile_, "rsp={:#x},", bochscpu_cpu_rsp(Cpu_)); + if (bochscpu_cpu_rsi(Cpu_) != CpuStatePrev_.rsi) + fmt::print(TraceFile_, "rsi={:#x},", bochscpu_cpu_rsi(Cpu_)); + if (bochscpu_cpu_rdi(Cpu_) != CpuStatePrev_.rdi) + fmt::print(TraceFile_, "rdi={:#x},", bochscpu_cpu_rdi(Cpu_)); + if (bochscpu_cpu_r8(Cpu_) != CpuStatePrev_.r8) + fmt::print(TraceFile_, "r8={:#x},", bochscpu_cpu_r8(Cpu_)); + if (bochscpu_cpu_r9(Cpu_) != CpuStatePrev_.r9) + fmt::print(TraceFile_, "r9={:#x},", bochscpu_cpu_r9(Cpu_)); + if (bochscpu_cpu_r10(Cpu_) != CpuStatePrev_.r10) + fmt::print(TraceFile_, "r10={:#x},", bochscpu_cpu_r10(Cpu_)); + if (bochscpu_cpu_r11(Cpu_) != CpuStatePrev_.r11) + fmt::print(TraceFile_, "r11={:#x},", bochscpu_cpu_r11(Cpu_)); + if (bochscpu_cpu_r12(Cpu_) != CpuStatePrev_.r12) + fmt::print(TraceFile_, "r12={:#x},", bochscpu_cpu_r12(Cpu_)); + if (bochscpu_cpu_r13(Cpu_) != CpuStatePrev_.r13) + fmt::print(TraceFile_, "r13={:#x},", bochscpu_cpu_r13(Cpu_)); + if (bochscpu_cpu_r14(Cpu_) != CpuStatePrev_.r14) + fmt::print(TraceFile_, "r14={:#x},", bochscpu_cpu_r14(Cpu_)); + if (bochscpu_cpu_r15(Cpu_) != CpuStatePrev_.r15) + fmt::print(TraceFile_, "r15={:#x},", bochscpu_cpu_r15(Cpu_)); + + fmt::print(TraceFile_, "rip={:#x}", bochscpu_cpu_rip(Cpu_)); + + // + // dump memory delta + // + + for (const auto AccessInfo : MemAccesses_) { + const char *type = NULL; + + // determine the label to use for this memory access + switch (AccessInfo.MemAccess) { + case BOCHSCPU_HOOK_MEM_READ: + type = "mr"; + break; + case BOCHSCPU_HOOK_MEM_RW: + type = "mrw"; + break; + case BOCHSCPU_HOOK_MEM_WRITE: + type = "mw"; + break; + } + + // Ignore unknown memory-accesses types for now... + if (type == NULL) + continue; + + // I think this is true for bochs, but just to be sure... + assert(Len < sizeof(buffer_raw)); + + // Fetch the memory that was read or written by the last executed instruction + VirtRead(Gva_t(AccessInfo.VirtualAddress), buffer_raw, AccessInfo.Len); + + // Convert the raw memory bytes to a human-readable hex string + for (int i = 0; i < AccessInfo.Len; i++) { + sprintf_s(&buffer_printed[i * 2], 3, "%02X", buffer_raw[i]); + } + + // Write the formatted memory access to file, eg 'mr=0x140148040:0000000400080040' + fmt::print(TraceFile_, ",{}={:#x}:{}", type, AccessInfo.VirtualAddress, buffer_printed); + } + + // Clear out the saved memory accesses as they are no longer needed + MemAccesses_.clear(); + + // End of delta + fmt::print(TraceFile_, "\n"); +} diff --git a/src/wtf/bochscpu_backend.h b/src/wtf/bochscpu_backend.h index 5c56ab9..df9a917 100644 --- a/src/wtf/bochscpu_backend.h +++ b/src/wtf/bochscpu_backend.h @@ -51,6 +51,20 @@ struct BochscpuBreakpoint_t { : Gva_(Gva), Handler_(Handler) {} }; +// +// A structure to capture information about a single memory access (tracing). +// + +struct BochscpuMemAccess_t { + const uint64_t VirtualAddress; + const uintptr_t Len; + const uint32_t MemAccess; + explicit BochscpuMemAccess_t(const uint64_t VirtualAddress, + const uintptr_t Len, + const uint32_t MemAccess) + : VirtualAddress(VirtualAddress), Len(Len), MemAccess(MemAccess) {} +}; + class BochscpuBackend_t : public Backend_t { // @@ -96,12 +110,24 @@ class BochscpuBackend_t : public Backend_t { std::vector Breakpoints_; + // + // List of memory accesses. + // + + std::vector MemAccesses_; + // // Cpu. // bochscpu_cpu_t Cpu_ = nullptr; + // + // A copy of Cpu registers at t-1 (the previous instruction). + // + + bochscpu_cpu_state_t CpuStatePrev_; + // // The hooks we define onto the Cpu. // @@ -285,4 +311,7 @@ class BochscpuBackend_t : public Backend_t { const uint8_t *GetTestcaseBuffer(); uint64_t GetTestcaseSize(); + + void DumpDelta(); + }; diff --git a/src/wtf/globals.h b/src/wtf/globals.h index c0b94b5..1f7a3f6 100644 --- a/src/wtf/globals.h +++ b/src/wtf/globals.h @@ -1200,7 +1200,14 @@ enum class TraceType_t { // This is a trace of only unique rip locations. // - UniqueRip + UniqueRip, + + // + // This is a Tenet trace of register & mem changes. + // + + Tenet + }; // diff --git a/src/wtf/wtf.cc b/src/wtf/wtf.cc index 8d2d0bc..23b9d2e 100644 --- a/src/wtf/wtf.cc +++ b/src/wtf/wtf.cc @@ -152,7 +152,10 @@ int main(int argc, const char *argv[]) { ->check(CLI::ExistingDirectory); const std::unordered_map TraceTypeMap = { - {"rip", TraceType_t::Rip}, {"cov", TraceType_t::UniqueRip}}; + {"rip", TraceType_t::Rip}, + {"cov", TraceType_t::UniqueRip}, + {"tenet", TraceType_t::Tenet} + }; TraceOpt->add_option("--trace-type", Opts.Run.TraceType, "Trace type") ->transform(CLI::CheckedTransformer(TraceTypeMap, CLI::ignore_case)) From 5738e0d80f0f8c06a7868fc4665bf1ce9006fc10 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 14 Aug 2021 17:11:56 -0700 Subject: [PATCH 3/8] Fix a bunch of stuffs --- src/wtf/bochscpu_backend.cc | 198 ++++++++++++++++++++---------------- src/wtf/bochscpu_backend.h | 6 +- src/wtf/utils.cc | 6 +- 3 files changed, 115 insertions(+), 95 deletions(-) diff --git a/src/wtf/bochscpu_backend.cc b/src/wtf/bochscpu_backend.cc index a5cfd67..cb2e8e7 100644 --- a/src/wtf/bochscpu_backend.cc +++ b/src/wtf/bochscpu_backend.cc @@ -418,12 +418,11 @@ __declspec(safebuffers) } else if (TenetTrace) { // - // Save a complete copy of the Cpu registers so that we can diff - // them against the next step when taking Tenet traces + // Save a complete copy of the registers so that we can diff + // them against the next step when taking Tenet traces. // bochscpu_cpu_state(Cpu_, &CpuStatePrev_); - } } @@ -456,11 +455,11 @@ void BochscpuBackend_t::LinAccessHook(/*void *Context, */ uint32_t, RunStats_.NumberMemoryAccesses += Len; // - // Log explicit details about the memory access if taking a full-trace + // Log explicit details about the memory access if taking a full-trace. // if (TraceFile_ && TraceType_ == TraceType_t::Tenet) { - MemAccesses_.push_back(BochscpuMemAccess_t(VirtualAddress, Len, MemAccess)); + MemAccesses_.emplace_back(VirtualAddress, Len, MemAccess); } // @@ -1050,90 +1049,117 @@ uint64_t BochscpuBackend_t::SetReg(const Registers_t Reg, void BochscpuBackend_t::DumpDelta() { - uint8_t buffer_raw[1024] = {}; - char buffer_printed[2048 + 1] = {}; // +1 for null term - - // - // dump register delta - // - - if (bochscpu_cpu_rax(Cpu_) != CpuStatePrev_.rax) - fmt::print(TraceFile_, "rax={:#x},", bochscpu_cpu_rax(Cpu_)); - if (bochscpu_cpu_rbx(Cpu_) != CpuStatePrev_.rbx) - fmt::print(TraceFile_, "rbx={:#x},", bochscpu_cpu_rbx(Cpu_)); - if (bochscpu_cpu_rcx(Cpu_) != CpuStatePrev_.rcx) - fmt::print(TraceFile_, "rcx={:#x},", bochscpu_cpu_rcx(Cpu_)); - if (bochscpu_cpu_rdx(Cpu_) != CpuStatePrev_.rdx) - fmt::print(TraceFile_, "rdx={:#x},", bochscpu_cpu_rdx(Cpu_)); - if (bochscpu_cpu_rbp(Cpu_) != CpuStatePrev_.rbp) - fmt::print(TraceFile_, "rbp={:#x},", bochscpu_cpu_rbp(Cpu_)); - if (bochscpu_cpu_rsp(Cpu_) != CpuStatePrev_.rsp) - fmt::print(TraceFile_, "rsp={:#x},", bochscpu_cpu_rsp(Cpu_)); - if (bochscpu_cpu_rsi(Cpu_) != CpuStatePrev_.rsi) - fmt::print(TraceFile_, "rsi={:#x},", bochscpu_cpu_rsi(Cpu_)); - if (bochscpu_cpu_rdi(Cpu_) != CpuStatePrev_.rdi) - fmt::print(TraceFile_, "rdi={:#x},", bochscpu_cpu_rdi(Cpu_)); - if (bochscpu_cpu_r8(Cpu_) != CpuStatePrev_.r8) - fmt::print(TraceFile_, "r8={:#x},", bochscpu_cpu_r8(Cpu_)); - if (bochscpu_cpu_r9(Cpu_) != CpuStatePrev_.r9) - fmt::print(TraceFile_, "r9={:#x},", bochscpu_cpu_r9(Cpu_)); - if (bochscpu_cpu_r10(Cpu_) != CpuStatePrev_.r10) - fmt::print(TraceFile_, "r10={:#x},", bochscpu_cpu_r10(Cpu_)); - if (bochscpu_cpu_r11(Cpu_) != CpuStatePrev_.r11) - fmt::print(TraceFile_, "r11={:#x},", bochscpu_cpu_r11(Cpu_)); - if (bochscpu_cpu_r12(Cpu_) != CpuStatePrev_.r12) - fmt::print(TraceFile_, "r12={:#x},", bochscpu_cpu_r12(Cpu_)); - if (bochscpu_cpu_r13(Cpu_) != CpuStatePrev_.r13) - fmt::print(TraceFile_, "r13={:#x},", bochscpu_cpu_r13(Cpu_)); - if (bochscpu_cpu_r14(Cpu_) != CpuStatePrev_.r14) - fmt::print(TraceFile_, "r14={:#x},", bochscpu_cpu_r14(Cpu_)); - if (bochscpu_cpu_r15(Cpu_) != CpuStatePrev_.r15) - fmt::print(TraceFile_, "r15={:#x},", bochscpu_cpu_r15(Cpu_)); - - fmt::print(TraceFile_, "rip={:#x}", bochscpu_cpu_rip(Cpu_)); - - // - // dump memory delta - // - - for (const auto AccessInfo : MemAccesses_) { - const char *type = NULL; - - // determine the label to use for this memory access - switch (AccessInfo.MemAccess) { - case BOCHSCPU_HOOK_MEM_READ: - type = "mr"; - break; - case BOCHSCPU_HOOK_MEM_RW: - type = "mrw"; - break; - case BOCHSCPU_HOOK_MEM_WRITE: - type = "mw"; - break; - } - - // Ignore unknown memory-accesses types for now... - if (type == NULL) - continue; - - // I think this is true for bochs, but just to be sure... - assert(Len < sizeof(buffer_raw)); - - // Fetch the memory that was read or written by the last executed instruction - VirtRead(Gva_t(AccessInfo.VirtualAddress), buffer_raw, AccessInfo.Len); - - // Convert the raw memory bytes to a human-readable hex string - for (int i = 0; i < AccessInfo.Len; i++) { - sprintf_s(&buffer_printed[i * 2], 3, "%02X", buffer_raw[i]); - } - - // Write the formatted memory access to file, eg 'mr=0x140148040:0000000400080040' - fmt::print(TraceFile_, ",{}={:#x}:{}", type, AccessInfo.VirtualAddress, buffer_printed); + // + // Dump register deltas. + // + +#define DeltaRegister(Reg) \ + { \ + if (&CpuStatePrev_.##Reg == &CpuStatePrev_.rip) { \ + fmt::print(TraceFile_, "rip={:#x}", bochscpu_cpu_rip(Cpu_)); \ + } else if (bochscpu_cpu_##Reg(Cpu_) != CpuStatePrev_.##Reg) { \ + fmt::print(TraceFile_, #Reg "={:#x},", bochscpu_cpu_##Reg(Cpu_)); \ + } \ } - // Clear out the saved memory accesses as they are no longer needed + DeltaRegister(rax); + DeltaRegister(rbx); + DeltaRegister(rcx); + DeltaRegister(rdx); + DeltaRegister(rbp); + DeltaRegister(rsp); + DeltaRegister(rsi); + DeltaRegister(rdi); + DeltaRegister(r8); + DeltaRegister(r9); + DeltaRegister(r10); + DeltaRegister(r11); + DeltaRegister(r12); + DeltaRegister(r13); + DeltaRegister(r14); + DeltaRegister(r15); + DeltaRegister(rip); +#undef DeltaRegister + + // + // Dump memory deltas. + // + + for (const auto &AccessInfo : MemAccesses_) { + const char *MemoryType = nullptr; + + // + // Determine the label to use for this memory access. + // + + switch (AccessInfo.MemAccess) { + case BOCHSCPU_HOOK_MEM_READ: { + MemoryType = "mr"; + break; + } + + case BOCHSCPU_HOOK_MEM_RW: { + MemoryType = "mrw"; + break; + } + + case BOCHSCPU_HOOK_MEM_WRITE: { + MemoryType = "mw"; + break; + } + + default: { + fmt::print("Unexpected MemAccess type, aborting\n"); + std::abort(); + } + } + + // + // Fetch the memory that was read or written by the last executed + // instruction. The largest load that can happen today is an AVX512 + // load which is 64 bytes long. + // + + std::array Buffer; + if (AccessInfo.Len > Buffer.size()) { + fmt::print("A memory access was bigger than {} bytes, aborting\n", + AccessInfo.Len); + std::abort(); + } + + if (!VirtRead(AccessInfo.VirtualAddress, Buffer.data(), AccessInfo.Len)) { + fmt::print("VirtRead at {#x} failed, aborting\n", + AccessInfo.VirtualAddress); + std::abort(); + } + + // + // Convert the raw memory bytes to a human-readable hex string. + // + + std::string HexString; + for (size_t Idx = 0; Idx < AccessInfo.Len; Idx++) { + HexString = fmt::format("{}{:02X}", HexString, Buffer[Idx]); + } + + // + // Write the formatted memory access to file, eg + // 'mr=0x140148040:0000000400080040'. + // + + fmt::print(TraceFile_, ",{}={:#x}:{}", MemoryType, + AccessInfo.VirtualAddress, HexString); + } + + // + // Clear out the saved memory accesses as they are no longer needed. + // + MemAccesses_.clear(); - // End of delta + // + // End of deltas. + // + fmt::print(TraceFile_, "\n"); } diff --git a/src/wtf/bochscpu_backend.h b/src/wtf/bochscpu_backend.h index 078fa59..ac0e3ef 100644 --- a/src/wtf/bochscpu_backend.h +++ b/src/wtf/bochscpu_backend.h @@ -44,12 +44,11 @@ struct BochscpuRunStats_t { // struct BochscpuMemAccess_t { - const uint64_t VirtualAddress; + const Gva_t VirtualAddress; const uintptr_t Len; const uint32_t MemAccess; explicit BochscpuMemAccess_t(const uint64_t VirtualAddress, - const uintptr_t Len, - const uint32_t MemAccess) + const uintptr_t Len, const uint32_t MemAccess) : VirtualAddress(VirtualAddress), Len(Len), MemAccess(MemAccess) {} }; @@ -301,5 +300,4 @@ class BochscpuBackend_t : public Backend_t { uint64_t GetTestcaseSize(); void DumpDelta(); - }; diff --git a/src/wtf/utils.cc b/src/wtf/utils.cc index 510fe5e..fc60035 100644 --- a/src/wtf/utils.cc +++ b/src/wtf/utils.cc @@ -244,12 +244,8 @@ std::string Blake3HexDigest(const uint8_t *Data, const size_t DataSize) { return Hexdigest; } -inline uint64_t rotr64(uint64_t w, uint64_t c) { - return (w >> c) | (w << (64 - c)); -} - const Gva_t DecodePointer(const uint64_t Cookie, const uint64_t Value) { - return Gva_t(rotr64(Value, 0x40 - (Cookie & 0x3F)) ^ Cookie); + return Gva_t(std::rotr(Value, 0x40 - (Cookie & 0x3F)) ^ Cookie); } std::string u16stringToString(const std::u16string &S) { From 5aa702060d3b3883493cec1979e75191593c17e8 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 14 Aug 2021 17:26:26 -0700 Subject: [PATCH 4/8] fix preprocessor --- src/wtf/bochscpu_backend.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wtf/bochscpu_backend.cc b/src/wtf/bochscpu_backend.cc index cb2e8e7..dc3f005 100644 --- a/src/wtf/bochscpu_backend.cc +++ b/src/wtf/bochscpu_backend.cc @@ -1055,9 +1055,9 @@ void BochscpuBackend_t::DumpDelta() { #define DeltaRegister(Reg) \ { \ - if (&CpuStatePrev_.##Reg == &CpuStatePrev_.rip) { \ + if (&CpuStatePrev_.Reg == &CpuStatePrev_.rip) { \ fmt::print(TraceFile_, "rip={:#x}", bochscpu_cpu_rip(Cpu_)); \ - } else if (bochscpu_cpu_##Reg(Cpu_) != CpuStatePrev_.##Reg) { \ + } else if (bochscpu_cpu_##Reg(Cpu_) != CpuStatePrev_.Reg) { \ fmt::print(TraceFile_, #Reg "={:#x},", bochscpu_cpu_##Reg(Cpu_)); \ } \ } From 0b05cc231c18dc1bfa255bf24cb273fab4d7a6ba Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 14 Aug 2021 18:14:40 -0700 Subject: [PATCH 5/8] fix flag --- src/wtf/bochscpu_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wtf/bochscpu_backend.cc b/src/wtf/bochscpu_backend.cc index dc3f005..8ae1a7d 100644 --- a/src/wtf/bochscpu_backend.cc +++ b/src/wtf/bochscpu_backend.cc @@ -1128,7 +1128,7 @@ void BochscpuBackend_t::DumpDelta() { } if (!VirtRead(AccessInfo.VirtualAddress, Buffer.data(), AccessInfo.Len)) { - fmt::print("VirtRead at {#x} failed, aborting\n", + fmt::print("VirtRead at {:#x} failed, aborting\n", AccessInfo.VirtualAddress); std::abort(); } From 43035f78b34984dffcd1af412e622becca1dfdc2 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 14 Aug 2021 18:24:38 -0700 Subject: [PATCH 6/8] extra comments --- src/wtf/bochscpu_backend.cc | 50 +++++++++++++++++++------------------ src/wtf/bochscpu_backend.h | 22 ++++++++++------ 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/wtf/bochscpu_backend.cc b/src/wtf/bochscpu_backend.cc index 8ae1a7d..649dcfd 100644 --- a/src/wtf/bochscpu_backend.cc +++ b/src/wtf/bochscpu_backend.cc @@ -354,7 +354,7 @@ void BochscpuBackend_t::AfterExecutionHook(/*void *Context, */ uint32_t, // if (TraceFile_ && TraceType_ == TraceType_t::Tenet) { - DumpDelta(); + TenetDumpDelta(); } // @@ -1047,7 +1047,30 @@ uint64_t BochscpuBackend_t::SetReg(const Registers_t Reg, return Value; } -void BochscpuBackend_t::DumpDelta() { +[[nodiscard]] constexpr const char * +MemAccessToTenetLabel(const uint32_t MemAccess) { + switch (MemAccess) { + case BOCHSCPU_HOOK_MEM_READ: { + return "mr"; + } + + case BOCHSCPU_HOOK_MEM_RW: { + return "mrw"; + } + + case BOCHSCPU_HOOK_MEM_WRITE: { + return "mw"; + break; + } + + default: { + fmt::print("Unexpected MemAccess type, aborting\n"); + std::abort(); + } + } +} + +void BochscpuBackend_t::TenetDumpDelta() { // // Dump register deltas. @@ -1086,33 +1109,12 @@ void BochscpuBackend_t::DumpDelta() { // for (const auto &AccessInfo : MemAccesses_) { - const char *MemoryType = nullptr; // // Determine the label to use for this memory access. // - switch (AccessInfo.MemAccess) { - case BOCHSCPU_HOOK_MEM_READ: { - MemoryType = "mr"; - break; - } - - case BOCHSCPU_HOOK_MEM_RW: { - MemoryType = "mrw"; - break; - } - - case BOCHSCPU_HOOK_MEM_WRITE: { - MemoryType = "mw"; - break; - } - - default: { - fmt::print("Unexpected MemAccess type, aborting\n"); - std::abort(); - } - } + const char *MemoryType = MemAccessToTenetLabel(AccessInfo.MemAccess); // // Fetch the memory that was read or written by the last executed diff --git a/src/wtf/bochscpu_backend.h b/src/wtf/bochscpu_backend.h index ac0e3ef..8e0bd3b 100644 --- a/src/wtf/bochscpu_backend.h +++ b/src/wtf/bochscpu_backend.h @@ -40,7 +40,8 @@ struct BochscpuRunStats_t { }; // -// A structure to capture information about a single memory access (tracing). +// A structure to capture information about a single memory access; used for +// Tenet traces. // struct BochscpuMemAccess_t { @@ -98,7 +99,7 @@ class BochscpuBackend_t : public Backend_t { tsl::robin_map Breakpoints_; // - // List of memory accesses. + // List of memory accesses; used for Tenet traces. // std::vector MemAccesses_; @@ -110,23 +111,24 @@ class BochscpuBackend_t : public Backend_t { bochscpu_cpu_t Cpu_ = nullptr; // - // A copy of Cpu registers at t-1 (the previous instruction). + // A copy of Cpu registers at t-1 (the previous instruction); used for Tenet + // traces. // - bochscpu_cpu_state_t CpuStatePrev_; + bochscpu_cpu_state_t CpuStatePrev_ = {}; // // The hooks we define onto the Cpu. // - bochscpu_hooks_t Hooks_; + bochscpu_hooks_t Hooks_ = {}; // // The chain of hooks. We only use a set of hooks, so we need // only two entries (it has to end with a nullptr entry). // - bochscpu_hooks_t *HookChain_[2]; + bochscpu_hooks_t *HookChain_[2] = {}; // // Instruction limit. @@ -164,7 +166,7 @@ class BochscpuBackend_t : public Backend_t { // Stats of the run. // - BochscpuRunStats_t RunStats_; + BochscpuRunStats_t RunStats_ = {}; uint64_t Seed_ = 0; @@ -299,5 +301,9 @@ class BochscpuBackend_t : public Backend_t { const uint8_t *GetTestcaseBuffer(); uint64_t GetTestcaseSize(); - void DumpDelta(); + // + // Dump the register & memory deltas for Tenet traces. + // + + void TenetDumpDelta(); }; From 4c821a192ebfc2600f87857fee5dba724c55fbe8 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 14 Aug 2021 20:12:23 -0700 Subject: [PATCH 7/8] fix bp bug --- src/wtf/bochscpu_backend.cc | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/wtf/bochscpu_backend.cc b/src/wtf/bochscpu_backend.cc index 649dcfd..bded71f 100644 --- a/src/wtf/bochscpu_backend.cc +++ b/src/wtf/bochscpu_backend.cc @@ -400,10 +400,10 @@ __declspec(safebuffers) LastNewCoverage_.emplace(Rip); } + const bool TenetTrace = TraceType_ == TraceType_t::Tenet; if (TraceFile_) { const bool RipTrace = TraceType_ == TraceType_t::Rip; const bool UniqueRipTrace = TraceType_ == TraceType_t::UniqueRip; - const bool TenetTrace = TraceType_ == TraceType_t::Tenet; const bool NewRip = Res.second; if (RipTrace || (UniqueRipTrace && NewRip)) { @@ -414,7 +414,6 @@ __declspec(safebuffers) // fmt::print(TraceFile_, "{:#x}\n", Rip); - } else if (TenetTrace) { // @@ -432,6 +431,20 @@ __declspec(safebuffers) if (Breakpoints_.contains(Rip)) { Breakpoints_.at(Rip)(this); + const Gva_t RipAfter = Gva_t(bochscpu_cpu_rip(Cpu_)); + if (TenetTrace && Rip != RipAfter) { + + // + // If we executed a breakpoint, we need to serialize the changes it did if + // it has changed rip. Why? If it has changed @rip, bochs returns from + // this callback noticing that @rip has changed and will not invoke the + // AfterExecution callback which is where the deltas are serialized. This + // means we miss every instruction / state changes happening right after a + // breakpoint that changes @rip. + // + + TenetDumpDelta(); + } } } From 493770e04b8609af37c1dca08178dcce0bccc1c6 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Sat, 14 Aug 2021 21:27:58 -0700 Subject: [PATCH 8/8] fixing all the bugs? --- src/wtf/bochscpu_backend.cc | 82 +++++++++++++++++++++---------------- src/wtf/bochscpu_backend.h | 36 ++++++++++------ 2 files changed, 69 insertions(+), 49 deletions(-) diff --git a/src/wtf/bochscpu_backend.cc b/src/wtf/bochscpu_backend.cc index bded71f..d3347e8 100644 --- a/src/wtf/bochscpu_backend.cc +++ b/src/wtf/bochscpu_backend.cc @@ -279,7 +279,6 @@ BochscpuBackend_t::Run(const uint8_t *Buffer, const uint64_t BufferSize) { TestcaseBuffer_ = Buffer; TestcaseBufferSize_ = BufferSize; LastNewCoverage_.clear(); - bochscpu_cpu_state(Cpu_, &CpuStatePrev_); // // Reset some of the stats. @@ -287,12 +286,28 @@ BochscpuBackend_t::Run(const uint8_t *Buffer, const uint64_t BufferSize) { RunStats_.Reset(); + // + // Force dumping all the registers if this is a Tenet trace. + // + + if (TraceType_ == TraceType_t::Tenet) { + DumpTenetDelta(true); + } + // // Lift off. // bochscpu_cpu_run(Cpu_, HookChain_); + // + // Dump the last delta for Tenet traces. + // + + if (TraceType_ == TraceType_t::Tenet) { + DumpTenetDelta(); + } + // // Fill in the stats. // @@ -349,14 +364,6 @@ void BochscpuBackend_t::AfterExecutionHook(/*void *Context, */ uint32_t, RunStats_.NumberInstructionsExecuted++; - // - // Dump register + mem changes if generating Tenet traces. - // - - if (TraceFile_ && TraceType_ == TraceType_t::Tenet) { - TenetDumpDelta(); - } - // // Check the instruction limit. // @@ -415,13 +422,23 @@ __declspec(safebuffers) fmt::print(TraceFile_, "{:#x}\n", Rip); } else if (TenetTrace) { + if (Tenet_.PastFirstInstruction_) { + + // + // If we already executed an instruction, dump register + mem changes if + // generating Tenet traces. + // + + DumpTenetDelta(); + } // - // Save a complete copy of the registers so that we can diff - // them against the next step when taking Tenet traces. + // Save a complete copy of the registers so that we can diff them against + // the next step when taking Tenet traces. // - bochscpu_cpu_state(Cpu_, &CpuStatePrev_); + bochscpu_cpu_state(Cpu_, &Tenet_.CpuStatePrev_); + Tenet_.PastFirstInstruction_ = true; } } @@ -431,20 +448,6 @@ __declspec(safebuffers) if (Breakpoints_.contains(Rip)) { Breakpoints_.at(Rip)(this); - const Gva_t RipAfter = Gva_t(bochscpu_cpu_rip(Cpu_)); - if (TenetTrace && Rip != RipAfter) { - - // - // If we executed a breakpoint, we need to serialize the changes it did if - // it has changed rip. Why? If it has changed @rip, bochs returns from - // this callback noticing that @rip has changed and will not invoke the - // AfterExecution callback which is where the deltas are serialized. This - // means we miss every instruction / state changes happening right after a - // breakpoint that changes @rip. - // - - TenetDumpDelta(); - } } } @@ -472,7 +475,7 @@ void BochscpuBackend_t::LinAccessHook(/*void *Context, */ uint32_t, // if (TraceFile_ && TraceType_ == TraceType_t::Tenet) { - MemAccesses_.emplace_back(VirtualAddress, Len, MemAccess); + Tenet_.MemAccesses_.emplace_back(VirtualAddress, Len, MemAccess); } // @@ -1083,21 +1086,25 @@ MemAccessToTenetLabel(const uint32_t MemAccess) { } } -void BochscpuBackend_t::TenetDumpDelta() { +void BochscpuBackend_t::DumpTenetDelta(const bool Force) { // // Dump register deltas. // -#define DeltaRegister(Reg) \ +#define __DeltaRegister(Reg, Comma) \ { \ - if (&CpuStatePrev_.Reg == &CpuStatePrev_.rip) { \ - fmt::print(TraceFile_, "rip={:#x}", bochscpu_cpu_rip(Cpu_)); \ - } else if (bochscpu_cpu_##Reg(Cpu_) != CpuStatePrev_.Reg) { \ - fmt::print(TraceFile_, #Reg "={:#x},", bochscpu_cpu_##Reg(Cpu_)); \ + if (bochscpu_cpu_##Reg(Cpu_) != Tenet_.CpuStatePrev_.Reg || Force) { \ + fmt::print(TraceFile_, #Reg "={:#x}", bochscpu_cpu_##Reg(Cpu_)); \ + if (Comma) { \ + fmt::print(TraceFile_, ","); \ + } \ } \ } +#define DeltaRegister(Reg) __DeltaRegister(Reg, true) +#define DeltaRegisterEnd(Reg) __DeltaRegister(Reg, false) + DeltaRegister(rax); DeltaRegister(rbx); DeltaRegister(rcx); @@ -1114,14 +1121,17 @@ void BochscpuBackend_t::TenetDumpDelta() { DeltaRegister(r13); DeltaRegister(r14); DeltaRegister(r15); - DeltaRegister(rip); + DeltaRegisterEnd(rip); + +#undef DeltaRegisterEnd #undef DeltaRegister +#undef __DeltaRegister // // Dump memory deltas. // - for (const auto &AccessInfo : MemAccesses_) { + for (const auto &AccessInfo : Tenet_.MemAccesses_) { // // Determine the label to use for this memory access. @@ -1170,7 +1180,7 @@ void BochscpuBackend_t::TenetDumpDelta() { // Clear out the saved memory accesses as they are no longer needed. // - MemAccesses_.clear(); + Tenet_.MemAccesses_.clear(); // // End of deltas. diff --git a/src/wtf/bochscpu_backend.h b/src/wtf/bochscpu_backend.h index 8e0bd3b..06a363d 100644 --- a/src/wtf/bochscpu_backend.h +++ b/src/wtf/bochscpu_backend.h @@ -98,24 +98,34 @@ class BochscpuBackend_t : public Backend_t { tsl::robin_map Breakpoints_; - // - // List of memory accesses; used for Tenet traces. - // - - std::vector MemAccesses_; - // // Cpu. // bochscpu_cpu_t Cpu_ = nullptr; - // - // A copy of Cpu registers at t-1 (the previous instruction); used for Tenet - // traces. - // + struct Tenet_t { + + // + // A copy of Cpu registers at t-1 (the previous instruction); used for Tenet + // traces. + // + + bochscpu_cpu_state_t CpuStatePrev_ = {}; + + // + // Boolean that tracks if the execution is past the first execution; used + // for Tenet traces. + // + + bool PastFirstInstruction_ = false; + + // + // List of memory accesses; used for Tenet traces. + // - bochscpu_cpu_state_t CpuStatePrev_ = {}; + std::vector MemAccesses_; + } Tenet_; // // The hooks we define onto the Cpu. @@ -302,8 +312,8 @@ class BochscpuBackend_t : public Backend_t { uint64_t GetTestcaseSize(); // - // Dump the register & memory deltas for Tenet traces. + // Dump the register & memory deltas for Tenet. // - void TenetDumpDelta(); + void DumpTenetDelta(const bool Force = false); };