mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
294 lines
8.7 KiB
C++
294 lines
8.7 KiB
C++
/// \file PTCDump.cpp
|
|
/// \brief This file handles dumping PTC to text
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
|
|
#include "revng/Support/Assert.h"
|
|
|
|
#include "PTCDump.h"
|
|
#include "PTCInterface.h"
|
|
|
|
static const int MaxTempNameLength = 128;
|
|
|
|
static void getTemporaryName(char *Buffer,
|
|
size_t BufferSize,
|
|
PTCInstructionList *Instructions,
|
|
unsigned TemporaryId) {
|
|
PTCTemp *Temporary = ptc_temp_get(Instructions, TemporaryId);
|
|
|
|
if (ptc_temp_is_global(Instructions, TemporaryId))
|
|
strncpy(Buffer, Temporary->name, BufferSize);
|
|
else if (Temporary->temp_local)
|
|
snprintf(Buffer,
|
|
BufferSize,
|
|
"loc%u",
|
|
TemporaryId - Instructions->global_temps);
|
|
else
|
|
snprintf(Buffer,
|
|
BufferSize,
|
|
"tmp%u",
|
|
TemporaryId - Instructions->global_temps);
|
|
}
|
|
|
|
int dumpInstruction(std::ostream &Result,
|
|
PTCInstructionList *Instructions,
|
|
unsigned Index) {
|
|
size_t I = 0;
|
|
// TODO: this should stay in Architecture
|
|
int Is64 = 0;
|
|
PTCInstruction &Instruction = Instructions->instructions[Index];
|
|
|
|
PTCOpcode Opcode = Instruction.opc;
|
|
PTCOpcodeDef *Definition = ptc_instruction_opcode_def(&ptc, &Instruction);
|
|
char TemporaryName[MaxTempNameLength + 1] = { '\0' };
|
|
|
|
if (Opcode == PTC_INSTRUCTION_op_debug_insn_start) {
|
|
// TODO: create accessors for PTC_INSTRUCTION_op_debug_insn_start
|
|
uint64_t PC = Instruction.args[0];
|
|
|
|
if (Is64)
|
|
PC |= Instruction.args[1] << 32;
|
|
|
|
Result << " ---- 0x" << std::hex << PC << std::endl;
|
|
} else if (Opcode == PTC_INSTRUCTION_op_call) {
|
|
// TODO: replace PRIx64 with PTC_PRIxARG
|
|
PTCInstructionArg FunctionPointer = 0;
|
|
FunctionPointer = ptc_call_instruction_const_arg(&ptc, &Instruction, 0);
|
|
PTCInstructionArg Flags = ptc_call_instruction_const_arg(&ptc,
|
|
&Instruction,
|
|
1);
|
|
size_t OutArgsCount = ptc_call_instruction_out_arg_count(&ptc,
|
|
&Instruction);
|
|
PTCHelperDef *Helper = ptc_find_helper(&ptc, FunctionPointer);
|
|
const char *HelperName = "unknown_helper";
|
|
|
|
if (Helper != nullptr && Helper->name != nullptr)
|
|
HelperName = Helper->name;
|
|
|
|
// The output format is:
|
|
// call name, flags, out_args_count, out_args [...], in_args [...]
|
|
Result << Definition->name << " " << HelperName << ","
|
|
<< "$0x" << std::hex << Flags << "," << std::dec << OutArgsCount;
|
|
|
|
// Print out arguments
|
|
for (I = 0; I < OutArgsCount; I++) {
|
|
getTemporaryName(TemporaryName,
|
|
MaxTempNameLength,
|
|
Instructions,
|
|
ptc_call_instruction_out_arg(&ptc, &Instruction, I));
|
|
Result << "," << TemporaryName;
|
|
}
|
|
|
|
// Print in arguments
|
|
size_t InArgsCount = ptc_call_instruction_in_arg_count(&ptc, &Instruction);
|
|
for (I = 0; I < InArgsCount; I++) {
|
|
PTCInstructionArg InArg = ptc_call_instruction_in_arg(&ptc,
|
|
&Instruction,
|
|
I);
|
|
|
|
if (InArg != PTC_CALL_DUMMY_ARG) {
|
|
getTemporaryName(TemporaryName, MaxTempNameLength, Instructions, InArg);
|
|
Result << "," << TemporaryName;
|
|
} else {
|
|
Result << ",<dummy>";
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// TODO: fix commas
|
|
Result << Definition->name << " ";
|
|
|
|
// Print out arguments
|
|
for (I = 0; I < ptc_instruction_out_arg_count(&ptc, &Instruction); I++) {
|
|
if (I != 0)
|
|
Result << ",";
|
|
|
|
getTemporaryName(TemporaryName,
|
|
MaxTempNameLength,
|
|
Instructions,
|
|
ptc_instruction_out_arg(&ptc, &Instruction, I));
|
|
Result << TemporaryName;
|
|
}
|
|
|
|
if (I != 0)
|
|
Result << ",";
|
|
|
|
// Print in arguments
|
|
for (I = 0; I < ptc_instruction_in_arg_count(&ptc, &Instruction); I++) {
|
|
if (I != 0)
|
|
Result << ",";
|
|
|
|
getTemporaryName(TemporaryName,
|
|
MaxTempNameLength,
|
|
Instructions,
|
|
ptc_instruction_in_arg(&ptc, &Instruction, I));
|
|
Result << TemporaryName;
|
|
}
|
|
|
|
if (I != 0)
|
|
Result << ",";
|
|
|
|
/* Parse some special const arguments */
|
|
I = 0;
|
|
switch (Opcode) {
|
|
case PTC_INSTRUCTION_op_brcond_i32:
|
|
case PTC_INSTRUCTION_op_setcond_i32:
|
|
case PTC_INSTRUCTION_op_movcond_i32:
|
|
case PTC_INSTRUCTION_op_brcond2_i32:
|
|
case PTC_INSTRUCTION_op_setcond2_i32:
|
|
case PTC_INSTRUCTION_op_brcond_i64:
|
|
case PTC_INSTRUCTION_op_setcond_i64:
|
|
case PTC_INSTRUCTION_op_movcond_i64: {
|
|
PTCInstructionArg Arg = ptc_instruction_const_arg(&ptc, &Instruction, 0);
|
|
PTCCondition ConditionId = static_cast<PTCCondition>(Arg);
|
|
const char *ConditionName = ptc.get_condition_name(ConditionId);
|
|
|
|
if (ConditionName != nullptr)
|
|
Result << "," << ConditionName;
|
|
else
|
|
Result << ","
|
|
<< "$0x" << std::hex << Arg;
|
|
|
|
/* Consume one argument */
|
|
I++;
|
|
|
|
} break;
|
|
case PTC_INSTRUCTION_op_qemu_ld_i32:
|
|
case PTC_INSTRUCTION_op_qemu_st_i32:
|
|
case PTC_INSTRUCTION_op_qemu_ld_i64:
|
|
case PTC_INSTRUCTION_op_qemu_st_i64: {
|
|
PTCInstructionArg Arg = ptc_instruction_const_arg(&ptc, &Instruction, 0);
|
|
PTCLoadStoreArg LoadStoreArg = {};
|
|
LoadStoreArg = ptc.parse_load_store_arg(Arg);
|
|
|
|
if (LoadStoreArg.access_type == PTC_MEMORY_ACCESS_UNKNOWN)
|
|
Result << ","
|
|
<< "$0x" << std::hex << LoadStoreArg.raw_op;
|
|
else {
|
|
const char *Alignment = nullptr;
|
|
const char *LoadStoreName = nullptr;
|
|
LoadStoreName = ptc.get_load_store_name(LoadStoreArg.type);
|
|
|
|
switch (LoadStoreArg.access_type) {
|
|
case PTC_MEMORY_ACCESS_NORMAL:
|
|
Alignment = "";
|
|
break;
|
|
case PTC_MEMORY_ACCESS_UNALIGNED:
|
|
Alignment = "un+";
|
|
break;
|
|
case PTC_MEMORY_ACCESS_ALIGNED:
|
|
Alignment = "al+";
|
|
break;
|
|
default:
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (LoadStoreName == nullptr)
|
|
return EXIT_FAILURE;
|
|
|
|
Result << "," << Alignment << LoadStoreName;
|
|
}
|
|
|
|
Result << "," << LoadStoreArg.mmu_index;
|
|
|
|
/* Consume one argument */
|
|
I++;
|
|
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (Opcode) {
|
|
case PTC_INSTRUCTION_op_set_label:
|
|
case PTC_INSTRUCTION_op_br:
|
|
case PTC_INSTRUCTION_op_brcond_i32:
|
|
case PTC_INSTRUCTION_op_brcond_i64:
|
|
case PTC_INSTRUCTION_op_brcond2_i32: {
|
|
PTCInstructionArg Arg = ptc_instruction_const_arg(&ptc, &Instruction, I);
|
|
Result << ","
|
|
<< "$L" << ptc.get_arg_label_id(Arg);
|
|
|
|
/* Consume one more argument */
|
|
I++;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Print remaining const arguments */
|
|
for (; I < ptc_instruction_const_arg_count(&ptc, &Instruction); I++) {
|
|
if (I != 0) {
|
|
Result << ",";
|
|
}
|
|
|
|
Result << "$0x" << std::hex
|
|
<< ptc_instruction_const_arg(&ptc, &Instruction, I);
|
|
}
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
void disassemble(std::ostream &Result,
|
|
MetaAddress PC,
|
|
uint32_t MaxBytes,
|
|
uint32_t InstructionCount) {
|
|
char *BufferPtr = nullptr;
|
|
size_t BufferLenPtr = 0;
|
|
FILE *MemoryStream = open_memstream(&BufferPtr, &BufferLenPtr);
|
|
|
|
revng_assert(MemoryStream != nullptr);
|
|
|
|
// Using SIZE_MAX is not very nice but the code should disassemble only a
|
|
// single instruction nonetheless.
|
|
ptc.disassemble(MemoryStream, PC.asPC(), MaxBytes, InstructionCount);
|
|
fflush(MemoryStream);
|
|
|
|
revng_assert(BufferPtr != nullptr);
|
|
|
|
Result << BufferPtr;
|
|
|
|
fclose(MemoryStream);
|
|
free(BufferPtr);
|
|
}
|
|
|
|
int dumpTranslation(MetaAddress VirtualAddress,
|
|
std::ostream &Result,
|
|
PTCInstructionList *Instructions) {
|
|
// TODO: this should stay in Architecture
|
|
int Is64 = 0;
|
|
|
|
for (unsigned Index = 0; Index < Instructions->instruction_count; Index++) {
|
|
PTCInstruction &Instruction = Instructions->instructions[Index];
|
|
PTCOpcode Opcode = Instruction.opc;
|
|
|
|
if (Opcode == PTC_INSTRUCTION_op_debug_insn_start) {
|
|
uint64_t PC = Instruction.args[0];
|
|
|
|
if (Is64)
|
|
PC |= Instruction.args[1] << 32;
|
|
|
|
disassemble(Result, VirtualAddress.replaceAddress(PC), 4096, 1);
|
|
}
|
|
|
|
Result << std::dec << Index << ": ";
|
|
|
|
if (dumpInstruction(Result, Instructions, Index) == EXIT_FAILURE)
|
|
return EXIT_FAILURE;
|
|
|
|
Result << std::endl;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|