/// \file PTCDump.cpp /// This file handles dumping PTC to text // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include #include #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 << ","; } } } 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(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; }