Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for generating Tenet traces with bochs backend #12

Merged
merged 9 commits into from
Aug 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 175 additions & 5 deletions src/wtf/bochscpu_backend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -286,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.
//
Expand Down Expand Up @@ -391,18 +407,38 @@ __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 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) {
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.
//

bochscpu_cpu_state(Cpu_, &Tenet_.CpuStatePrev_);
Tenet_.PastFirstInstruction_ = true;
}
}

Expand Down Expand Up @@ -434,6 +470,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) {
Tenet_.MemAccesses_.emplace_back(VirtualAddress, Len, MemAccess);
}

//
// If this is not a write access, we don't care to go further.
//
Expand Down Expand Up @@ -1018,3 +1062,129 @@ uint64_t BochscpuBackend_t::SetReg(const Registers_t Reg,
Setter(Cpu_, Value);
return Value;
}

[[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::DumpTenetDelta(const bool Force) {

//
// Dump register deltas.
//

#define __DeltaRegister(Reg, Comma) \
{ \
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);
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);
DeltaRegisterEnd(rip);

#undef DeltaRegisterEnd
#undef DeltaRegister
#undef __DeltaRegister

//
// Dump memory deltas.
//

for (const auto &AccessInfo : Tenet_.MemAccesses_) {

//
// Determine the label to use for this memory access.
//

const char *MemoryType = MemAccessToTenetLabel(AccessInfo.MemAccess);

//
// 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<uint8_t, 64> 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.
//

Tenet_.MemAccesses_.clear();

//
// End of deltas.
//

fmt::print(TraceFile_, "\n");
}
49 changes: 46 additions & 3 deletions src/wtf/bochscpu_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ struct BochscpuRunStats_t {
}
};

//
// A structure to capture information about a single memory access; used for
// Tenet traces.
//

struct BochscpuMemAccess_t {
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)
: VirtualAddress(VirtualAddress), Len(Len), MemAccess(MemAccess) {}
};

class BochscpuBackend_t : public Backend_t {

//
Expand Down Expand Up @@ -90,18 +104,41 @@ class BochscpuBackend_t : public Backend_t {

bochscpu_cpu_t Cpu_ = nullptr;

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.
//

std::vector<BochscpuMemAccess_t> MemAccesses_;
} Tenet_;

//
// 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.
Expand Down Expand Up @@ -139,7 +176,7 @@ class BochscpuBackend_t : public Backend_t {
// Stats of the run.
//

BochscpuRunStats_t RunStats_;
BochscpuRunStats_t RunStats_ = {};

uint64_t Seed_ = 0;

Expand Down Expand Up @@ -273,4 +310,10 @@ class BochscpuBackend_t : public Backend_t {

const uint8_t *GetTestcaseBuffer();
uint64_t GetTestcaseSize();

//
// Dump the register & memory deltas for Tenet.
//

void DumpTenetDelta(const bool Force = false);
};
9 changes: 8 additions & 1 deletion src/wtf/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

};

//
Expand Down
5 changes: 4 additions & 1 deletion src/wtf/wtf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ int main(int argc, const char *argv[]) {
->check(CLI::ExistingDirectory);

const std::unordered_map<std::string, TraceType_t> 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))
Expand Down