mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 11:46:07 +00:00
[trace] Add a flag to the decoder to output the instruction type
To build complex binding upon instruction trace, additional metadata 'instruction type' is needed. This diff has followings: - Add a flag -k / --kind for instruction dump - Remove SetGranularity and SetIgnoreErros from Trace cursor Sample output: ``` (lldb) thread trace dump instruction -k thread #1: tid = 3198805 libc.so.6`_IO_puts + 356 2107: 0x00007ffff7163594 ( return) retq 2106: 0x00007ffff7163592 ( other) popq %r13 2105: 0x00007ffff7163590 ( other) popq %r12 2104: 0x00007ffff716358f ( other) popq %rbp 2103: 0x00007ffff716358e ( other) popq %rbx 2102: 0x00007ffff716358c ( other) movl %ebx, %eax 2101: 0x00007ffff7163588 ( other) addq $0x8, %rsp 2100: 0x00007ffff7163570 ( cond jump) je 0x89588 ; <+344> 2099: 0x00007ffff716356e ( other) decl (%rdx) 2098: 0x00007ffff7163565 ( cond jump) je 0x8956e ; <+318> 2097: 0x00007ffff716355e ( other) cmpl $0x0, 0x33c02b(%rip) ; __libc_multiple_threads 2096: 0x00007ffff7163556 ( other) movq $0x0, 0x8(%rdx) 2095: 0x00007ffff7163554 ( cond jump) jne 0x89588 ; <+344> 2094: 0x00007ffff7163550 ( other) subl $0x1, 0x4(%rdx) 2093: 0x00007ffff7163549 ( other) movq 0x88(%rbp), %rdx 2092: 0x00007ffff7163547 ( cond jump) jne 0x89588 ; <+344> 2091: 0x00007ffff7163540 ( other) testl $0x8000, (%rbp) ; imm = 0x8000 2090: 0x00007ffff716353c ( other) cmovaq %rax, %rbx 2089: 0x00007ffff7163535 ( other) cmpq $0x7fffffff, %rbx ; imm = 0x7FFFFFFF 2088: 0x00007ffff7163530 ( other) movl $0x7fffffff, %eax ; imm = 0x7FFFFFFF ``` Reviewed By: wallace Differential Revision: https://reviews.llvm.org/D128477
This commit is contained in:
parent
5f8cefebd9
commit
ad7bcda940
@ -79,6 +79,12 @@ public:
|
|||||||
return m_comment.c_str();
|
return m_comment.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \return
|
||||||
|
/// The control flow kind of this instruction, or
|
||||||
|
/// eInstructionControlFlowKindUnknown if the instruction
|
||||||
|
/// can't be classified.
|
||||||
|
lldb::InstructionControlFlowKind GetControlFlowKind(const ArchSpec &arch);
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
CalculateMnemonicOperandsAndComment(const ExecutionContext *exe_ctx) = 0;
|
CalculateMnemonicOperandsAndComment(const ExecutionContext *exe_ctx) = 0;
|
||||||
|
|
||||||
@ -105,6 +111,9 @@ public:
|
|||||||
/// \param[in] show_bytes
|
/// \param[in] show_bytes
|
||||||
/// Whether the bytes of the assembly instruction should be printed.
|
/// Whether the bytes of the assembly instruction should be printed.
|
||||||
///
|
///
|
||||||
|
/// \param[in] show_control_flow_kind
|
||||||
|
/// Whether the control flow kind of the instruction should be printed.
|
||||||
|
///
|
||||||
/// \param[in] max_opcode_byte_size
|
/// \param[in] max_opcode_byte_size
|
||||||
/// The size (in bytes) of the largest instruction in the list that
|
/// The size (in bytes) of the largest instruction in the list that
|
||||||
/// we are printing (for text justification/alignment purposes)
|
/// we are printing (for text justification/alignment purposes)
|
||||||
@ -140,7 +149,8 @@ public:
|
|||||||
/// so this method can properly align the instruction opcodes.
|
/// so this method can properly align the instruction opcodes.
|
||||||
/// May be 0 to indicate no indentation/alignment of the opcodes.
|
/// May be 0 to indicate no indentation/alignment of the opcodes.
|
||||||
virtual void Dump(Stream *s, uint32_t max_opcode_byte_size, bool show_address,
|
virtual void Dump(Stream *s, uint32_t max_opcode_byte_size, bool show_address,
|
||||||
bool show_bytes, const ExecutionContext *exe_ctx,
|
bool show_bytes, bool show_control_flow_kind,
|
||||||
|
const ExecutionContext *exe_ctx,
|
||||||
const SymbolContext *sym_ctx,
|
const SymbolContext *sym_ctx,
|
||||||
const SymbolContext *prev_sym_ctx,
|
const SymbolContext *prev_sym_ctx,
|
||||||
const FormatEntity::Entry *disassembly_addr_format,
|
const FormatEntity::Entry *disassembly_addr_format,
|
||||||
@ -320,7 +330,7 @@ public:
|
|||||||
void Append(lldb::InstructionSP &inst_sp);
|
void Append(lldb::InstructionSP &inst_sp);
|
||||||
|
|
||||||
void Dump(Stream *s, bool show_address, bool show_bytes,
|
void Dump(Stream *s, bool show_address, bool show_bytes,
|
||||||
const ExecutionContext *exe_ctx);
|
bool show_control_flow_kind, const ExecutionContext *exe_ctx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::vector<lldb::InstructionSP> collection;
|
typedef std::vector<lldb::InstructionSP> collection;
|
||||||
@ -375,7 +385,8 @@ public:
|
|||||||
eOptionMarkPCSourceLine = (1u << 2), // Mark the source line that contains
|
eOptionMarkPCSourceLine = (1u << 2), // Mark the source line that contains
|
||||||
// the current PC (mixed mode only)
|
// the current PC (mixed mode only)
|
||||||
eOptionMarkPCAddress =
|
eOptionMarkPCAddress =
|
||||||
(1u << 3) // Mark the disassembly line the contains the PC
|
(1u << 3), // Mark the disassembly line the contains the PC
|
||||||
|
eOptionShowControlFlowKind = (1u << 4),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum HexImmediateStyle {
|
enum HexImmediateStyle {
|
||||||
|
@ -34,6 +34,8 @@ struct TraceDumperOptions {
|
|||||||
bool show_tsc = false;
|
bool show_tsc = false;
|
||||||
/// Dump the events that happened between instructions.
|
/// Dump the events that happened between instructions.
|
||||||
bool show_events = false;
|
bool show_events = false;
|
||||||
|
/// For each instruction, print the instruction kind.
|
||||||
|
bool show_control_flow_kind = false;
|
||||||
/// Optional custom id to start traversing from.
|
/// Optional custom id to start traversing from.
|
||||||
llvm::Optional<uint64_t> id = llvm::None;
|
llvm::Optional<uint64_t> id = llvm::None;
|
||||||
/// Optional number of instructions to skip from the starting position
|
/// Optional number of instructions to skip from the starting position
|
||||||
|
@ -970,20 +970,30 @@ enum ExpressionEvaluationPhase {
|
|||||||
/// control flow of a trace.
|
/// control flow of a trace.
|
||||||
///
|
///
|
||||||
/// A single instruction can match one or more of these categories.
|
/// A single instruction can match one or more of these categories.
|
||||||
FLAGS_ENUM(TraceInstructionControlFlowType){
|
enum InstructionControlFlowKind {
|
||||||
/// Any instruction.
|
/// The instruction could not be classified.
|
||||||
eTraceInstructionControlFlowTypeInstruction = (1u << 1),
|
eInstructionControlFlowKindUnknown = 0,
|
||||||
/// A conditional or unconditional branch/jump.
|
/// The instruction is something not listed below, i.e. it's a sequential
|
||||||
eTraceInstructionControlFlowTypeBranch = (1u << 2),
|
/// instruction that doesn't affect the control flow of the program.
|
||||||
/// A conditional or unconditional branch/jump that changed
|
eInstructionControlFlowKindOther,
|
||||||
/// the control flow of the program.
|
/// The instruction is a near (function) call.
|
||||||
eTraceInstructionControlFlowTypeTakenBranch = (1u << 3),
|
eInstructionControlFlowKindCall,
|
||||||
/// A call to a function.
|
/// The instruction is a near (function) return.
|
||||||
eTraceInstructionControlFlowTypeCall = (1u << 4),
|
eInstructionControlFlowKindReturn,
|
||||||
/// A return from a function.
|
/// The instruction is a near unconditional jump.
|
||||||
eTraceInstructionControlFlowTypeReturn = (1u << 5)};
|
eInstructionControlFlowKindJump,
|
||||||
|
/// The instruction is a near conditional jump.
|
||||||
LLDB_MARK_AS_BITMASK_ENUM(TraceInstructionControlFlowType)
|
eInstructionControlFlowKindCondJump,
|
||||||
|
/// The instruction is a call-like far transfer.
|
||||||
|
/// E.g. SYSCALL, SYSENTER, or FAR CALL.
|
||||||
|
eInstructionControlFlowKindFarCall,
|
||||||
|
/// The instruction is a return-like far transfer.
|
||||||
|
/// E.g. SYSRET, SYSEXIT, IRET, or FAR RET.
|
||||||
|
eInstructionControlFlowKindFarReturn,
|
||||||
|
/// The instruction is a jump-like far transfer.
|
||||||
|
/// E.g. FAR JMP.
|
||||||
|
eInstructionControlFlowKindFarJump
|
||||||
|
};
|
||||||
|
|
||||||
/// Watchpoint Kind.
|
/// Watchpoint Kind.
|
||||||
///
|
///
|
||||||
|
@ -241,7 +241,8 @@ bool SBInstruction::GetDescription(lldb::SBStream &s) {
|
|||||||
// didn't have a stream already created, one will get created...
|
// didn't have a stream already created, one will get created...
|
||||||
FormatEntity::Entry format;
|
FormatEntity::Entry format;
|
||||||
FormatEntity::Parse("${addr}: ", format);
|
FormatEntity::Parse("${addr}: ", format);
|
||||||
inst_sp->Dump(&s.ref(), 0, true, false, nullptr, &sc, nullptr, &format, 0);
|
inst_sp->Dump(&s.ref(), 0, true, false, /*show_control_flow_kind=*/false,
|
||||||
|
nullptr, &sc, nullptr, &format, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -275,8 +276,8 @@ void SBInstruction::Print(FileSP out_sp) {
|
|||||||
StreamFile out_stream(out_sp);
|
StreamFile out_stream(out_sp);
|
||||||
FormatEntity::Entry format;
|
FormatEntity::Entry format;
|
||||||
FormatEntity::Parse("${addr}: ", format);
|
FormatEntity::Parse("${addr}: ", format);
|
||||||
inst_sp->Dump(&out_stream, 0, true, false, nullptr, &sc, nullptr, &format,
|
inst_sp->Dump(&out_stream, 0, true, false, /*show_control_flow_kind=*/false,
|
||||||
0);
|
nullptr, &sc, nullptr, &format, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,8 +165,9 @@ bool SBInstructionList::GetDescription(Stream &sref) {
|
|||||||
addr, eSymbolContextEverything, sc);
|
addr, eSymbolContextEverything, sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
inst->Dump(&sref, max_opcode_byte_size, true, false, nullptr, &sc,
|
inst->Dump(&sref, max_opcode_byte_size, true, false,
|
||||||
&prev_sc, &format, 0);
|
/*show_control_flow_kind=*/false, nullptr, &sc, &prev_sc,
|
||||||
|
&format, 0);
|
||||||
sref.EOL();
|
sref.EOL();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -65,6 +65,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
|
|||||||
show_bytes = true;
|
show_bytes = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
show_control_flow_kind = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case 's': {
|
case 's': {
|
||||||
start_addr = OptionArgParser::ToAddress(execution_context, option_arg,
|
start_addr = OptionArgParser::ToAddress(execution_context, option_arg,
|
||||||
LLDB_INVALID_ADDRESS, &error);
|
LLDB_INVALID_ADDRESS, &error);
|
||||||
@ -154,6 +158,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
|
|||||||
ExecutionContext *execution_context) {
|
ExecutionContext *execution_context) {
|
||||||
show_mixed = false;
|
show_mixed = false;
|
||||||
show_bytes = false;
|
show_bytes = false;
|
||||||
|
show_control_flow_kind = false;
|
||||||
num_lines_context = 0;
|
num_lines_context = 0;
|
||||||
num_instructions = 0;
|
num_instructions = 0;
|
||||||
func_name.clear();
|
func_name.clear();
|
||||||
@ -493,6 +498,9 @@ bool CommandObjectDisassemble::DoExecute(Args &command,
|
|||||||
if (m_options.show_bytes)
|
if (m_options.show_bytes)
|
||||||
options |= Disassembler::eOptionShowBytes;
|
options |= Disassembler::eOptionShowBytes;
|
||||||
|
|
||||||
|
if (m_options.show_control_flow_kind)
|
||||||
|
options |= Disassembler::eOptionShowControlFlowKind;
|
||||||
|
|
||||||
if (m_options.raw)
|
if (m_options.raw)
|
||||||
options |= Disassembler::eOptionRawOuput;
|
options |= Disassembler::eOptionRawOuput;
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ public:
|
|||||||
|
|
||||||
bool show_mixed; // Show mixed source/assembly
|
bool show_mixed; // Show mixed source/assembly
|
||||||
bool show_bytes;
|
bool show_bytes;
|
||||||
|
bool show_control_flow_kind;
|
||||||
uint32_t num_lines_context = 0;
|
uint32_t num_lines_context = 0;
|
||||||
uint32_t num_instructions = 0;
|
uint32_t num_instructions = 0;
|
||||||
bool raw;
|
bool raw;
|
||||||
|
@ -2174,6 +2174,10 @@ public:
|
|||||||
m_dumper_options.forwards = true;
|
m_dumper_options.forwards = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'k': {
|
||||||
|
m_dumper_options.show_control_flow_kind = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 't': {
|
case 't': {
|
||||||
m_dumper_options.show_tsc = true;
|
m_dumper_options.show_tsc = true;
|
||||||
break;
|
break;
|
||||||
|
@ -300,6 +300,11 @@ let Command = "breakpoint command delete" in {
|
|||||||
let Command = "disassemble" in {
|
let Command = "disassemble" in {
|
||||||
def disassemble_options_bytes : Option<"bytes", "b">,
|
def disassemble_options_bytes : Option<"bytes", "b">,
|
||||||
Desc<"Show opcode bytes when disassembling.">;
|
Desc<"Show opcode bytes when disassembling.">;
|
||||||
|
def disassemble_options_kind : Option<"kind", "k">,
|
||||||
|
Desc<"Show instruction control flow kind. Refer to the enum "
|
||||||
|
"`InstructionControlFlowKind` for a list of control flow kind. "
|
||||||
|
"As an important note, far jumps, far calls and far returns often indicate "
|
||||||
|
"calls to and from kernel.">;
|
||||||
def disassemble_options_context : Option<"context", "C">, Arg<"NumLines">,
|
def disassemble_options_context : Option<"context", "C">, Arg<"NumLines">,
|
||||||
Desc<"Number of context lines of source to show.">;
|
Desc<"Number of context lines of source to show.">;
|
||||||
def disassemble_options_mixed : Option<"mixed", "m">,
|
def disassemble_options_mixed : Option<"mixed", "m">,
|
||||||
@ -1150,6 +1155,11 @@ let Command = "thread trace dump instructions" in {
|
|||||||
def thread_trace_dump_instructions_pretty_print: Option<"pretty-json", "J">,
|
def thread_trace_dump_instructions_pretty_print: Option<"pretty-json", "J">,
|
||||||
Group<1>,
|
Group<1>,
|
||||||
Desc<"Dump in JSON format but pretty printing the output for easier readability.">;
|
Desc<"Dump in JSON format but pretty printing the output for easier readability.">;
|
||||||
|
def thread_trace_dump_instructions_show_kind : Option<"kind", "k">, Group<1>,
|
||||||
|
Desc<"Show instruction control flow kind. Refer to the enum "
|
||||||
|
"`InstructionControlFlowKind` for a list of control flow kind. "
|
||||||
|
"As an important note, far jumps, far calls and far returns often indicate "
|
||||||
|
"calls to and from kernel.">;
|
||||||
def thread_trace_dump_instructions_show_tsc : Option<"tsc", "t">, Group<1>,
|
def thread_trace_dump_instructions_show_tsc : Option<"tsc", "t">, Group<1>,
|
||||||
Desc<"For each instruction, print the corresponding timestamp counter if "
|
Desc<"For each instruction, print the corresponding timestamp counter if "
|
||||||
"available.">;
|
"available.">;
|
||||||
|
@ -527,8 +527,11 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bool show_bytes = (options & eOptionShowBytes) != 0;
|
const bool show_bytes = (options & eOptionShowBytes) != 0;
|
||||||
inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc,
|
const bool show_control_flow_kind =
|
||||||
&prev_sc, nullptr, address_text_size);
|
(options & eOptionShowControlFlowKind) != 0;
|
||||||
|
inst->Dump(&strm, max_opcode_byte_size, true, show_bytes,
|
||||||
|
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
|
||||||
|
address_text_size);
|
||||||
strm.EOL();
|
strm.EOL();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -568,6 +571,334 @@ Instruction::Instruction(const Address &address, AddressClass addr_class)
|
|||||||
|
|
||||||
Instruction::~Instruction() = default;
|
Instruction::~Instruction() = default;
|
||||||
|
|
||||||
|
namespace x86 {
|
||||||
|
|
||||||
|
/// These are the three values deciding instruction control flow kind.
|
||||||
|
/// InstructionLengthDecode function decodes an instruction and get this struct.
|
||||||
|
///
|
||||||
|
/// primary_opcode
|
||||||
|
/// Primary opcode of the instruction.
|
||||||
|
/// For one-byte opcode instruction, it's the first byte after prefix.
|
||||||
|
/// For two- and three-byte opcodes, it's the second byte.
|
||||||
|
///
|
||||||
|
/// opcode_len
|
||||||
|
/// The length of opcode in bytes. Valid opcode lengths are 1, 2, or 3.
|
||||||
|
///
|
||||||
|
/// modrm
|
||||||
|
/// ModR/M byte of the instruction.
|
||||||
|
/// Bits[7:6] indicate MOD. Bits[5:3] specify a register and R/M bits[2:0]
|
||||||
|
/// may contain a register or specify an addressing mode, depending on MOD.
|
||||||
|
struct InstructionOpcodeAndModrm {
|
||||||
|
uint8_t primary_opcode;
|
||||||
|
uint8_t opcode_len;
|
||||||
|
uint8_t modrm;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Determine the InstructionControlFlowKind based on opcode and modrm bytes.
|
||||||
|
/// Refer to http://ref.x86asm.net/coder.html for the full list of opcode and
|
||||||
|
/// instruction set.
|
||||||
|
///
|
||||||
|
/// \param[in] opcode_and_modrm
|
||||||
|
/// Contains primary_opcode byte, its length, and ModR/M byte.
|
||||||
|
/// Refer to the struct InstructionOpcodeAndModrm for details.
|
||||||
|
///
|
||||||
|
/// \return
|
||||||
|
/// The control flow kind of the instruction or
|
||||||
|
/// eInstructionControlFlowKindOther if the instruction doesn't affect
|
||||||
|
/// the control flow of the program.
|
||||||
|
lldb::InstructionControlFlowKind
|
||||||
|
MapOpcodeIntoControlFlowKind(InstructionOpcodeAndModrm opcode_and_modrm) {
|
||||||
|
uint8_t opcode = opcode_and_modrm.primary_opcode;
|
||||||
|
uint8_t opcode_len = opcode_and_modrm.opcode_len;
|
||||||
|
uint8_t modrm = opcode_and_modrm.modrm;
|
||||||
|
|
||||||
|
if (opcode_len > 2)
|
||||||
|
return lldb::eInstructionControlFlowKindOther;
|
||||||
|
|
||||||
|
if (opcode >= 0x70 && opcode <= 0x7F) {
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindCondJump;
|
||||||
|
else
|
||||||
|
return lldb::eInstructionControlFlowKindOther;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode >= 0x80 && opcode <= 0x8F) {
|
||||||
|
if (opcode_len == 2)
|
||||||
|
return lldb::eInstructionControlFlowKindCondJump;
|
||||||
|
else
|
||||||
|
return lldb::eInstructionControlFlowKindOther;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (opcode) {
|
||||||
|
case 0x9A:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindFarCall;
|
||||||
|
break;
|
||||||
|
case 0xFF:
|
||||||
|
if (opcode_len == 1) {
|
||||||
|
uint8_t modrm_reg = (modrm >> 3) & 7;
|
||||||
|
if (modrm_reg == 2)
|
||||||
|
return lldb::eInstructionControlFlowKindCall;
|
||||||
|
else if (modrm_reg == 3)
|
||||||
|
return lldb::eInstructionControlFlowKindFarCall;
|
||||||
|
else if (modrm_reg == 4)
|
||||||
|
return lldb::eInstructionControlFlowKindJump;
|
||||||
|
else if (modrm_reg == 5)
|
||||||
|
return lldb::eInstructionControlFlowKindFarJump;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xE8:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindCall;
|
||||||
|
break;
|
||||||
|
case 0xCD:
|
||||||
|
case 0xCC:
|
||||||
|
case 0xCE:
|
||||||
|
case 0xF1:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindFarCall;
|
||||||
|
break;
|
||||||
|
case 0xCF:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindFarReturn;
|
||||||
|
break;
|
||||||
|
case 0xE9:
|
||||||
|
case 0xEB:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindJump;
|
||||||
|
break;
|
||||||
|
case 0xEA:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindFarJump;
|
||||||
|
break;
|
||||||
|
case 0xE3:
|
||||||
|
case 0xE0:
|
||||||
|
case 0xE1:
|
||||||
|
case 0xE2:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindCondJump;
|
||||||
|
break;
|
||||||
|
case 0xC3:
|
||||||
|
case 0xC2:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindReturn;
|
||||||
|
break;
|
||||||
|
case 0xCB:
|
||||||
|
case 0xCA:
|
||||||
|
if (opcode_len == 1)
|
||||||
|
return lldb::eInstructionControlFlowKindFarReturn;
|
||||||
|
break;
|
||||||
|
case 0x05:
|
||||||
|
case 0x34:
|
||||||
|
if (opcode_len == 2)
|
||||||
|
return lldb::eInstructionControlFlowKindFarCall;
|
||||||
|
break;
|
||||||
|
case 0x35:
|
||||||
|
case 0x07:
|
||||||
|
if (opcode_len == 2)
|
||||||
|
return lldb::eInstructionControlFlowKindFarReturn;
|
||||||
|
break;
|
||||||
|
case 0x01:
|
||||||
|
if (opcode_len == 2) {
|
||||||
|
switch (modrm) {
|
||||||
|
case 0xc1:
|
||||||
|
return lldb::eInstructionControlFlowKindFarCall;
|
||||||
|
case 0xc2:
|
||||||
|
case 0xc3:
|
||||||
|
return lldb::eInstructionControlFlowKindFarReturn;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lldb::eInstructionControlFlowKindOther;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode an instruction into opcode, modrm and opcode_len.
|
||||||
|
/// Refer to http://ref.x86asm.net/coder.html for the instruction bytes layout.
|
||||||
|
/// Opcodes in x86 are generally the first byte of instruction, though two-byte
|
||||||
|
/// instructions and prefixes exist. ModR/M is the byte following the opcode
|
||||||
|
/// and adds additional information for how the instruction is executed.
|
||||||
|
///
|
||||||
|
/// \param[in] inst_bytes
|
||||||
|
/// Raw bytes of the instruction
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// \param[in] bytes_len
|
||||||
|
/// The length of the inst_bytes array.
|
||||||
|
///
|
||||||
|
/// \param[in] is_exec_mode_64b
|
||||||
|
/// If true, the execution mode is 64 bit.
|
||||||
|
///
|
||||||
|
/// \return
|
||||||
|
/// Returns decoded instruction as struct InstructionOpcodeAndModrm, holding
|
||||||
|
/// primary_opcode, opcode_len and modrm byte. Refer to the struct definition
|
||||||
|
/// for more details.
|
||||||
|
/// Otherwise if the given instruction is invalid, returns None.
|
||||||
|
llvm::Optional<InstructionOpcodeAndModrm>
|
||||||
|
InstructionLengthDecode(const uint8_t *inst_bytes, int bytes_len,
|
||||||
|
bool is_exec_mode_64b) {
|
||||||
|
int op_idx = 0;
|
||||||
|
bool prefix_done = false;
|
||||||
|
InstructionOpcodeAndModrm ret = {0, 0, 0};
|
||||||
|
|
||||||
|
// In most cases, the primary_opcode is the first byte of the instruction
|
||||||
|
// but some instructions have a prefix to be skipped for these calculations.
|
||||||
|
// The following mapping is inspired from libipt's instruction decoding logic
|
||||||
|
// in `src/pt_ild.c`
|
||||||
|
while (!prefix_done) {
|
||||||
|
if (op_idx >= bytes_len)
|
||||||
|
return llvm::None;
|
||||||
|
|
||||||
|
ret.primary_opcode = inst_bytes[op_idx];
|
||||||
|
switch (ret.primary_opcode) {
|
||||||
|
// prefix_ignore
|
||||||
|
case 0x26:
|
||||||
|
case 0x2e:
|
||||||
|
case 0x36:
|
||||||
|
case 0x3e:
|
||||||
|
case 0x64:
|
||||||
|
case 0x65:
|
||||||
|
// prefix_osz, prefix_asz
|
||||||
|
case 0x66:
|
||||||
|
case 0x67:
|
||||||
|
// prefix_lock, prefix_f2, prefix_f3
|
||||||
|
case 0xf0:
|
||||||
|
case 0xf2:
|
||||||
|
case 0xf3:
|
||||||
|
op_idx++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// prefix_rex
|
||||||
|
case 0x40:
|
||||||
|
case 0x41:
|
||||||
|
case 0x42:
|
||||||
|
case 0x43:
|
||||||
|
case 0x44:
|
||||||
|
case 0x45:
|
||||||
|
case 0x46:
|
||||||
|
case 0x47:
|
||||||
|
case 0x48:
|
||||||
|
case 0x49:
|
||||||
|
case 0x4a:
|
||||||
|
case 0x4b:
|
||||||
|
case 0x4c:
|
||||||
|
case 0x4d:
|
||||||
|
case 0x4e:
|
||||||
|
case 0x4f:
|
||||||
|
if (is_exec_mode_64b)
|
||||||
|
op_idx++;
|
||||||
|
else
|
||||||
|
prefix_done = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// prefix_vex_c4, c5
|
||||||
|
case 0xc5:
|
||||||
|
if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) {
|
||||||
|
prefix_done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.opcode_len = 2;
|
||||||
|
ret.primary_opcode = inst_bytes[op_idx + 2];
|
||||||
|
ret.modrm = inst_bytes[op_idx + 3];
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
case 0xc4:
|
||||||
|
if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) {
|
||||||
|
prefix_done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret.opcode_len = inst_bytes[op_idx + 1] & 0x1f;
|
||||||
|
ret.primary_opcode = inst_bytes[op_idx + 3];
|
||||||
|
ret.modrm = inst_bytes[op_idx + 4];
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// prefix_evex
|
||||||
|
case 0x62:
|
||||||
|
if (!is_exec_mode_64b && (inst_bytes[op_idx + 1] & 0xc0) != 0xc0) {
|
||||||
|
prefix_done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ret.opcode_len = inst_bytes[op_idx + 1] & 0x03;
|
||||||
|
ret.primary_opcode = inst_bytes[op_idx + 4];
|
||||||
|
ret.modrm = inst_bytes[op_idx + 5];
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
default:
|
||||||
|
prefix_done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // prefix done
|
||||||
|
|
||||||
|
ret.primary_opcode = inst_bytes[op_idx];
|
||||||
|
ret.modrm = inst_bytes[op_idx + 1];
|
||||||
|
ret.opcode_len = 1;
|
||||||
|
|
||||||
|
// If the first opcode is 0F, it's two- or three- byte opcodes.
|
||||||
|
if (ret.primary_opcode == 0x0F) {
|
||||||
|
ret.primary_opcode = inst_bytes[++op_idx]; // get the next byte
|
||||||
|
|
||||||
|
if (ret.primary_opcode == 0x38) {
|
||||||
|
ret.opcode_len = 3;
|
||||||
|
ret.primary_opcode = inst_bytes[++op_idx]; // get the next byte
|
||||||
|
ret.modrm = inst_bytes[op_idx + 1];
|
||||||
|
} else if (ret.primary_opcode == 0x3A) {
|
||||||
|
ret.opcode_len = 3;
|
||||||
|
ret.primary_opcode = inst_bytes[++op_idx];
|
||||||
|
ret.modrm = inst_bytes[op_idx + 1];
|
||||||
|
} else if ((ret.primary_opcode & 0xf8) == 0x38) {
|
||||||
|
ret.opcode_len = 0;
|
||||||
|
ret.primary_opcode = inst_bytes[++op_idx];
|
||||||
|
ret.modrm = inst_bytes[op_idx + 1];
|
||||||
|
} else if (ret.primary_opcode == 0x0F) {
|
||||||
|
ret.opcode_len = 3;
|
||||||
|
// opcode is 0x0F, no needs to update
|
||||||
|
ret.modrm = inst_bytes[op_idx + 1];
|
||||||
|
} else {
|
||||||
|
ret.opcode_len = 2;
|
||||||
|
ret.modrm = inst_bytes[op_idx + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
lldb::InstructionControlFlowKind GetControlFlowKind(bool is_exec_mode_64b,
|
||||||
|
Opcode m_opcode) {
|
||||||
|
llvm::Optional<InstructionOpcodeAndModrm> ret = llvm::None;
|
||||||
|
|
||||||
|
if (m_opcode.GetOpcodeBytes() == nullptr || m_opcode.GetByteSize() <= 0) {
|
||||||
|
// x86_64 and i386 instructions are categorized as Opcode::Type::eTypeBytes
|
||||||
|
return lldb::eInstructionControlFlowKindUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opcode bytes will be decoded into primary_opcode, modrm and opcode length.
|
||||||
|
// These are the three values deciding instruction control flow kind.
|
||||||
|
ret = InstructionLengthDecode((const uint8_t *)m_opcode.GetOpcodeBytes(),
|
||||||
|
m_opcode.GetByteSize(), is_exec_mode_64b);
|
||||||
|
if (!ret)
|
||||||
|
return lldb::eInstructionControlFlowKindUnknown;
|
||||||
|
else
|
||||||
|
return MapOpcodeIntoControlFlowKind(ret.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace x86
|
||||||
|
|
||||||
|
lldb::InstructionControlFlowKind
|
||||||
|
Instruction::GetControlFlowKind(const ArchSpec &arch) {
|
||||||
|
if (arch.GetTriple().getArch() == llvm::Triple::x86)
|
||||||
|
return x86::GetControlFlowKind(/*is_exec_mode_64b=*/false, m_opcode);
|
||||||
|
else if (arch.GetTriple().getArch() == llvm::Triple::x86_64)
|
||||||
|
return x86::GetControlFlowKind(/*is_exec_mode_64b=*/true, m_opcode);
|
||||||
|
else
|
||||||
|
return eInstructionControlFlowKindUnknown; // not implemented
|
||||||
|
}
|
||||||
|
|
||||||
AddressClass Instruction::GetAddressClass() {
|
AddressClass Instruction::GetAddressClass() {
|
||||||
if (m_address_class == AddressClass::eInvalid)
|
if (m_address_class == AddressClass::eInvalid)
|
||||||
m_address_class = m_address.GetAddressClass();
|
m_address_class = m_address.GetAddressClass();
|
||||||
@ -576,6 +907,7 @@ AddressClass Instruction::GetAddressClass() {
|
|||||||
|
|
||||||
void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
|
void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
|
||||||
bool show_address, bool show_bytes,
|
bool show_address, bool show_bytes,
|
||||||
|
bool show_control_flow_kind,
|
||||||
const ExecutionContext *exe_ctx,
|
const ExecutionContext *exe_ctx,
|
||||||
const SymbolContext *sym_ctx,
|
const SymbolContext *sym_ctx,
|
||||||
const SymbolContext *prev_sym_ctx,
|
const SymbolContext *prev_sym_ctx,
|
||||||
@ -613,6 +945,38 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (show_control_flow_kind) {
|
||||||
|
switch (GetControlFlowKind(exe_ctx->GetTargetRef().GetArchitecture())) {
|
||||||
|
case eInstructionControlFlowKindUnknown:
|
||||||
|
ss.Printf("%-12s", "unknown");
|
||||||
|
break;
|
||||||
|
case eInstructionControlFlowKindOther:
|
||||||
|
ss.Printf("%-12s", "other");
|
||||||
|
break;
|
||||||
|
case eInstructionControlFlowKindCall:
|
||||||
|
ss.Printf("%-12s", "call");
|
||||||
|
break;
|
||||||
|
case eInstructionControlFlowKindReturn:
|
||||||
|
ss.Printf("%-12s", "return");
|
||||||
|
break;
|
||||||
|
case eInstructionControlFlowKindJump:
|
||||||
|
ss.Printf("%-12s", "jump");
|
||||||
|
break;
|
||||||
|
case eInstructionControlFlowKindCondJump:
|
||||||
|
ss.Printf("%-12s", "cond jump");
|
||||||
|
break;
|
||||||
|
case eInstructionControlFlowKindFarCall:
|
||||||
|
ss.Printf("%-12s", "far call");
|
||||||
|
break;
|
||||||
|
case eInstructionControlFlowKindFarReturn:
|
||||||
|
ss.Printf("%-12s", "far return");
|
||||||
|
break;
|
||||||
|
case eInstructionControlFlowKindFarJump:
|
||||||
|
ss.Printf("%-12s", "far jump");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const size_t opcode_pos = ss.GetSizeOfLastLine();
|
const size_t opcode_pos = ss.GetSizeOfLastLine();
|
||||||
|
|
||||||
// The default opcode size of 7 characters is plenty for most architectures
|
// The default opcode size of 7 characters is plenty for most architectures
|
||||||
@ -957,6 +1321,7 @@ InstructionSP InstructionList::GetInstructionAtAddress(const Address &address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,
|
void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,
|
||||||
|
bool show_control_flow_kind,
|
||||||
const ExecutionContext *exe_ctx) {
|
const ExecutionContext *exe_ctx) {
|
||||||
const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize();
|
const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize();
|
||||||
collection::const_iterator pos, begin, end;
|
collection::const_iterator pos, begin, end;
|
||||||
@ -975,8 +1340,9 @@ void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,
|
|||||||
pos != end; ++pos) {
|
pos != end; ++pos) {
|
||||||
if (pos != begin)
|
if (pos != begin)
|
||||||
s->EOL();
|
s->EOL();
|
||||||
(*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx,
|
(*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes,
|
||||||
nullptr, nullptr, disassembly_format, 0);
|
show_control_flow_kind, exe_ctx, nullptr, nullptr,
|
||||||
|
disassembly_format, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,10 +170,11 @@ static lldb::offset_t DumpInstructions(const DataExtractor &DE, Stream *s,
|
|||||||
offset += bytes_consumed;
|
offset += bytes_consumed;
|
||||||
const bool show_address = base_addr != LLDB_INVALID_ADDRESS;
|
const bool show_address = base_addr != LLDB_INVALID_ADDRESS;
|
||||||
const bool show_bytes = true;
|
const bool show_bytes = true;
|
||||||
|
const bool show_control_flow_kind = true;
|
||||||
ExecutionContext exe_ctx;
|
ExecutionContext exe_ctx;
|
||||||
exe_scope->CalculateExecutionContext(exe_ctx);
|
exe_scope->CalculateExecutionContext(exe_ctx);
|
||||||
disassembler_sp->GetInstructionList().Dump(s, show_address, show_bytes,
|
disassembler_sp->GetInstructionList().Dump(
|
||||||
&exe_ctx);
|
s, show_address, show_bytes, show_control_flow_kind, &exe_ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -200,7 +200,9 @@ Status IRExecutionUnit::DisassembleFunction(Stream &stream,
|
|||||||
UINT32_MAX, false, false);
|
UINT32_MAX, false, false);
|
||||||
|
|
||||||
InstructionList &instruction_list = disassembler_sp->GetInstructionList();
|
InstructionList &instruction_list = disassembler_sp->GetInstructionList();
|
||||||
instruction_list.Dump(&stream, true, true, &exe_ctx);
|
instruction_list.Dump(&stream, true, true, /*show_control_flow_kind=*/true,
|
||||||
|
&exe_ctx);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,15 +160,15 @@ TraceHTR::TraceHTR(Thread &thread, TraceCursor &cursor)
|
|||||||
cursor.Next();
|
cursor.Next();
|
||||||
} else {
|
} else {
|
||||||
lldb::addr_t current_instruction_load_address = cursor.GetLoadAddress();
|
lldb::addr_t current_instruction_load_address = cursor.GetLoadAddress();
|
||||||
lldb::TraceInstructionControlFlowType current_instruction_type =
|
lldb::InstructionControlFlowKind current_instruction_type =
|
||||||
cursor.GetInstructionControlFlowType();
|
cursor.GetInstructionControlFlowKind();
|
||||||
|
|
||||||
m_instruction_layer_up->AppendInstruction(
|
m_instruction_layer_up->AppendInstruction(
|
||||||
current_instruction_load_address);
|
current_instruction_load_address);
|
||||||
cursor.Next();
|
cursor.Next();
|
||||||
bool more_data_in_trace = cursor.HasValue();
|
bool more_data_in_trace = cursor.HasValue();
|
||||||
if (current_instruction_type &
|
if (current_instruction_type &
|
||||||
lldb::eTraceInstructionControlFlowTypeCall) {
|
lldb::eInstructionControlFlowKindCall) {
|
||||||
if (more_data_in_trace && !cursor.IsError()) {
|
if (more_data_in_trace && !cursor.IsError()) {
|
||||||
m_instruction_layer_up->AddCallInstructionMetadata(
|
m_instruction_layer_up->AddCallInstructionMetadata(
|
||||||
current_instruction_load_address,
|
current_instruction_load_address,
|
||||||
|
@ -83,6 +83,7 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
|
|||||||
const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
|
const uint32_t addr_byte_size = m_arch.GetAddressByteSize();
|
||||||
const bool show_address = true;
|
const bool show_address = true;
|
||||||
const bool show_bytes = true;
|
const bool show_bytes = true;
|
||||||
|
const bool show_control_flow_kind = true;
|
||||||
m_inst_emulator_up->GetRegisterInfo(unwind_plan.GetRegisterKind(),
|
m_inst_emulator_up->GetRegisterInfo(unwind_plan.GetRegisterKind(),
|
||||||
unwind_plan.GetInitialCFARegister(),
|
unwind_plan.GetInitialCFARegister(),
|
||||||
m_cfa_reg_info);
|
m_cfa_reg_info);
|
||||||
@ -244,7 +245,8 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly(
|
|||||||
lldb_private::FormatEntity::Entry format;
|
lldb_private::FormatEntity::Entry format;
|
||||||
FormatEntity::Parse("${frame.pc}: ", format);
|
FormatEntity::Parse("${frame.pc}: ", format);
|
||||||
inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address,
|
inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address,
|
||||||
show_bytes, nullptr, nullptr, nullptr, &format, 0);
|
show_bytes, show_control_flow_kind, nullptr, nullptr,
|
||||||
|
nullptr, &format, 0);
|
||||||
log->PutString(strm.GetString());
|
log->PutString(strm.GetString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,8 +439,9 @@ bool Function::GetDisassembly(const ExecutionContext &exe_ctx,
|
|||||||
if (disassembler_sp) {
|
if (disassembler_sp) {
|
||||||
const bool show_address = true;
|
const bool show_address = true;
|
||||||
const bool show_bytes = false;
|
const bool show_bytes = false;
|
||||||
disassembler_sp->GetInstructionList().Dump(&strm, show_address, show_bytes,
|
const bool show_control_flow_kind = false;
|
||||||
&exe_ctx);
|
disassembler_sp->GetInstructionList().Dump(
|
||||||
|
&strm, show_address, show_bytes, show_control_flow_kind, &exe_ctx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -558,8 +558,9 @@ bool Symbol::GetDisassembly(const ExecutionContext &exe_ctx, const char *flavor,
|
|||||||
if (disassembler_sp) {
|
if (disassembler_sp) {
|
||||||
const bool show_address = true;
|
const bool show_address = true;
|
||||||
const bool show_bytes = false;
|
const bool show_bytes = false;
|
||||||
disassembler_sp->GetInstructionList().Dump(&strm, show_address, show_bytes,
|
const bool show_control_flow_kind = false;
|
||||||
&exe_ctx);
|
disassembler_sp->GetInstructionList().Dump(
|
||||||
|
&strm, show_address, show_bytes, show_control_flow_kind, &exe_ctx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -170,13 +170,14 @@ void ThreadPlanAssemblyTracer::Log() {
|
|||||||
if (instruction_list.GetSize()) {
|
if (instruction_list.GetSize()) {
|
||||||
const bool show_bytes = true;
|
const bool show_bytes = true;
|
||||||
const bool show_address = true;
|
const bool show_address = true;
|
||||||
|
const bool show_control_flow_kind = true;
|
||||||
Instruction *instruction =
|
Instruction *instruction =
|
||||||
instruction_list.GetInstructionAtIndex(0).get();
|
instruction_list.GetInstructionAtIndex(0).get();
|
||||||
const FormatEntity::Entry *disassemble_format =
|
const FormatEntity::Entry *disassemble_format =
|
||||||
m_process.GetTarget().GetDebugger().GetDisassemblyFormat();
|
m_process.GetTarget().GetDebugger().GetDisassemblyFormat();
|
||||||
instruction->Dump(stream, max_opcode_byte_size, show_address,
|
instruction->Dump(stream, max_opcode_byte_size, show_address,
|
||||||
show_bytes, nullptr, nullptr, nullptr,
|
show_bytes, show_control_flow_kind, nullptr, nullptr,
|
||||||
disassemble_format, 0);
|
nullptr, disassemble_format, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,14 +147,14 @@ public:
|
|||||||
m_s.Format("{0:x+16}", item.load_address);
|
m_s.Format("{0:x+16}", item.load_address);
|
||||||
if (item.symbol_info) {
|
if (item.symbol_info) {
|
||||||
m_s << " ";
|
m_s << " ";
|
||||||
item.symbol_info->instruction->Dump(&m_s, /*max_opcode_byte_size=*/0,
|
item.symbol_info->instruction->Dump(
|
||||||
/*show_address=*/false,
|
&m_s, /*max_opcode_byte_size=*/0,
|
||||||
/*show_bytes=*/false,
|
/*show_address=*/false,
|
||||||
&item.symbol_info->exe_ctx,
|
/*show_bytes=*/false, m_options.show_control_flow_kind,
|
||||||
&item.symbol_info->sc,
|
&item.symbol_info->exe_ctx, &item.symbol_info->sc,
|
||||||
/*prev_sym_ctx=*/nullptr,
|
/*prev_sym_ctx=*/nullptr,
|
||||||
/*disassembly_addr_format=*/nullptr,
|
/*disassembly_addr_format=*/nullptr,
|
||||||
/*max_address_text_size=*/0);
|
/*max_address_text_size=*/0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
lldb/unittests/Disassembler/ARM/CMakeLists.txt
Normal file
12
lldb/unittests/Disassembler/ARM/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
add_lldb_unittest(DisassemblerTests
|
||||||
|
TestArm64Disassembly.cpp
|
||||||
|
TestArmv7Disassembly.cpp
|
||||||
|
LINK_LIBS
|
||||||
|
lldbCore
|
||||||
|
lldbSymbol
|
||||||
|
lldbTarget
|
||||||
|
lldbPluginDisassemblerLLVMC
|
||||||
|
lldbPluginProcessUtility
|
||||||
|
LINK_COMPONENTS
|
||||||
|
Support
|
||||||
|
${LLVM_TARGETS_TO_BUILD})
|
@ -1,14 +1,7 @@
|
|||||||
if("ARM" IN_LIST LLVM_TARGETS_TO_BUILD)
|
if("ARM" IN_LIST LLVM_TARGETS_TO_BUILD)
|
||||||
add_lldb_unittest(DisassemblerTests
|
add_subdirectory(ARM)
|
||||||
TestArm64Disassembly.cpp
|
endif()
|
||||||
TestArmv7Disassembly.cpp
|
|
||||||
LINK_LIBS
|
if("X86" IN_LIST LLVM_TARGETS_TO_BUILD)
|
||||||
lldbCore
|
add_subdirectory(x86)
|
||||||
lldbSymbol
|
|
||||||
lldbTarget
|
|
||||||
lldbPluginDisassemblerLLVMC
|
|
||||||
lldbPluginProcessUtility
|
|
||||||
LINK_COMPONENTS
|
|
||||||
Support
|
|
||||||
${LLVM_TARGETS_TO_BUILD})
|
|
||||||
endif()
|
endif()
|
||||||
|
12
lldb/unittests/Disassembler/x86/CMakeLists.txt
Normal file
12
lldb/unittests/Disassembler/x86/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
add_lldb_unittest(GetControlFlowKindx86Tests
|
||||||
|
TestGetControlFlowKindx86.cpp
|
||||||
|
LINK_LIBS
|
||||||
|
lldbCore
|
||||||
|
lldbSymbol
|
||||||
|
lldbTarget
|
||||||
|
lldbPluginDisassemblerLLVMC
|
||||||
|
lldbPluginProcessUtility
|
||||||
|
LINK_COMPONENTS
|
||||||
|
Support
|
||||||
|
${LLVM_TARGETS_TO_BUILD}
|
||||||
|
)
|
144
lldb/unittests/Disassembler/x86/TestGetControlFlowKindx86.cpp
Normal file
144
lldb/unittests/Disassembler/x86/TestGetControlFlowKindx86.cpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
//===-- TextX86GetControlFlowKind.cpp ------------------------------------------===//
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "llvm/Support/TargetSelect.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "lldb/Core/Address.h"
|
||||||
|
#include "lldb/Core/Disassembler.h"
|
||||||
|
#include "lldb/Target/ExecutionContext.h"
|
||||||
|
#include "lldb/Utility/ArchSpec.h"
|
||||||
|
|
||||||
|
#include "Plugins/Disassembler/LLVMC/DisassemblerLLVMC.h"
|
||||||
|
|
||||||
|
using namespace lldb;
|
||||||
|
using namespace lldb_private;
|
||||||
|
|
||||||
|
class TestGetControlFlowKindx86 : public testing::Test {
|
||||||
|
public:
|
||||||
|
static void SetUpTestCase();
|
||||||
|
static void TearDownTestCase();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestGetControlFlowKindx86::SetUpTestCase() {
|
||||||
|
llvm::InitializeAllTargets();
|
||||||
|
llvm::InitializeAllAsmPrinters();
|
||||||
|
llvm::InitializeAllTargetMCs();
|
||||||
|
llvm::InitializeAllDisassemblers();
|
||||||
|
DisassemblerLLVMC::Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGetControlFlowKindx86::TearDownTestCase() {
|
||||||
|
DisassemblerLLVMC::Terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TestGetControlFlowKindx86, TestX86_64Instruction) {
|
||||||
|
ArchSpec arch("x86_64-*-linux");
|
||||||
|
|
||||||
|
const unsigned num_of_instructions = 29;
|
||||||
|
uint8_t data[] = {
|
||||||
|
0x55, // other -- pushq %rbp
|
||||||
|
0x48, 0x89, 0xe5, // other -- movq %rsp, %rbp
|
||||||
|
|
||||||
|
0xe8, 0xfc, 0xfe, 0xff, 0xff, // call -- callq 0x4004c0
|
||||||
|
0x41, 0xff, 0x14, 0xdc, // call -- callq *(%r12,%rbx,8)
|
||||||
|
0xff, 0x50, 0x18, // call -- callq *0x18(%rax)
|
||||||
|
0xe8, 0x48, 0x0d, 0x00, 0x00, // call -- callq 0x94fe0
|
||||||
|
|
||||||
|
0xc3, // return -- retq
|
||||||
|
|
||||||
|
0xeb, 0xd3, // jump -- jmp 0x92dab
|
||||||
|
0xe9, 0x22, 0xff, 0xff, 0xff, // jump -- jmp 0x933ae
|
||||||
|
0xff, 0xe0, // jump -- jmpq *%rax
|
||||||
|
0xf2, 0xff, 0x25, 0x75, 0xe7, 0x39, 0x00, // jump -- repne jmpq *0x39e775
|
||||||
|
|
||||||
|
0x73, 0xc2, // cond jump -- jae 0x9515c
|
||||||
|
0x74, 0x1f, // cond jump -- je 0x400626
|
||||||
|
0x75, 0xea, // cond jump -- jne 0x400610
|
||||||
|
0x76, 0x10, // cond jump -- jbe 0x94d10
|
||||||
|
0x77, 0x58, // cond jump -- ja 0x1208c8
|
||||||
|
0x7e, 0x67, // cond jump -- jle 0x92180
|
||||||
|
0x78, 0x0b, // cond jump -- js 0x92dc3
|
||||||
|
0x0f, 0x82, 0x17, 0x01, 0x00, 0x00, // cond jump -- jb 0x9c7b0
|
||||||
|
0x0f, 0x83, 0xa7, 0x00, 0x00, 0x00, // cond jump -- jae 0x895c8
|
||||||
|
0x0f, 0x84, 0x8c, 0x00, 0x00, 0x00, // cond jump -- je 0x941f0
|
||||||
|
0x0f, 0x85, 0x51, 0xff, 0xff, 0xff, // cond jump -- jne 0x8952c
|
||||||
|
0x0f, 0x86, 0xa3, 0x02, 0x00, 0x00, // cond jump -- jbe 0x9ae10
|
||||||
|
0x0f, 0x87, 0xff, 0x00, 0x00, 0x00, // cond jump -- ja 0x9ab60
|
||||||
|
0x0f, 0x8e, 0x7e, 0x00, 0x00, 0x00, // cond jump -- jle 0x92dd8
|
||||||
|
0x0f, 0x86, 0xdf, 0x00, 0x00, 0x00, // cond jump -- jbe 0x921b0
|
||||||
|
|
||||||
|
0x0f, 0x05, // far call -- syscall
|
||||||
|
|
||||||
|
0x0f, 0x07, // far return -- sysret
|
||||||
|
0xcf, // far return -- interrupt ret
|
||||||
|
};
|
||||||
|
|
||||||
|
InstructionControlFlowKind result[] = {
|
||||||
|
eInstructionControlFlowKindOther,
|
||||||
|
eInstructionControlFlowKindOther,
|
||||||
|
|
||||||
|
eInstructionControlFlowKindCall,
|
||||||
|
eInstructionControlFlowKindCall,
|
||||||
|
eInstructionControlFlowKindCall,
|
||||||
|
eInstructionControlFlowKindCall,
|
||||||
|
|
||||||
|
eInstructionControlFlowKindReturn,
|
||||||
|
|
||||||
|
eInstructionControlFlowKindJump,
|
||||||
|
eInstructionControlFlowKindJump,
|
||||||
|
eInstructionControlFlowKindJump,
|
||||||
|
eInstructionControlFlowKindJump,
|
||||||
|
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
eInstructionControlFlowKindCondJump,
|
||||||
|
|
||||||
|
eInstructionControlFlowKindFarCall,
|
||||||
|
|
||||||
|
eInstructionControlFlowKindFarReturn,
|
||||||
|
eInstructionControlFlowKindFarReturn,
|
||||||
|
};
|
||||||
|
|
||||||
|
DisassemblerSP disass_sp;
|
||||||
|
Address start_addr(0x100);
|
||||||
|
disass_sp =
|
||||||
|
Disassembler::DisassembleBytes(arch, nullptr, nullptr, start_addr, &data,
|
||||||
|
sizeof (data), num_of_instructions, false);
|
||||||
|
|
||||||
|
// If we failed to get a disassembler, we can assume it is because
|
||||||
|
// the llvm we linked against was not built with the i386 target,
|
||||||
|
// and we should skip these tests without marking anything as failing.
|
||||||
|
|
||||||
|
if (disass_sp) {
|
||||||
|
const InstructionList inst_list(disass_sp->GetInstructionList());
|
||||||
|
EXPECT_EQ(num_of_instructions, inst_list.GetSize());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_of_instructions; ++i) {
|
||||||
|
InstructionSP inst_sp;
|
||||||
|
inst_sp = inst_list.GetInstructionAtIndex(i);
|
||||||
|
InstructionControlFlowKind kind = inst_sp->GetControlFlowKind(arch);
|
||||||
|
EXPECT_EQ(kind, result[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user