clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
519 lines
20 KiB
C++
519 lines
20 KiB
C++
//===-- EmulateInstruction.h ------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLDB_CORE_EMULATEINSTRUCTION_H
|
|
#define LLDB_CORE_EMULATEINSTRUCTION_H
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
|
|
#include "lldb/Core/Address.h"
|
|
#include "lldb/Core/Opcode.h"
|
|
#include "lldb/Core/PluginInterface.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "lldb/lldb-defines.h"
|
|
#include "lldb/lldb-enumerations.h"
|
|
#include "lldb/lldb-private-enumerations.h"
|
|
#include "lldb/lldb-private-types.h"
|
|
#include "lldb/lldb-types.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
namespace lldb_private {
|
|
class OptionValueDictionary;
|
|
class RegisterContext;
|
|
class RegisterValue;
|
|
class Stream;
|
|
class Target;
|
|
class UnwindPlan;
|
|
|
|
/// \class EmulateInstruction EmulateInstruction.h
|
|
/// "lldb/Core/EmulateInstruction.h"
|
|
/// A class that allows emulation of CPU opcodes.
|
|
///
|
|
/// This class is a plug-in interface that is accessed through the standard
|
|
/// static FindPlugin function call in the EmulateInstruction class. The
|
|
/// FindPlugin takes a target triple and returns a new object if there is a
|
|
/// plug-in that supports the architecture and OS. Four callbacks and a baton
|
|
/// are provided. The four callbacks are read register, write register, read
|
|
/// memory and write memory.
|
|
///
|
|
/// This class is currently designed for these main use cases: - Auto
|
|
/// generation of Call Frame Information (CFI) from assembly code - Predicting
|
|
/// single step breakpoint locations - Emulating instructions for breakpoint
|
|
/// traps
|
|
///
|
|
/// Objects can be asked to read an instruction which will cause a call to the
|
|
/// read register callback to get the PC, followed by a read memory call to
|
|
/// read the opcode. If ReadInstruction () returns true, then a call to
|
|
/// EmulateInstruction::EvaluateInstruction () can be made. At this point the
|
|
/// EmulateInstruction subclass will use all of the callbacks to emulate an
|
|
/// instruction.
|
|
///
|
|
/// Clients that provide the callbacks can either do the read/write
|
|
/// registers/memory to actually emulate the instruction on a real or virtual
|
|
/// CPU, or watch for the EmulateInstruction::Context which is context for the
|
|
/// read/write register/memory which explains why the callback is being
|
|
/// called. Examples of a context are: "pushing register 3 onto the stack at
|
|
/// offset -12", or "adjusting stack pointer by -16". This extra context
|
|
/// allows the generation of
|
|
/// CFI information from assembly code without having to actually do
|
|
/// the read/write register/memory.
|
|
///
|
|
/// Clients must be prepared that not all instructions for an Instruction Set
|
|
/// Architecture (ISA) will be emulated.
|
|
///
|
|
/// Subclasses at the very least should implement the instructions that save
|
|
/// and restore registers onto the stack and adjustment to the stack pointer.
|
|
/// By just implementing a few instructions for an ISA that are the typical
|
|
/// prologue opcodes, you can then generate CFI using a class that will soon
|
|
/// be available.
|
|
///
|
|
/// Implementing all of the instructions that affect the PC can then allow
|
|
/// single step prediction support.
|
|
///
|
|
/// Implementing all of the instructions allows for emulation of opcodes for
|
|
/// breakpoint traps and will pave the way for "thread centric" debugging. The
|
|
/// current debugging model is "process centric" where all threads must be
|
|
/// stopped when any thread is stopped; when hitting software breakpoints we
|
|
/// must disable the breakpoint by restoring the original breakpoint opcode,
|
|
/// single stepping and restoring the breakpoint trap. If all threads were
|
|
/// allowed to run then other threads could miss the breakpoint.
|
|
///
|
|
/// This class centralizes the code that usually is done in separate code
|
|
/// paths in a debugger (single step prediction, finding save restore
|
|
/// locations of registers for unwinding stack frame variables) and emulating
|
|
/// the instruction is just a bonus.
|
|
|
|
class EmulateInstruction : public PluginInterface {
|
|
public:
|
|
static EmulateInstruction *FindPlugin(const ArchSpec &arch,
|
|
InstructionType supported_inst_type,
|
|
const char *plugin_name);
|
|
|
|
enum ContextType {
|
|
eContextInvalid = 0,
|
|
// Read an instruction opcode from memory
|
|
eContextReadOpcode,
|
|
|
|
// Usually used for writing a register value whose source value is an
|
|
// immediate
|
|
eContextImmediate,
|
|
|
|
// Exclusively used when saving a register to the stack as part of the
|
|
// prologue
|
|
eContextPushRegisterOnStack,
|
|
|
|
// Exclusively used when restoring a register off the stack as part of the
|
|
// epilogue
|
|
eContextPopRegisterOffStack,
|
|
|
|
// Add or subtract a value from the stack
|
|
eContextAdjustStackPointer,
|
|
|
|
// Adjust the frame pointer for the current frame
|
|
eContextSetFramePointer,
|
|
|
|
// Typically in an epilogue sequence. Copy the frame pointer back into the
|
|
// stack pointer, use SP for CFA calculations again.
|
|
eContextRestoreStackPointer,
|
|
|
|
// Add or subtract a value from a base address register (other than SP)
|
|
eContextAdjustBaseRegister,
|
|
|
|
// Add or subtract a value from the PC or store a value to the PC.
|
|
eContextAdjustPC,
|
|
|
|
// Used in WriteRegister callbacks to indicate where the
|
|
eContextRegisterPlusOffset,
|
|
|
|
// Used in WriteMemory callback to indicate where the data came from
|
|
eContextRegisterStore,
|
|
|
|
eContextRegisterLoad,
|
|
|
|
// Used when performing a PC-relative branch where the
|
|
eContextRelativeBranchImmediate,
|
|
|
|
// Used when performing an absolute branch where the
|
|
eContextAbsoluteBranchRegister,
|
|
|
|
// Used when performing a supervisor call to an operating system to provide
|
|
// a service:
|
|
eContextSupervisorCall,
|
|
|
|
// Used when performing a MemU operation to read the PC-relative offset
|
|
// from an address.
|
|
eContextTableBranchReadMemory,
|
|
|
|
// Used when random bits are written into a register
|
|
eContextWriteRegisterRandomBits,
|
|
|
|
// Used when random bits are written to memory
|
|
eContextWriteMemoryRandomBits,
|
|
|
|
eContextArithmetic,
|
|
|
|
eContextAdvancePC,
|
|
|
|
eContextReturnFromException
|
|
};
|
|
|
|
enum InfoType {
|
|
eInfoTypeRegisterPlusOffset,
|
|
eInfoTypeRegisterPlusIndirectOffset,
|
|
eInfoTypeRegisterToRegisterPlusOffset,
|
|
eInfoTypeRegisterToRegisterPlusIndirectOffset,
|
|
eInfoTypeRegisterRegisterOperands,
|
|
eInfoTypeOffset,
|
|
eInfoTypeRegister,
|
|
eInfoTypeImmediate,
|
|
eInfoTypeImmediateSigned,
|
|
eInfoTypeAddress,
|
|
eInfoTypeISAAndImmediate,
|
|
eInfoTypeISAAndImmediateSigned,
|
|
eInfoTypeISA,
|
|
eInfoTypeNoArgs
|
|
};
|
|
|
|
struct Context {
|
|
ContextType type = eContextInvalid;
|
|
|
|
private:
|
|
enum InfoType info_type = eInfoTypeNoArgs;
|
|
|
|
public:
|
|
enum InfoType GetInfoType() const { return info_type; }
|
|
union ContextInfo {
|
|
struct RegisterPlusOffset {
|
|
RegisterInfo reg; // base register
|
|
int64_t signed_offset; // signed offset added to base register
|
|
} RegisterPlusOffset;
|
|
|
|
struct RegisterPlusIndirectOffset {
|
|
RegisterInfo base_reg; // base register number
|
|
RegisterInfo offset_reg; // offset register kind
|
|
} RegisterPlusIndirectOffset;
|
|
|
|
struct RegisterToRegisterPlusOffset {
|
|
RegisterInfo data_reg; // source/target register for data
|
|
RegisterInfo base_reg; // base register for address calculation
|
|
int64_t offset; // offset for address calculation
|
|
} RegisterToRegisterPlusOffset;
|
|
|
|
struct RegisterToRegisterPlusIndirectOffset {
|
|
RegisterInfo base_reg; // base register for address calculation
|
|
RegisterInfo offset_reg; // offset register for address calculation
|
|
RegisterInfo data_reg; // source/target register for data
|
|
} RegisterToRegisterPlusIndirectOffset;
|
|
|
|
struct RegisterRegisterOperands {
|
|
RegisterInfo
|
|
operand1; // register containing first operand for binary op
|
|
RegisterInfo
|
|
operand2; // register containing second operand for binary op
|
|
} RegisterRegisterOperands;
|
|
|
|
int64_t signed_offset; // signed offset by which to adjust self (for
|
|
// registers only)
|
|
|
|
RegisterInfo reg; // plain register
|
|
|
|
uint64_t unsigned_immediate; // unsigned immediate value
|
|
int64_t signed_immediate; // signed immediate value
|
|
|
|
lldb::addr_t address; // direct address
|
|
|
|
struct ISAAndImmediate {
|
|
uint32_t isa;
|
|
uint32_t unsigned_data32; // immediate data
|
|
} ISAAndImmediate;
|
|
|
|
struct ISAAndImmediateSigned {
|
|
uint32_t isa;
|
|
int32_t signed_data32; // signed immediate data
|
|
} ISAAndImmediateSigned;
|
|
|
|
uint32_t isa;
|
|
} info;
|
|
static_assert(std::is_trivial<ContextInfo>::value,
|
|
"ContextInfo must be trivial.");
|
|
|
|
Context() = default;
|
|
|
|
void SetRegisterPlusOffset(RegisterInfo base_reg, int64_t signed_offset) {
|
|
info_type = eInfoTypeRegisterPlusOffset;
|
|
info.RegisterPlusOffset.reg = base_reg;
|
|
info.RegisterPlusOffset.signed_offset = signed_offset;
|
|
}
|
|
|
|
void SetRegisterPlusIndirectOffset(RegisterInfo base_reg,
|
|
RegisterInfo offset_reg) {
|
|
info_type = eInfoTypeRegisterPlusIndirectOffset;
|
|
info.RegisterPlusIndirectOffset.base_reg = base_reg;
|
|
info.RegisterPlusIndirectOffset.offset_reg = offset_reg;
|
|
}
|
|
|
|
void SetRegisterToRegisterPlusOffset(RegisterInfo data_reg,
|
|
RegisterInfo base_reg,
|
|
int64_t offset) {
|
|
info_type = eInfoTypeRegisterToRegisterPlusOffset;
|
|
info.RegisterToRegisterPlusOffset.data_reg = data_reg;
|
|
info.RegisterToRegisterPlusOffset.base_reg = base_reg;
|
|
info.RegisterToRegisterPlusOffset.offset = offset;
|
|
}
|
|
|
|
void SetRegisterToRegisterPlusIndirectOffset(RegisterInfo base_reg,
|
|
RegisterInfo offset_reg,
|
|
RegisterInfo data_reg) {
|
|
info_type = eInfoTypeRegisterToRegisterPlusIndirectOffset;
|
|
info.RegisterToRegisterPlusIndirectOffset.base_reg = base_reg;
|
|
info.RegisterToRegisterPlusIndirectOffset.offset_reg = offset_reg;
|
|
info.RegisterToRegisterPlusIndirectOffset.data_reg = data_reg;
|
|
}
|
|
|
|
void SetRegisterRegisterOperands(RegisterInfo op1_reg,
|
|
RegisterInfo op2_reg) {
|
|
info_type = eInfoTypeRegisterRegisterOperands;
|
|
info.RegisterRegisterOperands.operand1 = op1_reg;
|
|
info.RegisterRegisterOperands.operand2 = op2_reg;
|
|
}
|
|
|
|
void SetOffset(int64_t signed_offset) {
|
|
info_type = eInfoTypeOffset;
|
|
info.signed_offset = signed_offset;
|
|
}
|
|
|
|
void SetRegister(RegisterInfo reg) {
|
|
info_type = eInfoTypeRegister;
|
|
info.reg = reg;
|
|
}
|
|
|
|
void SetImmediate(uint64_t immediate) {
|
|
info_type = eInfoTypeImmediate;
|
|
info.unsigned_immediate = immediate;
|
|
}
|
|
|
|
void SetImmediateSigned(int64_t signed_immediate) {
|
|
info_type = eInfoTypeImmediateSigned;
|
|
info.signed_immediate = signed_immediate;
|
|
}
|
|
|
|
void SetAddress(lldb::addr_t address) {
|
|
info_type = eInfoTypeAddress;
|
|
info.address = address;
|
|
}
|
|
void SetISAAndImmediate(uint32_t isa, uint32_t data) {
|
|
info_type = eInfoTypeISAAndImmediate;
|
|
info.ISAAndImmediate.isa = isa;
|
|
info.ISAAndImmediate.unsigned_data32 = data;
|
|
}
|
|
|
|
void SetISAAndImmediateSigned(uint32_t isa, int32_t data) {
|
|
info_type = eInfoTypeISAAndImmediateSigned;
|
|
info.ISAAndImmediateSigned.isa = isa;
|
|
info.ISAAndImmediateSigned.signed_data32 = data;
|
|
}
|
|
|
|
void SetISA(uint32_t isa) {
|
|
info_type = eInfoTypeISA;
|
|
info.isa = isa;
|
|
}
|
|
|
|
void SetNoArgs() { info_type = eInfoTypeNoArgs; }
|
|
|
|
void Dump(Stream &s, EmulateInstruction *instruction) const;
|
|
};
|
|
|
|
typedef size_t (*ReadMemoryCallback)(EmulateInstruction *instruction,
|
|
void *baton, const Context &context,
|
|
lldb::addr_t addr, void *dst,
|
|
size_t length);
|
|
|
|
typedef size_t (*WriteMemoryCallback)(EmulateInstruction *instruction,
|
|
void *baton, const Context &context,
|
|
lldb::addr_t addr, const void *dst,
|
|
size_t length);
|
|
|
|
typedef bool (*ReadRegisterCallback)(EmulateInstruction *instruction,
|
|
void *baton,
|
|
const RegisterInfo *reg_info,
|
|
RegisterValue ®_value);
|
|
|
|
typedef bool (*WriteRegisterCallback)(EmulateInstruction *instruction,
|
|
void *baton, const Context &context,
|
|
const RegisterInfo *reg_info,
|
|
const RegisterValue ®_value);
|
|
|
|
// Type to represent the condition of an instruction. The UINT32 value is
|
|
// reserved for the unconditional case and all other value can be used in an
|
|
// architecture dependent way.
|
|
typedef uint32_t InstructionCondition;
|
|
static const InstructionCondition UnconditionalCondition = UINT32_MAX;
|
|
|
|
EmulateInstruction(const ArchSpec &arch);
|
|
|
|
~EmulateInstruction() override = default;
|
|
|
|
// Mandatory overrides
|
|
virtual bool
|
|
SupportsEmulatingInstructionsOfType(InstructionType inst_type) = 0;
|
|
|
|
virtual bool SetTargetTriple(const ArchSpec &arch) = 0;
|
|
|
|
virtual bool ReadInstruction() = 0;
|
|
|
|
virtual std::optional<uint32_t> GetLastInstrSize() { return std::nullopt; }
|
|
|
|
virtual bool EvaluateInstruction(uint32_t evaluate_options) = 0;
|
|
|
|
virtual InstructionCondition GetInstructionCondition() {
|
|
return UnconditionalCondition;
|
|
}
|
|
|
|
virtual bool TestEmulation(Stream &out_stream, ArchSpec &arch,
|
|
OptionValueDictionary *test_data) = 0;
|
|
|
|
virtual std::optional<RegisterInfo>
|
|
GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num) = 0;
|
|
|
|
// Optional overrides
|
|
virtual bool SetInstruction(const Opcode &insn_opcode,
|
|
const Address &inst_addr, Target *target);
|
|
|
|
virtual bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan);
|
|
|
|
static const char *TranslateRegister(lldb::RegisterKind reg_kind,
|
|
uint32_t reg_num, std::string ®_name);
|
|
|
|
// RegisterInfo variants
|
|
std::optional<RegisterValue> ReadRegister(const RegisterInfo ®_info);
|
|
|
|
uint64_t ReadRegisterUnsigned(const RegisterInfo ®_info,
|
|
uint64_t fail_value, bool *success_ptr);
|
|
|
|
bool WriteRegister(const Context &context, const RegisterInfo &ref_info,
|
|
const RegisterValue ®_value);
|
|
|
|
bool WriteRegisterUnsigned(const Context &context,
|
|
const RegisterInfo ®_info, uint64_t reg_value);
|
|
|
|
// Register kind and number variants
|
|
bool ReadRegister(lldb::RegisterKind reg_kind, uint32_t reg_num,
|
|
RegisterValue ®_value);
|
|
|
|
bool WriteRegister(const Context &context, lldb::RegisterKind reg_kind,
|
|
uint32_t reg_num, const RegisterValue ®_value);
|
|
|
|
uint64_t ReadRegisterUnsigned(lldb::RegisterKind reg_kind, uint32_t reg_num,
|
|
uint64_t fail_value, bool *success_ptr);
|
|
|
|
bool WriteRegisterUnsigned(const Context &context,
|
|
lldb::RegisterKind reg_kind, uint32_t reg_num,
|
|
uint64_t reg_value);
|
|
|
|
size_t ReadMemory(const Context &context, lldb::addr_t addr, void *dst,
|
|
size_t dst_len);
|
|
|
|
uint64_t ReadMemoryUnsigned(const Context &context, lldb::addr_t addr,
|
|
size_t byte_size, uint64_t fail_value,
|
|
bool *success_ptr);
|
|
|
|
bool WriteMemory(const Context &context, lldb::addr_t addr, const void *src,
|
|
size_t src_len);
|
|
|
|
bool WriteMemoryUnsigned(const Context &context, lldb::addr_t addr,
|
|
uint64_t uval, size_t uval_byte_size);
|
|
|
|
uint32_t GetAddressByteSize() const { return m_arch.GetAddressByteSize(); }
|
|
|
|
lldb::ByteOrder GetByteOrder() const { return m_arch.GetByteOrder(); }
|
|
|
|
const Opcode &GetOpcode() const { return m_opcode; }
|
|
|
|
lldb::addr_t GetAddress() const { return m_addr; }
|
|
|
|
const ArchSpec &GetArchitecture() const { return m_arch; }
|
|
|
|
static size_t ReadMemoryFrame(EmulateInstruction *instruction, void *baton,
|
|
const Context &context, lldb::addr_t addr,
|
|
void *dst, size_t length);
|
|
|
|
static size_t WriteMemoryFrame(EmulateInstruction *instruction, void *baton,
|
|
const Context &context, lldb::addr_t addr,
|
|
const void *dst, size_t length);
|
|
|
|
static bool ReadRegisterFrame(EmulateInstruction *instruction, void *baton,
|
|
const RegisterInfo *reg_info,
|
|
RegisterValue ®_value);
|
|
|
|
static bool WriteRegisterFrame(EmulateInstruction *instruction, void *baton,
|
|
const Context &context,
|
|
const RegisterInfo *reg_info,
|
|
const RegisterValue ®_value);
|
|
|
|
static size_t ReadMemoryDefault(EmulateInstruction *instruction, void *baton,
|
|
const Context &context, lldb::addr_t addr,
|
|
void *dst, size_t length);
|
|
|
|
static size_t WriteMemoryDefault(EmulateInstruction *instruction, void *baton,
|
|
const Context &context, lldb::addr_t addr,
|
|
const void *dst, size_t length);
|
|
|
|
static bool ReadRegisterDefault(EmulateInstruction *instruction, void *baton,
|
|
const RegisterInfo *reg_info,
|
|
RegisterValue ®_value);
|
|
|
|
static bool WriteRegisterDefault(EmulateInstruction *instruction, void *baton,
|
|
const Context &context,
|
|
const RegisterInfo *reg_info,
|
|
const RegisterValue ®_value);
|
|
|
|
void SetBaton(void *baton);
|
|
|
|
void SetCallbacks(ReadMemoryCallback read_mem_callback,
|
|
WriteMemoryCallback write_mem_callback,
|
|
ReadRegisterCallback read_reg_callback,
|
|
WriteRegisterCallback write_reg_callback);
|
|
|
|
void SetReadMemCallback(ReadMemoryCallback read_mem_callback);
|
|
|
|
void SetWriteMemCallback(WriteMemoryCallback write_mem_callback);
|
|
|
|
void SetReadRegCallback(ReadRegisterCallback read_reg_callback);
|
|
|
|
void SetWriteRegCallback(WriteRegisterCallback write_reg_callback);
|
|
|
|
static bool GetBestRegisterKindAndNumber(const RegisterInfo *reg_info,
|
|
lldb::RegisterKind ®_kind,
|
|
uint32_t ®_num);
|
|
|
|
static uint32_t GetInternalRegisterNumber(RegisterContext *reg_ctx,
|
|
const RegisterInfo ®_info);
|
|
|
|
protected:
|
|
ArchSpec m_arch;
|
|
void *m_baton = nullptr;
|
|
ReadMemoryCallback m_read_mem_callback = &ReadMemoryDefault;
|
|
WriteMemoryCallback m_write_mem_callback = &WriteMemoryDefault;
|
|
ReadRegisterCallback m_read_reg_callback = &ReadRegisterDefault;
|
|
WriteRegisterCallback m_write_reg_callback = &WriteRegisterDefault;
|
|
lldb::addr_t m_addr = LLDB_INVALID_ADDRESS;
|
|
Opcode m_opcode;
|
|
|
|
private:
|
|
// For EmulateInstruction only
|
|
EmulateInstruction(const EmulateInstruction &) = delete;
|
|
const EmulateInstruction &operator=(const EmulateInstruction &) = delete;
|
|
};
|
|
|
|
} // namespace lldb_private
|
|
|
|
#endif // LLDB_CORE_EMULATEINSTRUCTION_H
|