diff options
Diffstat (limited to 'source/Plugins')
175 files changed, 83931 insertions, 0 deletions
diff --git a/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp new file mode 100644 index 000000000000..4685c3e759e0 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp @@ -0,0 +1,861 @@ +//===-- ABIMacOSX_arm.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABIMacOSX_arm.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_GCC_Registers.h" +#include "Plugins/Process/Utility/ARMDefines.h" + +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ========== ======= == === ============= ============ ======================= =================== =========================== ======================= ====================== ========== =============== + { "r0", "arg1", 4, 0, eEncodingUint , eFormatHex, { gcc_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, gdb_arm_r0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r1", "arg2", 4, 0, eEncodingUint , eFormatHex, { gcc_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, gdb_arm_r1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r2", "arg3", 4, 0, eEncodingUint , eFormatHex, { gcc_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, gdb_arm_r2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r3", "arg4", 4, 0, eEncodingUint , eFormatHex, { gcc_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, gdb_arm_r3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r4", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM, gdb_arm_r4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r5", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM, gdb_arm_r5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r6", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM, gdb_arm_r6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r7", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, gdb_arm_r7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM, gdb_arm_r8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM, gdb_arm_r9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM, gdb_arm_r10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM, gdb_arm_r11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM, gdb_arm_r12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "sp", "r13", 4, 0, eEncodingUint , eFormatHex, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, gdb_arm_sp, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "lr", "r14", 4, 0, eEncodingUint , eFormatHex, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, gdb_arm_lr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "pc", "r15", 4, 0, eEncodingUint , eFormatHex, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, gdb_arm_pc, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cpsr", "psr", 4, 0, eEncodingUint , eFormatHex, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, gdb_arm_cpsr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s0", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, gdb_arm_s0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s1", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, gdb_arm_s1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s2", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, gdb_arm_s2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s3", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, gdb_arm_s3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s4", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, gdb_arm_s4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s5", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, gdb_arm_s5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s6", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, gdb_arm_s6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s7", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, gdb_arm_s7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s8", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, gdb_arm_s8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s9", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, gdb_arm_s9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s10", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, gdb_arm_s10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s11", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, gdb_arm_s11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s12", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, gdb_arm_s12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s13", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, gdb_arm_s13, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s14", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, gdb_arm_s14, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s15", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, gdb_arm_s15, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s16", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, gdb_arm_s16, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s17", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, gdb_arm_s17, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s18", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, gdb_arm_s18, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s19", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, gdb_arm_s19, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s20", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, gdb_arm_s20, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s21", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, gdb_arm_s21, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s22", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, gdb_arm_s22, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s23", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, gdb_arm_s23, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s24", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, gdb_arm_s24, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s25", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, gdb_arm_s25, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s26", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, gdb_arm_s26, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s27", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, gdb_arm_s27, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s28", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, gdb_arm_s28, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s29", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, gdb_arm_s29, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s30", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, gdb_arm_s30, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s31", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, gdb_arm_s31, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fpscr", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, gdb_arm_fpscr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d0", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, gdb_arm_d0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d1", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, gdb_arm_d1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d2", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, gdb_arm_d2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d3", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, gdb_arm_d3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d4", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, gdb_arm_d4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d5", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, gdb_arm_d5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d6", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, gdb_arm_d6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d7", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, gdb_arm_d7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d8", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, gdb_arm_d8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d9", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, gdb_arm_d9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d10", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, gdb_arm_d10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d11", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, gdb_arm_d11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d12", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, gdb_arm_d12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d13", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, gdb_arm_d13, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d14", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, gdb_arm_d14, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d15", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, gdb_arm_d15, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d16", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, gdb_arm_d16, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d17", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, gdb_arm_d17, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d18", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, gdb_arm_d18, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d19", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, gdb_arm_d19, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d20", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, gdb_arm_d20, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d21", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, gdb_arm_d21, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d22", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, gdb_arm_d22, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d23", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, gdb_arm_d23, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d24", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, gdb_arm_d24, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d25", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, gdb_arm_d25, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d26", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, gdb_arm_d26, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d27", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, gdb_arm_d27, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d28", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, gdb_arm_d28, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d29", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, gdb_arm_d29, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d30", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, gdb_arm_d30, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d31", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, gdb_arm_d31, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r8_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r9_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r10_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r11_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r12_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_usr", "sp_usr", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_usr", "lr_usr", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r8_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r9_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r10_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r11_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r12_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_fiq", "sp_fiq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_fiq", "lr_fiq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_irq", "sp_irq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_irq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_irq", "lr_irq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_irq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_abt", "sp_abt", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_abt, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_abt", "lr_abt", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_abt, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_und", "sp_und", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_und, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_und", "lr_und", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_und, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_svc", "sp_svc", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_svc, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_svc", "lr_svc", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_svc, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL} +}; +static const uint32_t k_num_register_infos = sizeof(g_register_infos)/sizeof(RegisterInfo); +static bool g_register_info_names_constified = false; + +const lldb_private::RegisterInfo * +ABIMacOSX_arm::GetRegisterInfoArray (uint32_t &count) +{ + // Make the C-string names and alt_names for the register infos into const + // C-string values by having the ConstString unique the names in the global + // constant C-string pool. + if (!g_register_info_names_constified) + { + g_register_info_names_constified = true; + for (uint32_t i=0; i<k_num_register_infos; ++i) + { + if (g_register_infos[i].name) + g_register_infos[i].name = ConstString(g_register_infos[i].name).GetCString(); + if (g_register_infos[i].alt_name) + g_register_infos[i].alt_name = ConstString(g_register_infos[i].alt_name).GetCString(); + } + } + count = k_num_register_infos; + return g_register_infos; +} + + +size_t +ABIMacOSX_arm::GetRedZoneSize () const +{ + return 0; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +ABISP +ABIMacOSX_arm::CreateInstance (const ArchSpec &arch) +{ + static ABISP g_abi_sp; + const llvm::Triple::ArchType arch_type = arch.GetTriple().getArch(); + if ((arch_type == llvm::Triple::arm) || + (arch_type == llvm::Triple::thumb)) + { + if (!g_abi_sp) + g_abi_sp.reset (new ABIMacOSX_arm); + return g_abi_sp; + } + return ABISP(); +} + +bool +ABIMacOSX_arm::PrepareTrivialCall (Thread &thread, + addr_t sp, + addr_t function_addr, + addr_t return_addr, + addr_t *arg1_ptr, + addr_t *arg2_ptr, + addr_t *arg3_ptr, + addr_t *arg4_ptr, + addr_t *arg5_ptr, + addr_t *arg6_ptr) const +{ + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + const uint32_t ra_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + + RegisterValue reg_value; + + if (arg1_ptr) + { + reg_value.SetUInt32(*arg1_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r0"), reg_value)) + return false; + + if (arg2_ptr) + { + reg_value.SetUInt32(*arg2_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r1"), reg_value)) + return false; + + if (arg3_ptr) + { + reg_value.SetUInt32(*arg3_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r2"), reg_value)) + return false; + if (arg4_ptr) + { + reg_value.SetUInt32(*arg4_ptr); + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3"); + if (!reg_ctx->WriteRegister (reg_info, reg_value)) + return false; + if (arg5_ptr) + { + // Keep the stack 8 byte aligned, not that we need to + sp -= 8; + sp &= ~(8ull-1ull); + reg_value.SetUInt32(*arg5_ptr); + if (reg_ctx->WriteRegisterValueToMemory (reg_info, sp, reg_info->byte_size, reg_value).Fail()) + return false; + if (arg6_ptr) + { + reg_value.SetUInt32(*arg6_ptr); + if (reg_ctx->WriteRegisterValueToMemory (reg_info, sp + 4, reg_info->byte_size, reg_value).Fail()) + return false; + } + } + } + } + } + } + + + TargetSP target_sp (thread.CalculateTarget()); + Address so_addr; + + // Figure out if our return address is ARM or Thumb by using the + // Address::GetCallableLoadAddress(Target*) which will figure out the ARM + // thumb-ness and set the correct address bits for us. + so_addr.SetLoadAddress (return_addr, target_sp.get()); + return_addr = so_addr.GetCallableLoadAddress (target_sp.get()); + + // Set "lr" to the return address + if (!reg_ctx->WriteRegisterFromUnsigned (ra_reg_num, return_addr)) + return false; + + // Set "sp" to the requested value + if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_num, sp)) + return false; + + // If bit zero or 1 is set, this must be a thumb function, no need to figure + // this out from the symbols. + so_addr.SetLoadAddress (function_addr, target_sp.get()); + function_addr = so_addr.GetCallableLoadAddress (target_sp.get()); + + const RegisterInfo *cpsr_reg_info = reg_ctx->GetRegisterInfoByName("cpsr"); + const uint32_t curr_cpsr = reg_ctx->ReadRegisterAsUnsigned(cpsr_reg_info, 0); + + // Make a new CPSR and mask out any Thumb IT (if/then) bits + uint32_t new_cpsr = curr_cpsr & ~MASK_CPSR_IT_MASK; + // If bit zero or 1 is set, this must be thumb... + if (function_addr & 1ull) + new_cpsr |= MASK_CPSR_T; // Set T bit in CPSR + else + new_cpsr &= ~MASK_CPSR_T; // Clear T bit in CPSR + + if (new_cpsr != curr_cpsr) + { + if (!reg_ctx->WriteRegisterFromUnsigned (cpsr_reg_info, new_cpsr)) + return false; + } + + function_addr &= ~1ull; // clear bit zero since the CPSR will take care of the mode for us + + // Set "pc" to the address requested + if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_num, function_addr)) + return false; + + return true; +} + +bool +ABIMacOSX_arm::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + uint32_t num_values = values.GetSize(); + + + ExecutionContext exe_ctx (thread.shared_from_this()); + // For now, assume that the types in the AST values come from the Target's + // scratch AST. + + // Extract the register context so we can read arguments from registers + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + if (!reg_ctx) + return false; + + addr_t sp = 0; + + for (uint32_t value_idx = 0; value_idx < num_values; ++value_idx) + { + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + Value *value = values.GetValueAtIndex(value_idx); + + if (!value) + return false; + + ClangASTType clang_type = value->GetClangType(); + if (clang_type) + { + bool is_signed = false; + size_t bit_width = 0; + if (clang_type.IsIntegerType (is_signed)) + { + bit_width = clang_type.GetBitSize(); + } + else if (clang_type.IsPointerOrReferenceType ()) + { + bit_width = clang_type.GetBitSize(); + } + else + { + // We only handle integer, pointer and reference types currently... + return false; + } + + if (bit_width <= (exe_ctx.GetProcessRef().GetAddressByteSize() * 8)) + { + if (value_idx < 4) + { + // Arguments 1-4 are in r0-r3... + const RegisterInfo *arg_reg_info = NULL; + // Search by generic ID first, then fall back to by name + uint32_t arg_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + value_idx); + if (arg_reg_num != LLDB_INVALID_REGNUM) + { + arg_reg_info = reg_ctx->GetRegisterInfoAtIndex(arg_reg_num); + } + else + { + switch (value_idx) + { + case 0: arg_reg_info = reg_ctx->GetRegisterInfoByName("r0"); break; + case 1: arg_reg_info = reg_ctx->GetRegisterInfoByName("r1"); break; + case 2: arg_reg_info = reg_ctx->GetRegisterInfoByName("r2"); break; + case 3: arg_reg_info = reg_ctx->GetRegisterInfoByName("r3"); break; + } + } + + if (arg_reg_info) + { + RegisterValue reg_value; + + if (reg_ctx->ReadRegister(arg_reg_info, reg_value)) + { + if (is_signed) + reg_value.SignExtend(bit_width); + if (!reg_value.GetScalarValue(value->GetScalar())) + return false; + continue; + } + } + return false; + } + else + { + if (sp == 0) + { + // Read the stack pointer if it already hasn't been read + sp = reg_ctx->GetSP(0); + if (sp == 0) + return false; + } + + // Arguments 5 on up are on the stack + const uint32_t arg_byte_size = (bit_width + (8-1)) / 8; + Error error; + if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(sp, arg_byte_size, is_signed, value->GetScalar(), error)) + return false; + + sp += arg_byte_size; + } + } + } + } + return true; +} + +ValueObjectSP +ABIMacOSX_arm::GetReturnValueObjectImpl (Thread &thread, + lldb_private::ClangASTType &clang_type) const +{ + Value value; + ValueObjectSP return_valobj_sp; + + if (!clang_type) + return return_valobj_sp; + + clang::ASTContext *ast_context = clang_type.GetASTContext(); + if (!ast_context) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, clang_type.GetOpaqueQualType()); + value.SetClangType (clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + bool is_signed; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + const RegisterInfo *r0_reg_info = reg_ctx->GetRegisterInfoByName("r0", 0); + if (clang_type.IsIntegerType (is_signed)) + { + size_t bit_width = clang_type.GetBitSize(); + + switch (bit_width) + { + default: + return return_valobj_sp; + case 64: + { + const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfoByName("r1", 0); + uint64_t raw_value; + raw_value = reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX; + raw_value |= ((uint64_t)(reg_ctx->ReadRegisterAsUnsigned(r1_reg_info, 0) & UINT32_MAX)) << 32; + if (is_signed) + value.GetScalar() = (int64_t)raw_value; + else + value.GetScalar() = (uint64_t)raw_value; + } + break; + case 32: + if (is_signed) + value.GetScalar() = (int32_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX); + else + value.GetScalar() = (uint32_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX); + break; + case 16: + if (is_signed) + value.GetScalar() = (int16_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX); + else + value.GetScalar() = (uint16_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT16_MAX); + break; + case 8: + if (is_signed) + value.GetScalar() = (int8_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX); + else + value.GetScalar() = (uint8_t)(reg_ctx->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT8_MAX); + break; + } + } + else if (clang_type.IsPointerType ()) + { + uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX; + value.GetScalar() = ptr; + } + else + { + // not handled yet + return return_valobj_sp; + } + + // If we get here, we have a valid Value, so make our ValueObject out of it: + + return_valobj_sp = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + return return_valobj_sp; +} + +Error +ABIMacOSX_arm::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_type) + { + error.SetErrorString ("Null clang type for return value."); + return error; + } + + Thread *thread = frame_sp->GetThread().get(); + + bool is_signed; + uint32_t count; + bool is_complex; + + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + + bool set_it_simple = false; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + lldb::offset_t offset = 0; + if (num_bytes <= 8) + { + const RegisterInfo *r0_info = reg_ctx->GetRegisterInfoByName("r0", 0); + if (num_bytes <= 4) + { + uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); + + if (reg_ctx->WriteRegisterFromUnsigned (r0_info, raw_value)) + set_it_simple = true; + } + else + { + uint32_t raw_value = data.GetMaxU32(&offset, 4); + + if (reg_ctx->WriteRegisterFromUnsigned (r0_info, raw_value)) + { + const RegisterInfo *r1_info = reg_ctx->GetRegisterInfoByName("r1", 0); + uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset); + + if (reg_ctx->WriteRegisterFromUnsigned (r1_info, raw_value)) + set_it_simple = true; + } + } + } + else + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + } + else if (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + error.SetErrorString ("We don't support returning float values at present"); + } + + if (!set_it_simple) + error.SetErrorString ("We only support setting simple integer return types at present."); + + return error; +} + +bool +ABIMacOSX_arm::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t lr_reg_num = LLDB_INVALID_REGNUM; + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + lr_reg_num = dwarf_lr; + sp_reg_num = dwarf_sp; + pc_reg_num = dwarf_pc; + break; + + case eRegisterKindGeneric: + lr_reg_num = LLDB_REGNUM_GENERIC_RA; + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (lr_reg_num == LLDB_INVALID_REGNUM || + sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our Call Frame Address is the stack pointer value + row->SetCFARegister (sp_reg_num); + + // The previous PC is in the LR + row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true); + unwind_plan.AppendRow (row); + + // All other registers are the same. + + unwind_plan.SetSourceName ("arm at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + + return true; +} + +bool +ABIMacOSX_arm::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t fp_reg_num = dwarf_r7; // apple uses r7 for all frames. Normal arm uses r11; + uint32_t pc_reg_num = dwarf_pc; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + const int32_t ptr_size = 4; + + unwind_plan.Clear (); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + row->SetCFARegister (fp_reg_num); + row->SetCFAOffset (2 * ptr_size); + row->SetOffset (0); + + row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); + + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("arm-apple-ios default unwind plan"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + + return true; +} + +// ARMv7 on iOS general purpose reg rules: +// r0-r3 not preserved (used for argument passing) +// r4-r6 preserved +// r7 preserved (frame pointer) +// r8 preserved +// r9 not preserved (usable as volatile scratch register with iOS 3.x and later) +// r10-r11 preserved +// r12 not presrved +// r13 preserved (stack pointer) +// r14 not preserved (link register) +// r15 preserved (pc) +// cpsr not preserved (different rules for different bits) + +// ARMv7 on iOS floating point rules: +// d0-d7 not preserved (aka s0-s15, q0-q3) +// d8-d15 preserved (aka s16-s31, q4-q7) +// d16-d31 not preserved (aka q8-q15) + +bool +ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Volatile registers include: r0, r1, r2, r3, r9, r12, r13 + const char *name = reg_info->name; + if (name[0] == 'r') + { + switch (name[1]) + { + case '0': return name[2] == '\0'; // r0 + case '1': + switch (name[2]) + { + case '\0': + return true; // r1 + case '2': + case '3': + return name[2] == '\0'; // r12 - r13 + default: + break; + } + break; + + case '2': return name[2] == '\0'; // r2 + case '3': return name[2] == '\0'; // r3 + case '9': return name[2] == '\0'; // r9 (apple-ios only...) + + break; + } + } + else if (name[0] == 'd') + { + switch (name[1]) + { + case '0': + return name[2] == '\0'; // d0 is volatile + + case '1': + switch (name[2]) + { + case '\0': + return true; // d1 is volatile + case '6': + case '7': + case '8': + case '9': + return name[3] == '\0'; // d16 - d19 are volatile + default: + break; + } + break; + + case '2': + switch (name[2]) + { + case '\0': + return true; // d2 is volatile + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return name[3] == '\0'; // d20 - d29 are volatile + default: + break; + } + break; + + case '3': + switch (name[2]) + { + case '\0': + return true; // d3 is volatile + case '0': + case '1': + return name[3] == '\0'; // d30 - d31 are volatile + default: + break; + } + case '4': + case '5': + case '6': + case '7': + return name[2] == '\0'; // d4 - d7 are volatile + + default: + break; + } + } + else if (name[0] == 's') + { + switch (name[1]) + { + case '0': + return name[2] == '\0'; // s0 is volatile + + case '1': + switch (name[2]) + { + case '\0': + return true; // s1 is volatile + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + return name[3] == '\0'; // s10 - s15 are volatile + default: + break; + } + break; + + case '2': + switch (name[2]) + { + case '\0': + return true; // s2 is volatile + default: + break; + } + break; + + case '3': + switch (name[2]) + { + case '\0': + return true; // s3 is volatile + default: + break; + } + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return name[2] == '\0'; // s4 - s9 are volatile + + default: + break; + } + } + else if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') + return true; + } + return false; +} + +void +ABIMacOSX_arm::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "Mac OS X ABI for arm targets", + CreateInstance); +} + +void +ABIMacOSX_arm::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ABIMacOSX_arm::GetPluginNameStatic() +{ + static ConstString g_name("macosx-arm"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ABIMacOSX_arm::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABIMacOSX_arm::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h new file mode 100644 index 000000000000..27cea85aaf6f --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h @@ -0,0 +1,138 @@ +//===-- ABIMacOSX_arm.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABIMacOSX_arm_h_ +#define liblldb_ABIMacOSX_arm_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABIMacOSX_arm : public lldb_private::ABI +{ +public: + ~ABIMacOSX_arm() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t returnAddress, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // Make sure the stack call frame addresses are are 4 byte aligned + if (cfa & (4ull - 1ull)) + return false; // Not 4 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // Just make sure the address is a valid 32 bit address. Bit zero + // might be set due to Thumb function calls, so don't enforce 2 byte + // alignment + return pc <= UINT32_MAX; + } + + virtual lldb::addr_t + FixCodeAddress (lldb::addr_t pc) + { + // ARM uses bit zero to signify a code address is thumb, so we must + // strip bit zero in any code addresses. + return pc & ~(lldb::addr_t)1; + } + + virtual bool + FunctionCallsChangeCFA () + { + return false; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: +private: + ABIMacOSX_arm() : + lldb_private::ABI() + { + // Call CreateInstance instead. + } +}; + +#endif // liblldb_ABIMacOSX_arm_h_ diff --git a/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp new file mode 100644 index 000000000000..deb531d937a0 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp @@ -0,0 +1,977 @@ +//===-- ABIMacOSX_i386.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABIMacOSX_i386.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gcc_eax = 0, + gcc_ecx, + gcc_edx, + gcc_ebx, + gcc_ebp, + gcc_esp, + gcc_esi, + gcc_edi, + gcc_eip, + gcc_eflags +}; + +enum +{ + dwarf_eax = 0, + dwarf_ecx, + dwarf_edx, + dwarf_ebx, + dwarf_esp, + dwarf_ebp, + dwarf_esi, + dwarf_edi, + dwarf_eip, + dwarf_eflags, + dwarf_stmm0 = 11, + dwarf_stmm1, + dwarf_stmm2, + dwarf_stmm3, + dwarf_stmm4, + dwarf_stmm5, + dwarf_stmm6, + dwarf_stmm7, + dwarf_xmm0 = 21, + dwarf_xmm1, + dwarf_xmm2, + dwarf_xmm3, + dwarf_xmm4, + dwarf_xmm5, + dwarf_xmm6, + dwarf_xmm7, + dwarf_ymm0 = dwarf_xmm0, + dwarf_ymm1 = dwarf_xmm1, + dwarf_ymm2 = dwarf_xmm2, + dwarf_ymm3 = dwarf_xmm3, + dwarf_ymm4 = dwarf_xmm4, + dwarf_ymm5 = dwarf_xmm5, + dwarf_ymm6 = dwarf_xmm6, + dwarf_ymm7 = dwarf_xmm7 +}; + +enum +{ + gdb_eax = 0, + gdb_ecx = 1, + gdb_edx = 2, + gdb_ebx = 3, + gdb_esp = 4, + gdb_ebp = 5, + gdb_esi = 6, + gdb_edi = 7, + gdb_eip = 8, + gdb_eflags = 9, + gdb_cs = 10, + gdb_ss = 11, + gdb_ds = 12, + gdb_es = 13, + gdb_fs = 14, + gdb_gs = 15, + gdb_stmm0 = 16, + gdb_stmm1 = 17, + gdb_stmm2 = 18, + gdb_stmm3 = 19, + gdb_stmm4 = 20, + gdb_stmm5 = 21, + gdb_stmm6 = 22, + gdb_stmm7 = 23, + gdb_fctrl = 24, gdb_fcw = gdb_fctrl, + gdb_fstat = 25, gdb_fsw = gdb_fstat, + gdb_ftag = 26, gdb_ftw = gdb_ftag, + gdb_fiseg = 27, gdb_fpu_cs = gdb_fiseg, + gdb_fioff = 28, gdb_ip = gdb_fioff, + gdb_foseg = 29, gdb_fpu_ds = gdb_foseg, + gdb_fooff = 30, gdb_dp = gdb_fooff, + gdb_fop = 31, + gdb_xmm0 = 32, + gdb_xmm1 = 33, + gdb_xmm2 = 34, + gdb_xmm3 = 35, + gdb_xmm4 = 36, + gdb_xmm5 = 37, + gdb_xmm6 = 38, + gdb_xmm7 = 39, + gdb_mxcsr = 40, + gdb_mm0 = 41, + gdb_mm1 = 42, + gdb_mm2 = 43, + gdb_mm3 = 44, + gdb_mm4 = 45, + gdb_mm5 = 46, + gdb_mm6 = 47, + gdb_mm7 = 48, + gdb_ymm0 = gdb_xmm0, + gdb_ymm1 = gdb_xmm1, + gdb_ymm2 = gdb_xmm2, + gdb_ymm3 = gdb_xmm3, + gdb_ymm4 = gdb_xmm4, + gdb_ymm5 = gdb_xmm5, + gdb_ymm6 = gdb_xmm6, + gdb_ymm7 = gdb_xmm7 +}; + + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ====== ======= == === ============= ============ ===================== ===================== ============================ ==================== ====================== ========== =============== + { "eax", NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_eax , dwarf_eax , LLDB_INVALID_REGNUM , gdb_eax , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ebx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_ebx , dwarf_ebx , LLDB_INVALID_REGNUM , gdb_ebx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ecx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_ecx , dwarf_ecx , LLDB_REGNUM_GENERIC_ARG4 , gdb_ecx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "edx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_edx , dwarf_edx , LLDB_REGNUM_GENERIC_ARG3 , gdb_edx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "esi" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_esi , dwarf_esi , LLDB_REGNUM_GENERIC_ARG2 , gdb_esi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "edi" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_edi , dwarf_edi , LLDB_REGNUM_GENERIC_ARG1 , gdb_edi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ebp" , "fp", 4, 0, eEncodingUint , eFormatHex , { gcc_ebp , dwarf_ebp , LLDB_REGNUM_GENERIC_FP , gdb_ebp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "esp" , "sp", 4, 0, eEncodingUint , eFormatHex , { gcc_esp , dwarf_esp , LLDB_REGNUM_GENERIC_SP , gdb_esp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "eip" , "pc", 4, 0, eEncodingUint , eFormatHex , { gcc_eip , dwarf_eip , LLDB_REGNUM_GENERIC_PC , gdb_eip , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "eflags", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_eflags , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ss" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ds" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "es" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "gs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm0" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm0 , LLDB_INVALID_REGNUM , gdb_stmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm1" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm1 , LLDB_INVALID_REGNUM , gdb_stmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm2" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm2 , LLDB_INVALID_REGNUM , gdb_stmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm3" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm3 , LLDB_INVALID_REGNUM , gdb_stmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm4" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm4 , LLDB_INVALID_REGNUM , gdb_stmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm5" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm5 , LLDB_INVALID_REGNUM , gdb_stmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm6" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm6 , LLDB_INVALID_REGNUM , gdb_stmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm7" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm7 , LLDB_INVALID_REGNUM , gdb_stmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fctrl" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fctrl , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fstat" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fstat , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ftag" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftag , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fiseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fiseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fioff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fioff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "foseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_foseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fooff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fooff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fop" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm0" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm0 , LLDB_INVALID_REGNUM , gdb_xmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm1" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm1 , LLDB_INVALID_REGNUM , gdb_xmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm2" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm2 , LLDB_INVALID_REGNUM , gdb_xmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm3" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm3 , LLDB_INVALID_REGNUM , gdb_xmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm4" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm4 , LLDB_INVALID_REGNUM , gdb_xmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm5" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm5 , LLDB_INVALID_REGNUM , gdb_xmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm6" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm6 , LLDB_INVALID_REGNUM , gdb_xmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm7" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm7 , LLDB_INVALID_REGNUM , gdb_xmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "mxcsr" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm0" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm0 , LLDB_INVALID_REGNUM , gdb_ymm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm1" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm1 , LLDB_INVALID_REGNUM , gdb_ymm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm2" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm2 , LLDB_INVALID_REGNUM , gdb_ymm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm3" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm3 , LLDB_INVALID_REGNUM , gdb_ymm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm4" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm4 , LLDB_INVALID_REGNUM , gdb_ymm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm5" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm5 , LLDB_INVALID_REGNUM , gdb_ymm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm6" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm6 , LLDB_INVALID_REGNUM , gdb_ymm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm7" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm7 , LLDB_INVALID_REGNUM , gdb_ymm7 , LLDB_INVALID_REGNUM }, NULL, NULL} +}; + +static const uint32_t k_num_register_infos = sizeof(g_register_infos)/sizeof(RegisterInfo); +static bool g_register_info_names_constified = false; + +const lldb_private::RegisterInfo * +ABIMacOSX_i386::GetRegisterInfoArray (uint32_t &count) +{ + // Make the C-string names and alt_names for the register infos into const + // C-string values by having the ConstString unique the names in the global + // constant C-string pool. + if (!g_register_info_names_constified) + { + g_register_info_names_constified = true; + for (uint32_t i=0; i<k_num_register_infos; ++i) + { + if (g_register_infos[i].name) + g_register_infos[i].name = ConstString(g_register_infos[i].name).GetCString(); + if (g_register_infos[i].alt_name) + g_register_infos[i].alt_name = ConstString(g_register_infos[i].alt_name).GetCString(); + } + } + count = k_num_register_infos; + return g_register_infos; +} + +size_t +ABIMacOSX_i386::GetRedZoneSize () const +{ + return 0; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +ABISP +ABIMacOSX_i386::CreateInstance (const ArchSpec &arch) +{ + static ABISP g_abi_sp; + if (arch.GetTriple().getArch() == llvm::Triple::x86) + { + if (!g_abi_sp) + g_abi_sp.reset (new ABIMacOSX_i386); + return g_abi_sp; + } + return ABISP(); +} + +bool +ABIMacOSX_i386::PrepareTrivialCall (Thread &thread, + addr_t sp, + addr_t func_addr, + addr_t return_addr, + addr_t *arg1_ptr, + addr_t *arg2_ptr, + addr_t *arg3_ptr, + addr_t *arg4_ptr, + addr_t *arg5_ptr, + addr_t *arg6_ptr) const +{ + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // When writing a register value down to memory, the register info used + // to write memory just needs to have the correct size of a 32 bit register, + // the actual register it pertains to is not important, just the size needs + // to be correct. Here we use "eax"... + const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax"); + if (!reg_info_32) + return false; // TODO this should actually never happen + + // Make room for the argument(s) on the stack + + Error error; + RegisterValue reg_value; + + // Write any arguments onto the stack + if (arg1_ptr) + { + sp -= 4; + if (arg2_ptr) + { + sp -= 4; + if (arg3_ptr) + { + sp -= 4; + if (arg4_ptr) + { + sp -= 4; + if (arg5_ptr) + { + sp -= 4; + if (arg6_ptr) + { + sp -= 4; + } + } + } + } + } + } + + // Align the SP + sp &= ~(16ull-1ull); // 16-byte alignment + + if (arg1_ptr) + { + reg_value.SetUInt32(*arg1_ptr); + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg2_ptr) + { + reg_value.SetUInt32(*arg2_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 4, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg3_ptr) + { + reg_value.SetUInt32(*arg3_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 8, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg4_ptr) + { + reg_value.SetUInt32(*arg4_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 12, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + if (arg5_ptr) + { + reg_value.SetUInt32(*arg5_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 16, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + if (arg6_ptr) + { + reg_value.SetUInt32(*arg6_ptr); + // The register info used to write memory just needs to have the correct + // size of a 32 bit register, the actual register it pertains to is not + // important, just the size needs to be correct. Here we use "eax"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 20, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + } + } + } + } + } + } + + + // The return address is pushed onto the stack (yes after we just set the + // alignment above!). + sp -= 4; + reg_value.SetUInt32(return_addr); + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_num, sp)) + return false; + + // %eip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_num, func_addr)) + return false; + + return true; +} + +bool +ABIMacOSX_i386::PrepareNormalCall (Thread &thread, + addr_t sp, + addr_t func_addr, + addr_t return_addr, + ValueList &args) const +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + Process *process = exe_ctx.GetProcessPtr(); + Error error; + uint32_t fp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + + // Do the argument layout + + std::vector <uint32_t> argLayout; // 4-byte chunks, as discussed in the ABI Function Call Guide + + size_t numArgs = args.GetSize(); + size_t index; + + for (index = 0; index < numArgs; ++index) + { + Value *val = args.GetValueAtIndex(index); + + if (!val) + return false; + + switch (val->GetValueType()) + { + case Value::eValueTypeScalar: + { + Scalar &scalar = val->GetScalar(); + switch (scalar.GetType()) + { + case Scalar::e_void: + return false; + case Scalar::e_sint: + case Scalar::e_uint: + case Scalar::e_slong: + case Scalar::e_ulong: + case Scalar::e_slonglong: + case Scalar::e_ulonglong: + { + uint64_t data = scalar.ULongLong(); + + switch (scalar.GetByteSize()) + { + default: + return false; + case 1: + argLayout.push_back((uint32_t)(data & 0xffull)); + break; + case 2: + argLayout.push_back((uint32_t)(data & 0xffffull)); + break; + case 4: + argLayout.push_back((uint32_t)(data & 0xffffffffull)); + break; + case 8: + argLayout.push_back((uint32_t)(data & 0xffffffffull)); + argLayout.push_back((uint32_t)(data >> 32)); + break; + } + } + break; + case Scalar::e_float: + { + float data = scalar.Float(); + uint32_t dataRaw = *((uint32_t*)(&data)); + argLayout.push_back(dataRaw); + } + break; + case Scalar::e_double: + { + double data = scalar.Double(); + uint32_t *dataRaw = ((uint32_t*)(&data)); + argLayout.push_back(dataRaw[0]); + argLayout.push_back(dataRaw[1]); + } + break; + case Scalar::e_long_double: + { + long double data = scalar.Double(); + uint32_t *dataRaw = ((uint32_t*)(&data)); + while ((argLayout.size() * 4) & 0xf) + argLayout.push_back(0); + argLayout.push_back(dataRaw[0]); + argLayout.push_back(dataRaw[1]); + argLayout.push_back(dataRaw[2]); + argLayout.push_back(dataRaw[3]); + } + break; + } + } + break; + case Value::eValueTypeHostAddress: + { + ClangASTType clang_type (val->GetClangType()); + if (clang_type) + { + uint32_t cstr_length = 0; + if (clang_type.IsCStringType (cstr_length)) + { + const char *cstr = (const char*)val->GetScalar().ULongLong(); + cstr_length = strlen(cstr); + + // Push the string onto the stack immediately. + + sp -= (cstr_length + 1); + + if (process->WriteMemory(sp, cstr, cstr_length + 1, error) != (cstr_length + 1)) + return false; + + // Put the address of the string into the argument array. + + argLayout.push_back((uint32_t)(sp & 0xffffffff)); + } + else + { + return false; + } + } + break; + } + break; + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + default: + return false; + } + } + + // Make room for the arguments on the stack + + sp -= 4 * argLayout.size(); + + // Align the SP + + sp &= ~(16ull-1ull); // 16-byte alignment + + // Write the arguments on the stack + + size_t numChunks = argLayout.size(); + + for (index = 0; index < numChunks; ++index) + if (process->WriteMemory(sp + (index * 4), &argLayout[index], sizeof(uint32_t), error) != sizeof(uint32_t)) + return false; + + // The return address is pushed onto the stack. + + sp -= 4; + uint32_t returnAddressU32 = return_addr; + if (process->WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32)) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp)) + return false; + + // %ebp is set to a fake value, in our case 0x0x00000000 + + if (!reg_ctx->WriteRegisterFromUnsigned(fp_reg_num, 0x00000000)) + return false; + + // %eip is set to the address of the called function. + + if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, func_addr)) + return false; + + return true; +} + +static bool +ReadIntegerArgument (Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Process *process, + addr_t ¤t_stack_argument) +{ + + uint32_t byte_size = (bit_width + (8-1))/8; + Error error; + if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, is_signed, scalar, error)) + { + current_stack_argument += byte_size; + return true; + } + return false; +} + +bool +ABIMacOSX_i386::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + if (!reg_ctx) + return false; + + addr_t sp = reg_ctx->GetSP(0); + + if (!sp) + return false; + + addr_t current_stack_argument = sp + 4; // jump over return address + + for (value_index = 0; + value_index < num_values; + ++value_index) + { + Value *value = values.GetValueAtIndex(value_index); + + if (!value) + return false; + + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + ClangASTType clang_type (value->GetClangType()); + if (clang_type) + { + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + is_signed, + thread.GetProcess().get(), + current_stack_argument); + } + else if (clang_type.IsPointerType()) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + false, + thread.GetProcess().get(), + current_stack_argument); + } + } + } + + return true; +} + +Error +ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_type) + { + error.SetErrorString ("Null clang type for return value."); + return error; + } + + Thread *thread = frame_sp->GetThread().get(); + + bool is_signed; + uint32_t count; + bool is_complex; + + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + + bool set_it_simple = false; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + lldb::offset_t offset = 0; + if (num_bytes <= 8) + { + const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0); + if (num_bytes <= 4) + { + uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); + + if (reg_ctx->WriteRegisterFromUnsigned (eax_info, raw_value)) + set_it_simple = true; + } + else + { + uint32_t raw_value = data.GetMaxU32(&offset, 4); + + if (reg_ctx->WriteRegisterFromUnsigned (eax_info, raw_value)) + { + const RegisterInfo *edx_info = reg_ctx->GetRegisterInfoByName("edx", 0); + uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset); + + if (reg_ctx->WriteRegisterFromUnsigned (edx_info, raw_value)) + set_it_simple = true; + } + } + } + else + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + } + else if (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + error.SetErrorString ("We don't support returning float values at present"); + } + + if (!set_it_simple) + error.SetErrorString ("We only support setting simple integer return types at present."); + + return error; +} + +ValueObjectSP +ABIMacOSX_i386::GetReturnValueObjectImpl (Thread &thread, + ClangASTType &clang_type) const +{ + Value value; + ValueObjectSP return_valobj_sp; + + if (!clang_type) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, clang_type.GetOpaqueQualType()); + value.SetClangType (clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + size_t bit_width = clang_type.GetBitSize(); + + unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; + unsigned edx_id = reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB]; + + switch (bit_width) + { + default: + case 128: + // Scalar can't hold 128-bit literals, so we don't handle this + return return_valobj_sp; + case 64: + uint64_t raw_value; + raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff; + raw_value |= (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) & 0xffffffff) << 32; + if (is_signed) + value.GetScalar() = (int64_t)raw_value; + else + value.GetScalar() = (uint64_t)raw_value; + break; + case 32: + if (is_signed) + value.GetScalar() = (int32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff); + else + value.GetScalar() = (uint32_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff); + break; + case 16: + if (is_signed) + value.GetScalar() = (int16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffff); + else + value.GetScalar() = (uint16_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffff); + break; + case 8: + if (is_signed) + value.GetScalar() = (int8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xff); + else + value.GetScalar() = (uint8_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xff); + break; + } + } + else if (clang_type.IsPointerType ()) + { + unsigned eax_id = reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; + uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff; + value.GetScalar() = ptr; + } + else + { + // not handled yet + return return_valobj_sp; + } + + // If we get here, we have a valid Value, so make our ValueObject out of it: + + return_valobj_sp = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + return return_valobj_sp; +} + +bool +ABIMacOSX_i386::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + sp_reg_num = dwarf_esp; + pc_reg_num = dwarf_eip; + break; + + case eRegisterKindGCC: + sp_reg_num = gcc_esp; + pc_reg_num = gcc_eip; + break; + + case eRegisterKindGDB: + sp_reg_num = gdb_esp; + pc_reg_num = gdb_eip; + break; + + case eRegisterKindGeneric: + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + row->SetCFARegister (sp_reg_num); + row->SetCFAOffset (4); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false); + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("i386 at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + return true; +} + +bool +ABIMacOSX_i386::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t fp_reg_num = dwarf_ebp; + uint32_t sp_reg_num = dwarf_esp; + uint32_t pc_reg_num = dwarf_eip; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + const int32_t ptr_size = 4; + + unwind_plan.Clear (); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + row->SetCFARegister (fp_reg_num); + row->SetCFAOffset (2 * ptr_size); + row->SetOffset (0); + + row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); + row->SetRegisterLocationToAtCFAPlusOffset(sp_reg_num, ptr_size * 0, true); + + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("i386 default unwind plan"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + return true; +} + +bool +ABIMacOSX_i386::RegisterIsVolatile (const RegisterInfo *reg_info) +{ + return !RegisterIsCalleeSaved (reg_info); +} + +// v. http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4 + +bool +ABIMacOSX_i386::RegisterIsCalleeSaved (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Saved registers are ebx, ebp, esi, edi, esp, eip + const char *name = reg_info->name; + if (name[0] == 'e') + { + switch (name[1]) + { + case 'b': + if (name[2] == 'x' || name[2] == 'p') + return name[3] == '\0'; + break; + case 'd': + if (name[2] == 'i') + return name[3] == '\0'; + break; + case 'i': + if (name[2] == 'p') + return name[3] == '\0'; + break; + case 's': + if (name[2] == 'i' || name[2] == 'p') + return name[3] == '\0'; + break; + } + } + if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp + return true; + if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp + return true; + if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc + return true; + } + return false; +} + +void +ABIMacOSX_i386::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "Mac OS X ABI for i386 targets", + CreateInstance); +} + +void +ABIMacOSX_i386::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ABIMacOSX_i386::GetPluginNameStatic () +{ + static ConstString g_short_name("abi.macosx-i386"); + return g_short_name; + +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ABIMacOSX_i386::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABIMacOSX_i386::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h new file mode 100644 index 000000000000..8c2d945e6342 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h @@ -0,0 +1,139 @@ +//===-- ABIMacOSX_i386.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABIMacOSX_i386_h_ +#define liblldb_ABIMacOSX_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Core/Value.h" + +class ABIMacOSX_i386 : + public lldb_private::ABI +{ +public: + + ~ABIMacOSX_i386() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t return_addr, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + PrepareNormalCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t return_addr, + lldb_private::ValueList &args) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // Make sure the stack call frame addresses are are 8 byte aligned + if (cfa & (8ull - 1ull)) + return false; // Not 8 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // Just make sure the address is a valid 32 bit address. + return pc <= UINT32_MAX; + } + + virtual bool + FunctionCallsChangeCFA () + { + return true; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + static lldb_private::ConstString + GetPluginNameStatic (); + + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + bool + RegisterIsCalleeSaved (const lldb_private::RegisterInfo *reg_info); + +private: + ABIMacOSX_i386() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + + +#endif // liblldb_ABI_h_ diff --git a/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp new file mode 100644 index 000000000000..a904d8b649ca --- /dev/null +++ b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp @@ -0,0 +1,1288 @@ +//===-- ABISysV_x86_64.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABISysV_x86_64.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_rax = 0, + gcc_dwarf_rdx, + gcc_dwarf_rcx, + gcc_dwarf_rbx, + gcc_dwarf_rsi, + gcc_dwarf_rdi, + gcc_dwarf_rbp, + gcc_dwarf_rsp, + gcc_dwarf_r8, + gcc_dwarf_r9, + gcc_dwarf_r10, + gcc_dwarf_r11, + gcc_dwarf_r12, + gcc_dwarf_r13, + gcc_dwarf_r14, + gcc_dwarf_r15, + gcc_dwarf_rip, + gcc_dwarf_xmm0, + gcc_dwarf_xmm1, + gcc_dwarf_xmm2, + gcc_dwarf_xmm3, + gcc_dwarf_xmm4, + gcc_dwarf_xmm5, + gcc_dwarf_xmm6, + gcc_dwarf_xmm7, + gcc_dwarf_xmm8, + gcc_dwarf_xmm9, + gcc_dwarf_xmm10, + gcc_dwarf_xmm11, + gcc_dwarf_xmm12, + gcc_dwarf_xmm13, + gcc_dwarf_xmm14, + gcc_dwarf_xmm15, + gcc_dwarf_stmm0, + gcc_dwarf_stmm1, + gcc_dwarf_stmm2, + gcc_dwarf_stmm3, + gcc_dwarf_stmm4, + gcc_dwarf_stmm5, + gcc_dwarf_stmm6, + gcc_dwarf_stmm7, + gcc_dwarf_ymm0, + gcc_dwarf_ymm1, + gcc_dwarf_ymm2, + gcc_dwarf_ymm3, + gcc_dwarf_ymm4, + gcc_dwarf_ymm5, + gcc_dwarf_ymm6, + gcc_dwarf_ymm7, + gcc_dwarf_ymm8, + gcc_dwarf_ymm9, + gcc_dwarf_ymm10, + gcc_dwarf_ymm11, + gcc_dwarf_ymm12, + gcc_dwarf_ymm13, + gcc_dwarf_ymm14, + gcc_dwarf_ymm15 +}; + +enum gdb_regnums +{ + gdb_rax = 0, + gdb_rbx = 1, + gdb_rcx = 2, + gdb_rdx = 3, + gdb_rsi = 4, + gdb_rdi = 5, + gdb_rbp = 6, + gdb_rsp = 7, + gdb_r8 = 8, + gdb_r9 = 9, + gdb_r10 = 10, + gdb_r11 = 11, + gdb_r12 = 12, + gdb_r13 = 13, + gdb_r14 = 14, + gdb_r15 = 15, + gdb_rip = 16, + gdb_rflags = 17, + gdb_cs = 18, + gdb_ss = 19, + gdb_ds = 20, + gdb_es = 21, + gdb_fs = 22, + gdb_gs = 23, + gdb_stmm0 = 24, + gdb_stmm1 = 25, + gdb_stmm2 = 26, + gdb_stmm3 = 27, + gdb_stmm4 = 28, + gdb_stmm5 = 29, + gdb_stmm6 = 30, + gdb_stmm7 = 31, + gdb_fctrl = 32, gdb_fcw = gdb_fctrl, + gdb_fstat = 33, gdb_fsw = gdb_fstat, + gdb_ftag = 34, gdb_ftw = gdb_ftag, + gdb_fiseg = 35, gdb_fpu_cs = gdb_fiseg, + gdb_fioff = 36, gdb_ip = gdb_fioff, + gdb_foseg = 37, gdb_fpu_ds = gdb_foseg, + gdb_fooff = 38, gdb_dp = gdb_fooff, + gdb_fop = 39, + gdb_xmm0 = 40, + gdb_xmm1 = 41, + gdb_xmm2 = 42, + gdb_xmm3 = 43, + gdb_xmm4 = 44, + gdb_xmm5 = 45, + gdb_xmm6 = 46, + gdb_xmm7 = 47, + gdb_xmm8 = 48, + gdb_xmm9 = 49, + gdb_xmm10 = 50, + gdb_xmm11 = 51, + gdb_xmm12 = 52, + gdb_xmm13 = 53, + gdb_xmm14 = 54, + gdb_xmm15 = 55, + gdb_mxcsr = 56, + gdb_ymm0 = 57, + gdb_ymm1 = 58, + gdb_ymm2 = 59, + gdb_ymm3 = 60, + gdb_ymm4 = 61, + gdb_ymm5 = 62, + gdb_ymm6 = 63, + gdb_ymm7 = 64, + gdb_ymm8 = 65, + gdb_ymm9 = 66, + gdb_ymm10 = 67, + gdb_ymm11 = 68, + gdb_ymm12 = 69, + gdb_ymm13 = 70, + gdb_ymm14 = 71, + gdb_ymm15 = 72 +}; + + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ======== ======= == === ============= =================== ======================= ===================== =========================== ===================== ====================== ========== =============== + { "rax" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rax , gcc_dwarf_rax , LLDB_INVALID_REGNUM , gdb_rax , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rbx" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rbx , gcc_dwarf_rbx , LLDB_INVALID_REGNUM , gdb_rbx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rcx" , "arg4", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rcx , gcc_dwarf_rcx , LLDB_REGNUM_GENERIC_ARG4 , gdb_rcx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rdx" , "arg3", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rdx , gcc_dwarf_rdx , LLDB_REGNUM_GENERIC_ARG3 , gdb_rdx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rsi" , "arg2", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rsi , gcc_dwarf_rsi , LLDB_REGNUM_GENERIC_ARG2 , gdb_rsi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rdi" , "arg1", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rdi , gcc_dwarf_rdi , LLDB_REGNUM_GENERIC_ARG1 , gdb_rdi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rbp" , "fp", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rbp , gcc_dwarf_rbp , LLDB_REGNUM_GENERIC_FP , gdb_rbp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rsp" , "sp", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rsp , gcc_dwarf_rsp , LLDB_REGNUM_GENERIC_SP , gdb_rsp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8" , "arg5", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r8 , gcc_dwarf_r8 , LLDB_REGNUM_GENERIC_ARG5 , gdb_r8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9" , "arg6", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r9 , gcc_dwarf_r9 , LLDB_REGNUM_GENERIC_ARG6 , gdb_r9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r10 , gcc_dwarf_r10 , LLDB_INVALID_REGNUM , gdb_r10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r11 , gcc_dwarf_r11 , LLDB_INVALID_REGNUM , gdb_r11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r12 , gcc_dwarf_r12 , LLDB_INVALID_REGNUM , gdb_r12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r13 , gcc_dwarf_r13 , LLDB_INVALID_REGNUM , gdb_r13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r14 , gcc_dwarf_r14 , LLDB_INVALID_REGNUM , gdb_r14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r15" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r15 , gcc_dwarf_r15 , LLDB_INVALID_REGNUM , gdb_r15 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rip" , "pc", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rip , gcc_dwarf_rip , LLDB_REGNUM_GENERIC_PC , gdb_rip , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rflags", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_rflags , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ss" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ds" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "es" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "gs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm0" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm0 , gcc_dwarf_stmm0 , LLDB_INVALID_REGNUM , gdb_stmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm1" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm1 , gcc_dwarf_stmm1 , LLDB_INVALID_REGNUM , gdb_stmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm2" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm2 , gcc_dwarf_stmm2 , LLDB_INVALID_REGNUM , gdb_stmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm3" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm3 , gcc_dwarf_stmm3 , LLDB_INVALID_REGNUM , gdb_stmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm4" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm4 , gcc_dwarf_stmm4 , LLDB_INVALID_REGNUM , gdb_stmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm5" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm5 , gcc_dwarf_stmm5 , LLDB_INVALID_REGNUM , gdb_stmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm6" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm6 , gcc_dwarf_stmm6 , LLDB_INVALID_REGNUM , gdb_stmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm7" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm7 , gcc_dwarf_stmm7 , LLDB_INVALID_REGNUM , gdb_stmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fctrl" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fctrl , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fstat" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fstat , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ftag" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftag , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fiseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fiseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fioff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fioff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "foseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_foseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fooff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fooff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fop" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm0" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm0 , gcc_dwarf_xmm0 , LLDB_INVALID_REGNUM , gdb_xmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm1" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm1 , gcc_dwarf_xmm1 , LLDB_INVALID_REGNUM , gdb_xmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm2" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm2 , gcc_dwarf_xmm2 , LLDB_INVALID_REGNUM , gdb_xmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm3" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm3 , gcc_dwarf_xmm3 , LLDB_INVALID_REGNUM , gdb_xmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm4" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm4 , gcc_dwarf_xmm4 , LLDB_INVALID_REGNUM , gdb_xmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm5" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm5 , gcc_dwarf_xmm5 , LLDB_INVALID_REGNUM , gdb_xmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm6" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm6 , gcc_dwarf_xmm6 , LLDB_INVALID_REGNUM , gdb_xmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm7" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm7 , gcc_dwarf_xmm7 , LLDB_INVALID_REGNUM , gdb_xmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm8" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm8 , gcc_dwarf_xmm8 , LLDB_INVALID_REGNUM , gdb_xmm8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm9" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm9 , gcc_dwarf_xmm9 , LLDB_INVALID_REGNUM , gdb_xmm9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm10" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm10 , gcc_dwarf_xmm10 , LLDB_INVALID_REGNUM , gdb_xmm10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm11" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm11 , gcc_dwarf_xmm11 , LLDB_INVALID_REGNUM , gdb_xmm11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm12" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm12 , gcc_dwarf_xmm12 , LLDB_INVALID_REGNUM , gdb_xmm12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm13" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm13 , gcc_dwarf_xmm13 , LLDB_INVALID_REGNUM , gdb_xmm13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm14" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm14 , gcc_dwarf_xmm14 , LLDB_INVALID_REGNUM , gdb_xmm14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm15" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm15 , gcc_dwarf_xmm15 , LLDB_INVALID_REGNUM , gdb_xmm15 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "mxcsr" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm0" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm0 , gcc_dwarf_ymm0 , LLDB_INVALID_REGNUM , gdb_ymm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm1" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm1 , gcc_dwarf_ymm1 , LLDB_INVALID_REGNUM , gdb_ymm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm2" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm2 , gcc_dwarf_ymm2 , LLDB_INVALID_REGNUM , gdb_ymm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm3" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm3 , gcc_dwarf_ymm3 , LLDB_INVALID_REGNUM , gdb_ymm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm4" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm4 , gcc_dwarf_ymm4 , LLDB_INVALID_REGNUM , gdb_ymm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm5" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm5 , gcc_dwarf_ymm5 , LLDB_INVALID_REGNUM , gdb_ymm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm6" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm6 , gcc_dwarf_ymm6 , LLDB_INVALID_REGNUM , gdb_ymm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm7" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm7 , gcc_dwarf_ymm7 , LLDB_INVALID_REGNUM , gdb_ymm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm8" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm8 , gcc_dwarf_ymm8 , LLDB_INVALID_REGNUM , gdb_ymm8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm9" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm9 , gcc_dwarf_ymm9 , LLDB_INVALID_REGNUM , gdb_ymm9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm10" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm10 , gcc_dwarf_ymm10 , LLDB_INVALID_REGNUM , gdb_ymm10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm11" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm11 , gcc_dwarf_ymm11 , LLDB_INVALID_REGNUM , gdb_ymm11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm12" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm12 , gcc_dwarf_ymm12 , LLDB_INVALID_REGNUM , gdb_ymm12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm13" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm13 , gcc_dwarf_ymm13 , LLDB_INVALID_REGNUM , gdb_ymm13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm14" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm14 , gcc_dwarf_ymm14 , LLDB_INVALID_REGNUM , gdb_ymm14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm15" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm15 , gcc_dwarf_ymm15 , LLDB_INVALID_REGNUM , gdb_ymm15 , LLDB_INVALID_REGNUM }, NULL, NULL} +}; + +static const uint32_t k_num_register_infos = sizeof(g_register_infos)/sizeof(RegisterInfo); +static bool g_register_info_names_constified = false; + +const lldb_private::RegisterInfo * +ABISysV_x86_64::GetRegisterInfoArray (uint32_t &count) +{ + // Make the C-string names and alt_names for the register infos into const + // C-string values by having the ConstString unique the names in the global + // constant C-string pool. + if (!g_register_info_names_constified) + { + g_register_info_names_constified = true; + for (uint32_t i=0; i<k_num_register_infos; ++i) + { + if (g_register_infos[i].name) + g_register_infos[i].name = ConstString(g_register_infos[i].name).GetCString(); + if (g_register_infos[i].alt_name) + g_register_infos[i].alt_name = ConstString(g_register_infos[i].alt_name).GetCString(); + } + } + count = k_num_register_infos; + return g_register_infos; +} + + +size_t +ABISysV_x86_64::GetRedZoneSize () const +{ + return 128; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +ABISP +ABISysV_x86_64::CreateInstance (const ArchSpec &arch) +{ + static ABISP g_abi_sp; + if (arch.GetTriple().getArch() == llvm::Triple::x86_64) + { + if (!g_abi_sp) + g_abi_sp.reset (new ABISysV_x86_64); + return g_abi_sp; + } + return ABISP(); +} + +bool +ABISysV_x86_64::PrepareTrivialCall (Thread &thread, + addr_t sp, + addr_t func_addr, + addr_t return_addr, + addr_t *arg1_ptr, + addr_t *arg2_ptr, + addr_t *arg3_ptr, + addr_t *arg4_ptr, + addr_t *arg5_ptr, + addr_t *arg6_ptr) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + StreamString s; + s.Printf("ABISysV_x86_64::PrepareTrivialCall (tid = 0x%" PRIx64 ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 ", return_addr = 0x%" PRIx64, + thread.GetID(), + (uint64_t)sp, + (uint64_t)func_addr, + (uint64_t)return_addr); + + if (arg1_ptr) + { + s.Printf (", arg1 = 0x%" PRIx64, (uint64_t)*arg1_ptr); + if (arg2_ptr) + { + s.Printf (", arg2 = 0x%" PRIx64, (uint64_t)*arg2_ptr); + if (arg3_ptr) + { + s.Printf (", arg3 = 0x%" PRIx64, (uint64_t)*arg3_ptr); + if (arg4_ptr) + { + s.Printf (", arg4 = 0x%" PRIx64, (uint64_t)*arg4_ptr); + if (arg5_ptr) + { + s.Printf (", arg5 = 0x%" PRIx64, (uint64_t)*arg5_ptr); + if (arg6_ptr) + s.Printf (", arg6 = 0x%" PRIx64, (uint64_t)*arg6_ptr); + } + } + } + } + } + s.PutCString (")"); + log->PutCString(s.GetString().c_str()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const RegisterInfo *reg_info = NULL; + if (arg1_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rdi", 0); + if (log) + log->Printf("About to write arg1 (0x%" PRIx64 ") into %s", (uint64_t)*arg1_ptr, reg_info->name); + + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg1_ptr)) + return false; + + if (arg2_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rsi", 0); + if (log) + log->Printf("About to write arg2 (0x%" PRIx64 ") into %s", (uint64_t)*arg2_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg2_ptr)) + return false; + + if (arg3_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rdx", 0); + if (log) + log->Printf("About to write arg3 (0x%" PRIx64 ") into %s", (uint64_t)*arg3_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg3_ptr)) + return false; + + if (arg4_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rcx", 0); + if (log) + log->Printf("About to write arg4 (0x%" PRIx64 ") into %s", (uint64_t)*arg4_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg4_ptr)) + return false; + + if (arg5_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("r8", 0); + if (log) + log->Printf("About to write arg5 (0x%" PRIx64 ") into %s", (uint64_t)*arg5_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg5_ptr)) + return false; + + if (arg6_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("r9", 0); + if (log) + log->Printf("About to write arg6 (0x%" PRIx64 ") into %s", (uint64_t)*arg6_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg6_ptr)) + return false; + } + } + } + } + } + } + + + // First, align the SP + + if (log) + log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, (uint64_t)sp, (uint64_t)(sp & ~0xfull)); + + sp &= ~(0xfull); // 16-byte alignment + + // The return address is pushed onto the stack (yes after the alignment...) + sp -= 8; + + RegisterValue reg_value; + reg_value.SetUInt64 (return_addr); + + if (log) + log->Printf("Pushing the return address onto the stack: new SP 0x%" PRIx64 ", return address 0x%" PRIx64, (uint64_t)sp, (uint64_t)return_addr); + + const RegisterInfo *pc_reg_info = reg_ctx->GetRegisterInfoByName("rip"); + Error error (reg_ctx->WriteRegisterValueToMemory(pc_reg_info, sp, pc_reg_info->byte_size, reg_value)); + if (error.Fail()) + return false; + + // %rsp is set to the actual stack value. + + if (log) + log->Printf("Writing SP (0x%" PRIx64 ") down", (uint64_t)sp); + + if (!reg_ctx->WriteRegisterFromUnsigned (reg_ctx->GetRegisterInfoByName("rsp"), sp)) + return false; + + // %rip is set to the address of the called function. + + if (log) + log->Printf("Writing new IP (0x%" PRIx64 ") down", (uint64_t)func_addr); + + if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_info, func_addr)) + return false; + + return true; +} + +static bool ReadIntegerArgument(Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Thread &thread, + uint32_t *argument_register_ids, + unsigned int ¤t_argument_register, + addr_t ¤t_stack_argument) +{ + if (bit_width > 64) + return false; // Scalar can't hold large integer arguments + + if (current_argument_register < 6) + { + scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(argument_register_ids[current_argument_register], 0); + current_argument_register++; + if (is_signed) + scalar.SignExtend (bit_width); + } + else + { + uint32_t byte_size = (bit_width + (8-1))/8; + Error error; + if (thread.GetProcess()->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, is_signed, scalar, error)) + { + current_stack_argument += byte_size; + return true; + } + return false; + } + return true; +} + +bool +ABISysV_x86_64::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // Extract the register context so we can read arguments from registers + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + if (!reg_ctx) + return false; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + addr_t sp = reg_ctx->GetSP(0); + + if (!sp) + return false; + + addr_t current_stack_argument = sp + 8; // jump over return address + + uint32_t argument_register_ids[6]; + + argument_register_ids[0] = reg_ctx->GetRegisterInfoByName("rdi", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[1] = reg_ctx->GetRegisterInfoByName("rsi", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[2] = reg_ctx->GetRegisterInfoByName("rdx", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[3] = reg_ctx->GetRegisterInfoByName("rcx", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[4] = reg_ctx->GetRegisterInfoByName("r8", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[5] = reg_ctx->GetRegisterInfoByName("r9", 0)->kinds[eRegisterKindLLDB]; + + unsigned int current_argument_register = 0; + + for (value_index = 0; + value_index < num_values; + ++value_index) + { + Value *value = values.GetValueAtIndex(value_index); + + if (!value) + return false; + + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + ClangASTType clang_type = value->GetClangType(); + if (!clang_type) + return false; + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + is_signed, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + else if (clang_type.IsPointerType ()) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + false, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + } + + return true; +} + +Error +ABISysV_x86_64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_type) + { + error.SetErrorString ("Null clang type for return value."); + return error; + } + + Thread *thread = frame_sp->GetThread().get(); + + bool is_signed; + uint32_t count; + bool is_complex; + + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + + bool set_it_simple = false; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("rax", 0); + + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + lldb::offset_t offset = 0; + if (num_bytes <= 8) + { + uint64_t raw_value = data.GetMaxU64(&offset, num_bytes); + + if (reg_ctx->WriteRegisterFromUnsigned (reg_info, raw_value)) + set_it_simple = true; + } + else + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + + } + else if (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + { + size_t bit_width = clang_type.GetBitSize(); + if (bit_width <= 64) + { + const RegisterInfo *xmm0_info = reg_ctx->GetRegisterInfoByName("xmm0", 0); + RegisterValue xmm0_value; + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + + unsigned char buffer[16]; + ByteOrder byte_order = data.GetByteOrder(); + + data.CopyByteOrderedData (0, num_bytes, buffer, 16, byte_order); + xmm0_value.SetBytes(buffer, 16, byte_order); + reg_ctx->WriteRegister(xmm0_info, xmm0_value); + set_it_simple = true; + } + else + { + // FIXME - don't know how to do 80 bit long doubles yet. + error.SetErrorString ("We don't support returning float values > 64 bits at present"); + } + } + } + + if (!set_it_simple) + { + // Okay we've got a structure or something that doesn't fit in a simple register. + // We should figure out where it really goes, but we don't support this yet. + error.SetErrorString ("We only support setting simple integer and float return types at present."); + } + + return error; +} + + +ValueObjectSP +ABISysV_x86_64::GetReturnValueObjectSimple (Thread &thread, + ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + Value value; + + if (!return_clang_type) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, return_value_type); + value.SetClangType (return_clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + const uint32_t type_flags = return_clang_type.GetTypeInfo (); + if (type_flags & ClangASTType::eTypeIsScalar) + { + value.SetValueType(Value::eValueTypeScalar); + + bool success = false; + if (type_flags & ClangASTType::eTypeIsInteger) + { + // Extract the register context so we can read arguments from registers + + const size_t byte_size = return_clang_type.GetByteSize(); + uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("rax", 0), 0); + const bool is_signed = (type_flags & ClangASTType::eTypeIsSigned) != 0; + switch (byte_size) + { + default: + break; + + case sizeof(uint64_t): + if (is_signed) + value.GetScalar() = (int64_t)(raw_value); + else + value.GetScalar() = (uint64_t)(raw_value); + success = true; + break; + + case sizeof(uint32_t): + if (is_signed) + value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); + else + value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); + success = true; + break; + + case sizeof(uint16_t): + if (is_signed) + value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); + else + value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); + success = true; + break; + + case sizeof(uint8_t): + if (is_signed) + value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); + else + value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); + success = true; + break; + } + } + else if (type_flags & ClangASTType::eTypeIsFloat) + { + if (type_flags & ClangASTType::eTypeIsComplex) + { + // Don't handle complex yet. + } + else + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size <= sizeof(long double)) + { + const RegisterInfo *xmm0_info = reg_ctx->GetRegisterInfoByName("xmm0", 0); + RegisterValue xmm0_value; + if (reg_ctx->ReadRegister (xmm0_info, xmm0_value)) + { + DataExtractor data; + if (xmm0_value.GetData(data)) + { + lldb::offset_t offset = 0; + if (byte_size == sizeof(float)) + { + value.GetScalar() = (float) data.GetFloat(&offset); + success = true; + } + else if (byte_size == sizeof(double)) + { + value.GetScalar() = (double) data.GetDouble(&offset); + success = true; + } + else if (byte_size == sizeof(long double)) + { + // Don't handle long double since that can be encoded as 80 bit floats... + } + } + } + } + } + } + + if (success) + return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + + } + else if (type_flags & ClangASTType::eTypeIsPointer) + { + unsigned rax_id = reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB]; + value.GetScalar() = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0); + value.SetValueType(Value::eValueTypeScalar); + return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + } + else if (type_flags & ClangASTType::eTypeIsVector) + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size > 0) + { + + const RegisterInfo *altivec_reg = reg_ctx->GetRegisterInfoByName("ymm0", 0); + if (altivec_reg == NULL) + { + altivec_reg = reg_ctx->GetRegisterInfoByName("xmm0", 0); + if (altivec_reg == NULL) + altivec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0); + } + + if (altivec_reg) + { + if (byte_size <= altivec_reg->byte_size) + { + ProcessSP process_sp (thread.GetProcess()); + if (process_sp) + { + std::unique_ptr<DataBufferHeap> heap_data_ap (new DataBufferHeap(byte_size, 0)); + const ByteOrder byte_order = process_sp->GetByteOrder(); + RegisterValue reg_value; + if (reg_ctx->ReadRegister(altivec_reg, reg_value)) + { + Error error; + if (reg_value.GetAsMemoryData (altivec_reg, + heap_data_ap->GetBytes(), + heap_data_ap->GetByteSize(), + byte_order, + error)) + { + DataExtractor data (DataBufferSP (heap_data_ap.release()), + byte_order, + process_sp->GetTarget().GetArchitecture().GetAddressByteSize()); + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + data); + } + } + } + } + } + } + } + + return return_valobj_sp; +} + +ValueObjectSP +ABISysV_x86_64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + + if (!return_clang_type) + return return_valobj_sp; + + ExecutionContext exe_ctx (thread.shared_from_this()); + return_valobj_sp = GetReturnValueObjectSimple(thread, return_clang_type); + if (return_valobj_sp) + return return_valobj_sp; + + RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); + if (!reg_ctx_sp) + return return_valobj_sp; + + const size_t bit_width = return_clang_type.GetBitSize(); + if (return_clang_type.IsAggregateType()) + { + Target *target = exe_ctx.GetTargetPtr(); + bool is_memory = true; + if (bit_width <= 128) + { + ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder(); + DataBufferSP data_sp (new DataBufferHeap(16, 0)); + DataExtractor return_ext (data_sp, + target_byte_order, + target->GetArchitecture().GetAddressByteSize()); + + const RegisterInfo *rax_info = reg_ctx_sp->GetRegisterInfoByName("rax", 0); + const RegisterInfo *rdx_info = reg_ctx_sp->GetRegisterInfoByName("rdx", 0); + const RegisterInfo *xmm0_info = reg_ctx_sp->GetRegisterInfoByName("xmm0", 0); + const RegisterInfo *xmm1_info = reg_ctx_sp->GetRegisterInfoByName("xmm1", 0); + + RegisterValue rax_value, rdx_value, xmm0_value, xmm1_value; + reg_ctx_sp->ReadRegister (rax_info, rax_value); + reg_ctx_sp->ReadRegister (rdx_info, rdx_value); + reg_ctx_sp->ReadRegister (xmm0_info, xmm0_value); + reg_ctx_sp->ReadRegister (xmm1_info, xmm1_value); + + DataExtractor rax_data, rdx_data, xmm0_data, xmm1_data; + + rax_value.GetData(rax_data); + rdx_value.GetData(rdx_data); + xmm0_value.GetData(xmm0_data); + xmm1_value.GetData(xmm1_data); + + uint32_t fp_bytes = 0; // Tracks how much of the xmm registers we've consumed so far + uint32_t integer_bytes = 0; // Tracks how much of the rax/rds registers we've consumed so far + + const uint32_t num_children = return_clang_type.GetNumFields (); + + // Since we are in the small struct regime, assume we are not in memory. + is_memory = false; + + for (uint32_t idx = 0; idx < num_children; idx++) + { + std::string name; + uint64_t field_bit_offset = 0; + bool is_signed; + bool is_complex; + uint32_t count; + + ClangASTType field_clang_type = return_clang_type.GetFieldAtIndex (idx, name, &field_bit_offset, NULL, NULL); + const size_t field_bit_width = field_clang_type.GetBitSize(); + + // If there are any unaligned fields, this is stored in memory. + if (field_bit_offset % field_bit_width != 0) + { + is_memory = true; + break; + } + + uint32_t field_byte_width = field_bit_width/8; + uint32_t field_byte_offset = field_bit_offset/8; + + + DataExtractor *copy_from_extractor = NULL; + uint32_t copy_from_offset = 0; + + if (field_clang_type.IsIntegerType (is_signed) || field_clang_type.IsPointerType ()) + { + if (integer_bytes < 8) + { + if (integer_bytes + field_byte_width <= 8) + { + // This is in RAX, copy from register to our result structure: + copy_from_extractor = &rax_data; + copy_from_offset = integer_bytes; + integer_bytes += field_byte_width; + } + else + { + // The next field wouldn't fit in the remaining space, so we pushed it to rdx. + copy_from_extractor = &rdx_data; + copy_from_offset = 0; + integer_bytes = 8 + field_byte_width; + + } + } + else if (integer_bytes + field_byte_width <= 16) + { + copy_from_extractor = &rdx_data; + copy_from_offset = integer_bytes - 8; + integer_bytes += field_byte_width; + } + else + { + // The last field didn't fit. I can't see how that would happen w/o the overall size being + // greater than 16 bytes. For now, return a NULL return value object. + return return_valobj_sp; + } + } + else if (field_clang_type.IsFloatingPointType (count, is_complex)) + { + // Structs with long doubles are always passed in memory. + if (field_bit_width == 128) + { + is_memory = true; + break; + } + else if (field_bit_width == 64) + { + // These have to be in a single xmm register. + if (fp_bytes == 0) + copy_from_extractor = &xmm0_data; + else + copy_from_extractor = &xmm1_data; + + copy_from_offset = 0; + fp_bytes += field_byte_width; + } + else if (field_bit_width == 32) + { + // This one is kind of complicated. If we are in an "eightbyte" with another float, we'll + // be stuffed into an xmm register with it. If we are in an "eightbyte" with one or more ints, + // then we will be stuffed into the appropriate GPR with them. + bool in_gpr; + if (field_byte_offset % 8 == 0) + { + // We are at the beginning of one of the eightbytes, so check the next element (if any) + if (idx == num_children - 1) + in_gpr = false; + else + { + uint64_t next_field_bit_offset = 0; + ClangASTType next_field_clang_type = return_clang_type.GetFieldAtIndex (idx + 1, + name, + &next_field_bit_offset, + NULL, + NULL); + if (next_field_clang_type.IsIntegerType (is_signed)) + in_gpr = true; + else + { + copy_from_offset = 0; + in_gpr = false; + } + } + + } + else if (field_byte_offset % 4 == 0) + { + // We are inside of an eightbyte, so see if the field before us is floating point: + // This could happen if somebody put padding in the structure. + if (idx == 0) + in_gpr = false; + else + { + uint64_t prev_field_bit_offset = 0; + ClangASTType prev_field_clang_type = return_clang_type.GetFieldAtIndex (idx - 1, + name, + &prev_field_bit_offset, + NULL, + NULL); + if (prev_field_clang_type.IsIntegerType (is_signed)) + in_gpr = true; + else + { + copy_from_offset = 4; + in_gpr = false; + } + } + + } + else + { + is_memory = true; + continue; + } + + // Okay, we've figured out whether we are in GPR or XMM, now figure out which one. + if (in_gpr) + { + if (integer_bytes < 8) + { + // This is in RAX, copy from register to our result structure: + copy_from_extractor = &rax_data; + copy_from_offset = integer_bytes; + integer_bytes += field_byte_width; + } + else + { + copy_from_extractor = &rdx_data; + copy_from_offset = integer_bytes - 8; + integer_bytes += field_byte_width; + } + } + else + { + if (fp_bytes < 8) + copy_from_extractor = &xmm0_data; + else + copy_from_extractor = &xmm1_data; + + fp_bytes += field_byte_width; + } + } + } + + // These two tests are just sanity checks. If I somehow get the + // type calculation wrong above it is better to just return nothing + // than to assert or crash. + if (!copy_from_extractor) + return return_valobj_sp; + if (copy_from_offset + field_byte_width > copy_from_extractor->GetByteSize()) + return return_valobj_sp; + + copy_from_extractor->CopyByteOrderedData (copy_from_offset, + field_byte_width, + data_sp->GetBytes() + field_byte_offset, + field_byte_width, + target_byte_order); + } + + if (!is_memory) + { + // The result is in our data buffer. Let's make a variable object out of it: + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + return_ext); + } + } + + + // FIXME: This is just taking a guess, rax may very well no longer hold the return storage location. + // If we are going to do this right, when we make a new frame we should check to see if it uses a memory + // return, and if we are at the first instruction and if so stash away the return location. Then we would + // only return the memory return value if we know it is valid. + + if (is_memory) + { + unsigned rax_id = reg_ctx_sp->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB]; + lldb::addr_t storage_addr = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0); + return_valobj_sp = ValueObjectMemory::Create (&thread, + "", + Address (storage_addr, NULL), + return_clang_type); + } + } + + return return_valobj_sp; +} + +bool +ABISysV_x86_64::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + sp_reg_num = gcc_dwarf_rsp; + pc_reg_num = gcc_dwarf_rip; + break; + + case eRegisterKindGDB: + sp_reg_num = gdb_rsp; + pc_reg_num = gdb_rip; + break; + + case eRegisterKindGeneric: + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + row->SetCFARegister (sp_reg_num); + row->SetCFAOffset (8); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false); + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("x86_64 at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + return true; +} + +bool +ABISysV_x86_64::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t fp_reg_num = LLDB_INVALID_REGNUM; + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + fp_reg_num = gcc_dwarf_rbp; + sp_reg_num = gcc_dwarf_rsp; + pc_reg_num = gcc_dwarf_rip; + break; + + case eRegisterKindGDB: + fp_reg_num = gdb_rbp; + sp_reg_num = gdb_rsp; + pc_reg_num = gdb_rip; + break; + + case eRegisterKindGeneric: + fp_reg_num = LLDB_REGNUM_GENERIC_FP; + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (fp_reg_num == LLDB_INVALID_REGNUM || + sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + const int32_t ptr_size = 8; + row->SetCFARegister (LLDB_REGNUM_GENERIC_FP); + row->SetCFAOffset (2 * ptr_size); + row->SetOffset (0); + + row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true); + row->SetRegisterLocationToAtCFAPlusOffset(sp_reg_num, ptr_size * 0, true); + + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("x86_64 default unwind plan"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + return true; +} + +bool +ABISysV_x86_64::RegisterIsVolatile (const RegisterInfo *reg_info) +{ + return !RegisterIsCalleeSaved (reg_info); +} + + + +// See "Register Usage" in the +// "System V Application Binary Interface" +// "AMD64 Architecture Processor Supplement" +// (or "x86-64(tm) Architecture Processor Supplement" in earlier revisions) +// (this doc is also commonly referred to as the x86-64/AMD64 psABI) +// Edited by Michael Matz, Jan Hubicka, Andreas Jaeger, and Mark Mitchell +// current version is 0.99.6 released 2012-07-02 at http://refspecs.linuxfoundation.org/elf/x86-64-abi-0.99.pdf + +bool +ABISysV_x86_64::RegisterIsCalleeSaved (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Preserved registers are : + // rbx, rsp, rbp, r12, r13, r14, r15 + // mxcsr (partially preserved) + // x87 control word + + const char *name = reg_info->name; + if (name[0] == 'r') + { + switch (name[1]) + { + case '1': // r12, r13, r14, r15 + if (name[2] >= '2' && name[2] <= '5') + return name[3] == '\0'; + break; + + default: + break; + } + } + + // Accept shorter-variant versions, rbx/ebx, rip/ eip, etc. + if (name[0] == 'r' || name[0] == 'e') + { + switch (name[1]) + { + case 'b': // rbp, rbx + if (name[2] == 'p' || name[2] == 'x') + return name[3] == '\0'; + break; + + case 'i': // rip + if (name[2] == 'p') + return name[3] == '\0'; + break; + + case 's': // rsp + if (name[2] == 'p') + return name[3] == '\0'; + break; + + } + } + if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp + return true; + if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp + return true; + if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc + return true; + } + return false; +} + + + +void +ABISysV_x86_64::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "System V ABI for x86_64 targets", + CreateInstance); +} + +void +ABISysV_x86_64::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ABISysV_x86_64::GetPluginNameStatic() +{ + static ConstString g_name("sysv-x86_64"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ABISysV_x86_64::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABISysV_x86_64::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h new file mode 100644 index 000000000000..b10181960e89 --- /dev/null +++ b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h @@ -0,0 +1,138 @@ +//===-- ABISysV_x86_64.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABISysV_x86_64_h_ +#define liblldb_ABISysV_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABISysV_x86_64 : + public lldb_private::ABI +{ +public: + + ~ABISysV_x86_64() + { + } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + lldb::ValueObjectSP + GetReturnValueObjectSimple (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &type) const; + + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // Make sure the stack call frame addresses are are 8 byte aligned + if (cfa & (8ull - 1ull)) + return false; // Not 8 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // We have a 64 bit address space, so anything is valid as opcodes + // aren't fixed width... + return true; + } + + virtual bool + FunctionCallsChangeCFA () + { + return true; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + void + CreateRegisterMapIfNeeded (); + + bool + RegisterIsCalleeSaved (const lldb_private::RegisterInfo *reg_info); + +private: + ABISysV_x86_64() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + +#endif // liblldb_ABI_h_ diff --git a/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp new file mode 100644 index 000000000000..e920d70cd596 --- /dev/null +++ b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp @@ -0,0 +1,864 @@ +//===-- DisassemblerLLVMC.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DisassemblerLLVMC.h" + +#include "llvm-c/Disassembler.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCRelocationInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryObject.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/ADT/SmallString.h" + + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/StackFrame.h" + +#include <regex.h> + +using namespace lldb; +using namespace lldb_private; + +class InstructionLLVMC : public lldb_private::Instruction +{ +public: + InstructionLLVMC (DisassemblerLLVMC &disasm, + const lldb_private::Address &address, + AddressClass addr_class) : + Instruction (address, addr_class), + m_disasm_sp (disasm.shared_from_this()), + m_does_branch (eLazyBoolCalculate), + m_is_valid (false), + m_using_file_addr (false) + { + } + + virtual + ~InstructionLLVMC () + { + } + + virtual bool + DoesBranch () + { + if (m_does_branch == eLazyBoolCalculate) + { + GetDisassemblerLLVMC().Lock(this, NULL); + DataExtractor data; + if (m_opcode.GetData(data)) + { + bool is_alternate_isa; + lldb::addr_t pc = m_address.GetFileAddress(); + + DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); + const uint8_t *opcode_data = data.GetDataStart(); + const size_t opcode_data_len = data.GetByteSize(); + llvm::MCInst inst; + const size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data, + opcode_data_len, + pc, + inst); + // Be conservative, if we didn't understand the instruction, say it might branch... + if (inst_size == 0) + m_does_branch = eLazyBoolYes; + else + { + const bool can_branch = mc_disasm_ptr->CanBranch(inst); + if (can_branch) + m_does_branch = eLazyBoolYes; + else + m_does_branch = eLazyBoolNo; + } + } + GetDisassemblerLLVMC().Unlock(); + } + return m_does_branch == eLazyBoolYes; + } + + DisassemblerLLVMC::LLVMCDisassembler * + GetDisasmToUse (bool &is_alternate_isa) + { + is_alternate_isa = false; + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + if (llvm_disasm.m_alternate_disasm_ap.get() != NULL) + { + const AddressClass address_class = GetAddressClass (); + + if (address_class == eAddressClassCodeAlternateISA) + { + is_alternate_isa = true; + return llvm_disasm.m_alternate_disasm_ap.get(); + } + } + return llvm_disasm.m_disasm_ap.get(); + } + + virtual size_t + Decode (const lldb_private::Disassembler &disassembler, + const lldb_private::DataExtractor &data, + lldb::offset_t data_offset) + { + // All we have to do is read the opcode which can be easy for some + // architectures + bool got_op = false; + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + const ArchSpec &arch = llvm_disasm.GetArchitecture(); + + const uint32_t min_op_byte_size = arch.GetMinimumOpcodeByteSize(); + const uint32_t max_op_byte_size = arch.GetMaximumOpcodeByteSize(); + if (min_op_byte_size == max_op_byte_size) + { + // Fixed size instructions, just read that amount of data. + if (!data.ValidOffsetForDataOfSize(data_offset, min_op_byte_size)) + return false; + + switch (min_op_byte_size) + { + case 1: + m_opcode.SetOpcode8 (data.GetU8 (&data_offset)); + got_op = true; + break; + + case 2: + m_opcode.SetOpcode16 (data.GetU16 (&data_offset)); + got_op = true; + break; + + case 4: + m_opcode.SetOpcode32 (data.GetU32 (&data_offset)); + got_op = true; + break; + + case 8: + m_opcode.SetOpcode64 (data.GetU64 (&data_offset)); + got_op = true; + break; + + default: + m_opcode.SetOpcodeBytes(data.PeekData(data_offset, min_op_byte_size), min_op_byte_size); + got_op = true; + break; + } + } + if (!got_op) + { + bool is_alternate_isa = false; + DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); + + const llvm::Triple::ArchType machine = arch.GetMachine(); + if (machine == llvm::Triple::arm || machine == llvm::Triple::thumb) + { + if (machine == llvm::Triple::thumb || is_alternate_isa) + { + uint32_t thumb_opcode = data.GetU16(&data_offset); + if ((thumb_opcode & 0xe000) != 0xe000 || ((thumb_opcode & 0x1800u) == 0)) + { + m_opcode.SetOpcode16 (thumb_opcode); + m_is_valid = true; + } + else + { + thumb_opcode <<= 16; + thumb_opcode |= data.GetU16(&data_offset); + m_opcode.SetOpcode16_2 (thumb_opcode); + m_is_valid = true; + } + } + else + { + m_opcode.SetOpcode32 (data.GetU32(&data_offset)); + m_is_valid = true; + } + } + else + { + // The opcode isn't evenly sized, so we need to actually use the llvm + // disassembler to parse it and get the size. + uint8_t *opcode_data = const_cast<uint8_t *>(data.PeekData (data_offset, 1)); + const size_t opcode_data_len = data.BytesLeft(data_offset); + const addr_t pc = m_address.GetFileAddress(); + llvm::MCInst inst; + + llvm_disasm.Lock(this, NULL); + const size_t inst_size = mc_disasm_ptr->GetMCInst(opcode_data, + opcode_data_len, + pc, + inst); + llvm_disasm.Unlock(); + if (inst_size == 0) + m_opcode.Clear(); + else + { + m_opcode.SetOpcodeBytes(opcode_data, inst_size); + m_is_valid = true; + } + } + } + return m_opcode.GetByteSize(); + } + + void + AppendComment (std::string &description) + { + if (m_comment.empty()) + m_comment.swap (description); + else + { + m_comment.append(", "); + m_comment.append(description); + } + } + + virtual void + CalculateMnemonicOperandsAndComment (const lldb_private::ExecutionContext *exe_ctx) + { + DataExtractor data; + const AddressClass address_class = GetAddressClass (); + + if (m_opcode.GetData(data)) + { + char out_string[512]; + + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + + DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr; + + if (address_class == eAddressClassCodeAlternateISA) + mc_disasm_ptr = llvm_disasm.m_alternate_disasm_ap.get(); + else + mc_disasm_ptr = llvm_disasm.m_disasm_ap.get(); + + lldb::addr_t pc = m_address.GetFileAddress(); + m_using_file_addr = true; + + const bool data_from_file = GetDisassemblerLLVMC().m_data_from_file; + bool use_hex_immediates = true; + Disassembler::HexImmediateStyle hex_style = Disassembler::eHexStyleC; + + if (exe_ctx) + { + Target *target = exe_ctx->GetTargetPtr(); + if (target) + { + use_hex_immediates = target->GetUseHexImmediates(); + hex_style = target->GetHexImmediateStyle(); + + if (!data_from_file) + { + const lldb::addr_t load_addr = m_address.GetLoadAddress(target); + if (load_addr != LLDB_INVALID_ADDRESS) + { + pc = load_addr; + m_using_file_addr = false; + } + } + } + } + + llvm_disasm.Lock(this, exe_ctx); + + const uint8_t *opcode_data = data.GetDataStart(); + const size_t opcode_data_len = data.GetByteSize(); + llvm::MCInst inst; + size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data, + opcode_data_len, + pc, + inst); + + if (inst_size > 0) + { + mc_disasm_ptr->SetStyle(use_hex_immediates, hex_style); + mc_disasm_ptr->PrintMCInst(inst, out_string, sizeof(out_string)); + } + + llvm_disasm.Unlock(); + + if (inst_size == 0) + { + m_comment.assign ("unknown opcode"); + inst_size = m_opcode.GetByteSize(); + StreamString mnemonic_strm; + lldb::offset_t offset = 0; + switch (inst_size) + { + case 1: + { + const uint8_t uval8 = data.GetU8 (&offset); + m_opcode.SetOpcode8 (uval8); + m_opcode_name.assign (".byte"); + mnemonic_strm.Printf("0x%2.2x", uval8); + } + break; + case 2: + { + const uint16_t uval16 = data.GetU16(&offset); + m_opcode.SetOpcode16(uval16); + m_opcode_name.assign (".short"); + mnemonic_strm.Printf("0x%4.4x", uval16); + } + break; + case 4: + { + const uint32_t uval32 = data.GetU32(&offset); + m_opcode.SetOpcode32(uval32); + m_opcode_name.assign (".long"); + mnemonic_strm.Printf("0x%8.8x", uval32); + } + break; + case 8: + { + const uint64_t uval64 = data.GetU64(&offset); + m_opcode.SetOpcode64(uval64); + m_opcode_name.assign (".quad"); + mnemonic_strm.Printf("0x%16.16" PRIx64, uval64); + } + break; + default: + if (inst_size == 0) + return; + else + { + const uint8_t *bytes = data.PeekData(offset, inst_size); + if (bytes == NULL) + return; + m_opcode_name.assign (".byte"); + m_opcode.SetOpcodeBytes(bytes, inst_size); + mnemonic_strm.Printf("0x%2.2x", bytes[0]); + for (uint32_t i=1; i<inst_size; ++i) + mnemonic_strm.Printf(" 0x%2.2x", bytes[i]); + } + break; + } + m_mnemonics.swap(mnemonic_strm.GetString()); + return; + } + else + { + if (m_does_branch == eLazyBoolCalculate) + { + const bool can_branch = mc_disasm_ptr->CanBranch(inst); + if (can_branch) + m_does_branch = eLazyBoolYes; + else + m_does_branch = eLazyBoolNo; + + } + } + + if (!s_regex_compiled) + { + ::regcomp(&s_regex, "[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?", REG_EXTENDED); + s_regex_compiled = true; + } + + ::regmatch_t matches[3]; + + if (!::regexec(&s_regex, out_string, sizeof(matches) / sizeof(::regmatch_t), matches, 0)) + { + if (matches[1].rm_so != -1) + m_opcode_name.assign(out_string + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (matches[2].rm_so != -1) + m_mnemonics.assign(out_string + matches[2].rm_so, matches[2].rm_eo - matches[2].rm_so); + } + } + } + + bool + IsValid () const + { + return m_is_valid; + } + + bool + UsingFileAddress() const + { + return m_using_file_addr; + } + size_t + GetByteSize () const + { + return m_opcode.GetByteSize(); + } + + DisassemblerLLVMC & + GetDisassemblerLLVMC () + { + return *(DisassemblerLLVMC *)m_disasm_sp.get(); + } +protected: + + DisassemblerSP m_disasm_sp; // for ownership + LazyBool m_does_branch; + bool m_is_valid; + bool m_using_file_addr; + + static bool s_regex_compiled; + static ::regex_t s_regex; +}; + +bool InstructionLLVMC::s_regex_compiled = false; +::regex_t InstructionLLVMC::s_regex; + +DisassemblerLLVMC::LLVMCDisassembler::LLVMCDisassembler (const char *triple, unsigned flavor, DisassemblerLLVMC &owner): + m_is_valid(true) +{ + std::string Error; + const llvm::Target *curr_target = llvm::TargetRegistry::lookupTarget(triple, Error); + if (!curr_target) + { + m_is_valid = false; + return; + } + + m_instr_info_ap.reset(curr_target->createMCInstrInfo()); + m_reg_info_ap.reset (curr_target->createMCRegInfo(triple)); + + std::string features_str; + + m_subtarget_info_ap.reset(curr_target->createMCSubtargetInfo(triple, "", + features_str)); + + m_asm_info_ap.reset(curr_target->createMCAsmInfo(*curr_target->createMCRegInfo(triple), triple)); + + if (m_instr_info_ap.get() == NULL || m_reg_info_ap.get() == NULL || m_subtarget_info_ap.get() == NULL || m_asm_info_ap.get() == NULL) + { + m_is_valid = false; + return; + } + + m_context_ap.reset(new llvm::MCContext(m_asm_info_ap.get(), m_reg_info_ap.get(), 0)); + + m_disasm_ap.reset(curr_target->createMCDisassembler(*m_subtarget_info_ap.get())); + if (m_disasm_ap.get() && m_context_ap.get()) + { + llvm::OwningPtr<llvm::MCRelocationInfo> RelInfo(curr_target->createMCRelocationInfo(triple, *m_context_ap.get())); + if (!RelInfo) + { + m_is_valid = false; + return; + } + m_disasm_ap->setupForSymbolicDisassembly(NULL, + DisassemblerLLVMC::SymbolLookupCallback, + (void *) &owner, + m_context_ap.get(), + RelInfo); + + unsigned asm_printer_variant; + if (flavor == ~0U) + asm_printer_variant = m_asm_info_ap->getAssemblerDialect(); + else + { + asm_printer_variant = flavor; + } + + m_instr_printer_ap.reset(curr_target->createMCInstPrinter(asm_printer_variant, + *m_asm_info_ap.get(), + *m_instr_info_ap.get(), + *m_reg_info_ap.get(), + *m_subtarget_info_ap.get())); + if (m_instr_printer_ap.get() == NULL) + { + m_disasm_ap.reset(); + m_is_valid = false; + } + } + else + m_is_valid = false; +} + +DisassemblerLLVMC::LLVMCDisassembler::~LLVMCDisassembler() +{ +} + +namespace { + // This is the memory object we use in GetInstruction. + class LLDBDisasmMemoryObject : public llvm::MemoryObject { + const uint8_t *m_bytes; + uint64_t m_size; + uint64_t m_base_PC; + public: + LLDBDisasmMemoryObject(const uint8_t *bytes, uint64_t size, uint64_t basePC) : + m_bytes(bytes), m_size(size), m_base_PC(basePC) {} + + uint64_t getBase() const { return m_base_PC; } + uint64_t getExtent() const { return m_size; } + + int readByte(uint64_t addr, uint8_t *byte) const { + if (addr - m_base_PC >= m_size) + return -1; + *byte = m_bytes[addr - m_base_PC]; + return 0; + } + }; +} // End Anonymous Namespace + +uint64_t +DisassemblerLLVMC::LLVMCDisassembler::GetMCInst (const uint8_t *opcode_data, + size_t opcode_data_len, + lldb::addr_t pc, + llvm::MCInst &mc_inst) +{ + LLDBDisasmMemoryObject memory_object (opcode_data, opcode_data_len, pc); + llvm::MCDisassembler::DecodeStatus status; + + uint64_t new_inst_size; + status = m_disasm_ap->getInstruction(mc_inst, + new_inst_size, + memory_object, + pc, + llvm::nulls(), + llvm::nulls()); + if (status == llvm::MCDisassembler::Success) + return new_inst_size; + else + return 0; +} + +uint64_t +DisassemblerLLVMC::LLVMCDisassembler::PrintMCInst (llvm::MCInst &mc_inst, + char *dst, + size_t dst_len) +{ + llvm::StringRef unused_annotations; + llvm::SmallString<64> inst_string; + llvm::raw_svector_ostream inst_stream(inst_string); + m_instr_printer_ap->printInst (&mc_inst, inst_stream, unused_annotations); + inst_stream.flush(); + const size_t output_size = std::min(dst_len - 1, inst_string.size()); + std::memcpy(dst, inst_string.data(), output_size); + dst[output_size] = '\0'; + + return output_size; +} + +void +DisassemblerLLVMC::LLVMCDisassembler::SetStyle (bool use_hex_immed, HexImmediateStyle hex_style) +{ + m_instr_printer_ap->setPrintImmHex(use_hex_immed); + switch(hex_style) + { + case eHexStyleC: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::C); break; + case eHexStyleAsm: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::Asm); break; + } +} + +bool +DisassemblerLLVMC::LLVMCDisassembler::CanBranch (llvm::MCInst &mc_inst) +{ + return m_instr_info_ap->get(mc_inst.getOpcode()).mayAffectControlFlow(mc_inst, *m_reg_info_ap.get()); +} + +bool +DisassemblerLLVMC::FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor) +{ + llvm::Triple triple = arch.GetTriple(); + if (flavor == NULL || strcmp (flavor, "default") == 0) + return true; + + if (triple.getArch() == llvm::Triple::x86 || triple.getArch() == llvm::Triple::x86_64) + { + if (strcmp (flavor, "intel") == 0 || strcmp (flavor, "att") == 0) + return true; + else + return false; + } + else + return false; +} + + +Disassembler * +DisassemblerLLVMC::CreateInstance (const ArchSpec &arch, const char *flavor) +{ + if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch) + { + std::unique_ptr<DisassemblerLLVMC> disasm_ap (new DisassemblerLLVMC(arch, flavor)); + + if (disasm_ap.get() && disasm_ap->IsValid()) + return disasm_ap.release(); + } + return NULL; +} + +DisassemblerLLVMC::DisassemblerLLVMC (const ArchSpec &arch, const char *flavor_string) : + Disassembler(arch, flavor_string), + m_exe_ctx (NULL), + m_inst (NULL), + m_data_from_file (false) +{ + if (!FlavorValidForArchSpec (arch, m_flavor.c_str())) + { + m_flavor.assign("default"); + } + + const char *triple = arch.GetTriple().getTriple().c_str(); + unsigned flavor = ~0U; + + // So far the only supported flavor is "intel" on x86. The base class will set this + // correctly coming in. + if (arch.GetTriple().getArch() == llvm::Triple::x86 + || arch.GetTriple().getArch() == llvm::Triple::x86_64) + { + if (m_flavor == "intel") + { + flavor = 1; + } + else if (m_flavor == "att") + { + flavor = 0; + } + } + + ArchSpec thumb_arch(arch); + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + std::string thumb_arch_name (thumb_arch.GetTriple().getArchName().str()); + // Replace "arm" with "thumb" so we get all thumb variants correct + if (thumb_arch_name.size() > 3) + { + thumb_arch_name.erase(0,3); + thumb_arch_name.insert(0, "thumb"); + } + else + { + thumb_arch_name = "thumbv7"; + } + thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name.c_str())); + } + + // Cortex-M3 devices (e.g. armv7m) can only execute thumb (T2) instructions, + // so hardcode the primary disassembler to thumb mode. + if (arch.GetTriple().getArch() == llvm::Triple::arm + && (arch.GetCore() == ArchSpec::Core::eCore_arm_armv7m || arch.GetCore() == ArchSpec::Core::eCore_arm_armv7em)) + { + triple = thumb_arch.GetTriple().getTriple().c_str(); + } + + m_disasm_ap.reset (new LLVMCDisassembler(triple, flavor, *this)); + if (!m_disasm_ap->IsValid()) + { + // We use m_disasm_ap.get() to tell whether we are valid or not, so if this isn't good for some reason, + // we reset it, and then we won't be valid and FindPlugin will fail and we won't get used. + m_disasm_ap.reset(); + } + + // For arm CPUs that can execute arm or thumb instructions, also create a thumb instruction disassembler. + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + std::string thumb_triple(thumb_arch.GetTriple().getTriple()); + m_alternate_disasm_ap.reset(new LLVMCDisassembler(thumb_triple.c_str(), flavor, *this)); + if (!m_alternate_disasm_ap->IsValid()) + { + m_disasm_ap.reset(); + m_alternate_disasm_ap.reset(); + } + } +} + +DisassemblerLLVMC::~DisassemblerLLVMC() +{ +} + +size_t +DisassemblerLLVMC::DecodeInstructions (const Address &base_addr, + const DataExtractor& data, + lldb::offset_t data_offset, + size_t num_instructions, + bool append, + bool data_from_file) +{ + if (!append) + m_instruction_list.Clear(); + + if (!IsValid()) + return 0; + + m_data_from_file = data_from_file; + uint32_t data_cursor = data_offset; + const size_t data_byte_size = data.GetByteSize(); + uint32_t instructions_parsed = 0; + Address inst_addr(base_addr); + + while (data_cursor < data_byte_size && instructions_parsed < num_instructions) + { + + AddressClass address_class = eAddressClassCode; + + if (m_alternate_disasm_ap.get() != NULL) + address_class = inst_addr.GetAddressClass (); + + InstructionSP inst_sp(new InstructionLLVMC(*this, + inst_addr, + address_class)); + + if (!inst_sp) + break; + + uint32_t inst_size = inst_sp->Decode(*this, data, data_cursor); + + if (inst_size == 0) + break; + + m_instruction_list.Append(inst_sp); + data_cursor += inst_size; + inst_addr.Slide(inst_size); + instructions_parsed++; + } + + return data_cursor - data_offset; +} + +void +DisassemblerLLVMC::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "Disassembler that uses LLVM MC to disassemble i386, x86_64 and ARM.", + CreateInstance); + + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllDisassemblers(); +} + +void +DisassemblerLLVMC::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +ConstString +DisassemblerLLVMC::GetPluginNameStatic() +{ + static ConstString g_name("llvm-mc"); + return g_name; +} + +int DisassemblerLLVMC::OpInfoCallback (void *disassembler, + uint64_t pc, + uint64_t offset, + uint64_t size, + int tag_type, + void *tag_bug) +{ + return static_cast<DisassemblerLLVMC*>(disassembler)->OpInfo (pc, + offset, + size, + tag_type, + tag_bug); +} + +const char *DisassemblerLLVMC::SymbolLookupCallback (void *disassembler, + uint64_t value, + uint64_t *type, + uint64_t pc, + const char **name) +{ + return static_cast<DisassemblerLLVMC*>(disassembler)->SymbolLookup(value, + type, + pc, + name); +} + +int DisassemblerLLVMC::OpInfo (uint64_t PC, + uint64_t Offset, + uint64_t Size, + int tag_type, + void *tag_bug) +{ + switch (tag_type) + { + default: + break; + case 1: + bzero (tag_bug, sizeof(::LLVMOpInfo1)); + break; + } + return 0; +} + +const char *DisassemblerLLVMC::SymbolLookup (uint64_t value, + uint64_t *type_ptr, + uint64_t pc, + const char **name) +{ + if (*type_ptr) + { + if (m_exe_ctx && m_inst) + { + //std::string remove_this_prior_to_checkin; + Target *target = m_exe_ctx ? m_exe_ctx->GetTargetPtr() : NULL; + Address value_so_addr; + if (m_inst->UsingFileAddress()) + { + ModuleSP module_sp(m_inst->GetAddress().GetModule()); + if (module_sp) + module_sp->ResolveFileAddress(value, value_so_addr); + } + else if (target && !target->GetSectionLoadList().IsEmpty()) + { + target->GetSectionLoadList().ResolveLoadAddress(value, value_so_addr); + } + + if (value_so_addr.IsValid() && value_so_addr.GetSection()) + { + StreamString ss; + + value_so_addr.Dump (&ss, + target, + Address::DumpStyleResolvedDescriptionNoModule, + Address::DumpStyleSectionNameOffset); + + if (!ss.GetString().empty()) + { + m_inst->AppendComment(ss.GetString()); + } + } + } + } + + *type_ptr = LLVMDisassembler_ReferenceType_InOut_None; + *name = NULL; + return NULL; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +DisassemblerLLVMC::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DisassemblerLLVMC::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h new file mode 100644 index 000000000000..c567791866d5 --- /dev/null +++ b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h @@ -0,0 +1,166 @@ +//===-- DisassemblerLLVMC.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DisassemblerLLVMC_h_ +#define liblldb_DisassemblerLLVMC_h_ + +#include <string> + +#include "llvm-c/Disassembler.h" + +// Opaque references to C++ Objects in LLVM's MC. +namespace llvm +{ + class MCContext; + class MCInst; + class MCInstrInfo; + class MCRegisterInfo; + class MCDisassembler; + class MCInstPrinter; + class MCAsmInfo; + class MCSubtargetInfo; +} + +#include "lldb/Core/Address.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/Mutex.h" + +class InstructionLLVMC; + +class DisassemblerLLVMC : public lldb_private::Disassembler +{ + // Since we need to make two actual MC Disassemblers for ARM (ARM & THUMB), and there's a bit of goo to set up and own + // in the MC disassembler world, I added this class to manage the actual disassemblers. + class LLVMCDisassembler + { + public: + LLVMCDisassembler (const char *triple, unsigned flavor, DisassemblerLLVMC &owner); + + ~LLVMCDisassembler(); + + uint64_t GetMCInst (const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc, llvm::MCInst &mc_inst); + uint64_t PrintMCInst (llvm::MCInst &mc_inst, char *output_buffer, size_t out_buffer_len); + void SetStyle (bool use_hex_immed, HexImmediateStyle hex_style); + bool CanBranch (llvm::MCInst &mc_inst); + bool IsValid() + { + return m_is_valid; + } + + private: + bool m_is_valid; + std::unique_ptr<llvm::MCContext> m_context_ap; + std::unique_ptr<llvm::MCAsmInfo> m_asm_info_ap; + std::unique_ptr<llvm::MCSubtargetInfo> m_subtarget_info_ap; + std::unique_ptr<llvm::MCInstrInfo> m_instr_info_ap; + std::unique_ptr<llvm::MCRegisterInfo> m_reg_info_ap; + std::unique_ptr<llvm::MCInstPrinter> m_instr_printer_ap; + std::unique_ptr<llvm::MCDisassembler> m_disasm_ap; + }; + +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static lldb_private::Disassembler * + CreateInstance(const lldb_private::ArchSpec &arch, const char *flavor); + + DisassemblerLLVMC(const lldb_private::ArchSpec &arch, const char *flavor /* = NULL */); + + virtual + ~DisassemblerLLVMC(); + + virtual size_t + DecodeInstructions (const lldb_private::Address &base_addr, + const lldb_private::DataExtractor& data, + lldb::offset_t data_offset, + size_t num_instructions, + bool append, + bool data_from_file); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + friend class InstructionLLVMC; + + virtual bool + FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor); + + bool + IsValid() + { + return (m_disasm_ap.get() != NULL && m_disasm_ap->IsValid()); + } + + int OpInfo(uint64_t PC, + uint64_t Offset, + uint64_t Size, + int TagType, + void *TagBug); + + const char *SymbolLookup (uint64_t ReferenceValue, + uint64_t *ReferenceType, + uint64_t ReferencePC, + const char **ReferenceName); + + static int OpInfoCallback (void *DisInfo, + uint64_t PC, + uint64_t Offset, + uint64_t Size, + int TagType, + void *TagBug); + + static const char *SymbolLookupCallback(void *DisInfo, + uint64_t ReferenceValue, + uint64_t *ReferenceType, + uint64_t ReferencePC, + const char **ReferenceName); + + void Lock(InstructionLLVMC *inst, + const lldb_private::ExecutionContext *exe_ctx) + { + m_mutex.Lock(); + m_inst = inst; + m_exe_ctx = exe_ctx; + } + + void Unlock() + { + m_inst = NULL; + m_exe_ctx = NULL; + m_mutex.Unlock(); + } + + const lldb_private::ExecutionContext *m_exe_ctx; + InstructionLLVMC *m_inst; + lldb_private::Mutex m_mutex; + bool m_data_from_file; + + std::unique_ptr<LLVMCDisassembler> m_disasm_ap; + std::unique_ptr<LLVMCDisassembler> m_alternate_disasm_ap; +}; + +#endif // liblldb_DisassemblerLLVM_h_ diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp new file mode 100644 index 000000000000..2604ae670164 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp @@ -0,0 +1,177 @@ +//===-- AuxVector.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" + +#if defined(__linux__) or defined(__FreeBSD__) +#include "Plugins/Process/elf-core/ProcessElfCore.h" +#endif + +#include "AuxVector.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +GetMaxU64(DataExtractor &data, + lldb::offset_t *offset_ptr, + uint64_t *value, + unsigned int byte_size) +{ + lldb::offset_t saved_offset = *offset_ptr; + *value = data.GetMaxU64(offset_ptr, byte_size); + return *offset_ptr != saved_offset; +} + +static bool +ParseAuxvEntry(DataExtractor &data, + AuxVector::Entry &entry, + lldb::offset_t *offset_ptr, + unsigned int byte_size) +{ + if (!GetMaxU64(data, offset_ptr, &entry.type, byte_size)) + return false; + + if (!GetMaxU64(data, offset_ptr, &entry.value, byte_size)) + return false; + + return true; +} + +DataBufferSP +AuxVector::GetAuxvData() +{ +#if defined(__linux__) or defined(__FreeBSD__) + if (m_process->GetPluginName() == ProcessElfCore::GetPluginNameStatic()) + return static_cast<ProcessElfCore *>(m_process)->GetAuxvData(); +#endif + return lldb_private::Host::GetAuxvData(m_process); +} + +void +AuxVector::ParseAuxv(DataExtractor &data) +{ + const unsigned int byte_size = m_process->GetAddressByteSize(); + lldb::offset_t offset = 0; + + for (;;) + { + Entry entry; + + if (!ParseAuxvEntry(data, entry, &offset, byte_size)) + break; + + if (entry.type == AT_NULL) + break; + + if (entry.type == AT_IGNORE) + continue; + + m_auxv.push_back(entry); + } +} + +AuxVector::AuxVector(Process *process) + : m_process(process) +{ + DataExtractor data; + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + + data.SetData(GetAuxvData()); + data.SetByteOrder(m_process->GetByteOrder()); + data.SetAddressByteSize(m_process->GetAddressByteSize()); + + ParseAuxv(data); + + if (log) + DumpToLog(log); +} + +AuxVector::iterator +AuxVector::FindEntry(EntryType type) const +{ + for (iterator I = begin(); I != end(); ++I) + { + if (I->type == static_cast<uint64_t>(type)) + return I; + } + + return end(); +} + +void +AuxVector::DumpToLog(Log *log) const +{ + if (!log) + return; + + log->PutCString("AuxVector: "); + for (iterator I = begin(); I != end(); ++I) + { + log->Printf(" %s [%" PRIu64 "]: %" PRIx64, GetEntryName(*I), I->type, I->value); + } +} + +const char * +AuxVector::GetEntryName(EntryType type) +{ + const char *name = "AT_???"; + +#define ENTRY_NAME(_type) _type: name = #_type + switch (type) + { + case ENTRY_NAME(AT_NULL); break; + case ENTRY_NAME(AT_IGNORE); break; + case ENTRY_NAME(AT_EXECFD); break; + case ENTRY_NAME(AT_PHDR); break; + case ENTRY_NAME(AT_PHENT); break; + case ENTRY_NAME(AT_PHNUM); break; + case ENTRY_NAME(AT_PAGESZ); break; + case ENTRY_NAME(AT_BASE); break; + case ENTRY_NAME(AT_FLAGS); break; + case ENTRY_NAME(AT_ENTRY); break; + case ENTRY_NAME(AT_NOTELF); break; + case ENTRY_NAME(AT_UID); break; + case ENTRY_NAME(AT_EUID); break; + case ENTRY_NAME(AT_GID); break; + case ENTRY_NAME(AT_EGID); break; + case ENTRY_NAME(AT_CLKTCK); break; + case ENTRY_NAME(AT_PLATFORM); break; + case ENTRY_NAME(AT_HWCAP); break; + case ENTRY_NAME(AT_FPUCW); break; + case ENTRY_NAME(AT_DCACHEBSIZE); break; + case ENTRY_NAME(AT_ICACHEBSIZE); break; + case ENTRY_NAME(AT_UCACHEBSIZE); break; + case ENTRY_NAME(AT_IGNOREPPC); break; + case ENTRY_NAME(AT_SECURE); break; + case ENTRY_NAME(AT_BASE_PLATFORM); break; + case ENTRY_NAME(AT_RANDOM); break; + case ENTRY_NAME(AT_EXECFN); break; + case ENTRY_NAME(AT_SYSINFO); break; + case ENTRY_NAME(AT_SYSINFO_EHDR); break; + case ENTRY_NAME(AT_L1I_CACHESHAPE); break; + case ENTRY_NAME(AT_L1D_CACHESHAPE); break; + case ENTRY_NAME(AT_L2_CACHESHAPE); break; + case ENTRY_NAME(AT_L3_CACHESHAPE); break; + } +#undef ENTRY_NAME + + return name; +} + diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h b/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h new file mode 100644 index 000000000000..2d39eddcacc6 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h @@ -0,0 +1,115 @@ +//===-- AuxVector.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AuxVector_H_ +#define liblldb_AuxVector_H_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +#include "lldb/lldb-forward.h" + +namespace lldb_private { +class DataExtractor; +} + +/// @class AuxVector +/// @brief Represents a processes auxiliary vector. +/// +/// When a process is loaded on Linux a vector of values is placed onto the +/// stack communicating operating system specific information. On construction +/// this class locates and parses this information and provides a simple +/// read-only interface to the entries found. +class AuxVector { + +public: + AuxVector(lldb_private::Process *process); + + struct Entry { + uint64_t type; + uint64_t value; + + Entry() : type(0), value(0) { } + }; + + /// Constants describing the type of entry. + /// On Linux, running "LD_SHOW_AUXV=1 ./executable" will spew AUX information. + enum EntryType { + AT_NULL = 0, ///< End of auxv. + AT_IGNORE = 1, ///< Ignore entry. + AT_EXECFD = 2, ///< File descriptor of program. + AT_PHDR = 3, ///< Program headers. + AT_PHENT = 4, ///< Size of program header. + AT_PHNUM = 5, ///< Number of program headers. + AT_PAGESZ = 6, ///< Page size. + AT_BASE = 7, ///< Interpreter base address. + AT_FLAGS = 8, ///< Flags. + AT_ENTRY = 9, ///< Program entry point. + AT_NOTELF = 10, ///< Set if program is not an ELF. + AT_UID = 11, ///< UID. + AT_EUID = 12, ///< Effective UID. + AT_GID = 13, ///< GID. + AT_EGID = 14, ///< Effective GID. + AT_CLKTCK = 17, ///< Clock frequency (e.g. times(2)). + AT_PLATFORM = 15, ///< String identifying platform. + AT_HWCAP = 16, ///< Machine dependent hints about processor capabilities. + AT_FPUCW = 18, ///< Used FPU control word. + AT_DCACHEBSIZE = 19, ///< Data cache block size. + AT_ICACHEBSIZE = 20, ///< Instruction cache block size. + AT_UCACHEBSIZE = 21, ///< Unified cache block size. + AT_IGNOREPPC = 22, ///< Entry should be ignored. + AT_SECURE = 23, ///< Boolean, was exec setuid-like? + AT_BASE_PLATFORM = 24, ///< String identifying real platforms. + AT_RANDOM = 25, ///< Address of 16 random bytes. + AT_EXECFN = 31, ///< Filename of executable. + AT_SYSINFO = 32, ///< Pointer to the global system page used for system calls and other nice things. + AT_SYSINFO_EHDR = 33, + AT_L1I_CACHESHAPE = 34, ///< Shapes of the caches. + AT_L1D_CACHESHAPE = 35, + AT_L2_CACHESHAPE = 36, + AT_L3_CACHESHAPE = 37, + }; + +private: + typedef std::vector<Entry> EntryVector; + +public: + typedef EntryVector::const_iterator iterator; + + iterator begin() const { return m_auxv.begin(); } + iterator end() const { return m_auxv.end(); } + + iterator + FindEntry(EntryType type) const; + + static const char * + GetEntryName(const Entry &entry) { + return GetEntryName(static_cast<EntryType>(entry.type)); + } + + static const char * + GetEntryName(EntryType type); + + void + DumpToLog(lldb_private::Log *log) const; + +private: + lldb_private::Process *m_process; + EntryVector m_auxv; + + lldb::DataBufferSP + GetAuxvData(); + + void + ParseAuxv(lldb_private::DataExtractor &data); +}; + +#endif diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp new file mode 100644 index 000000000000..3e1b52938f49 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp @@ -0,0 +1,336 @@ +//===-- DYLDRendezvous.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "DYLDRendezvous.h" + +using namespace lldb; +using namespace lldb_private; + +/// Locates the address of the rendezvous structure. Returns the address on +/// success and LLDB_INVALID_ADDRESS on failure. +static addr_t +ResolveRendezvousAddress(Process *process) +{ + addr_t info_location; + addr_t info_addr; + Error error; + size_t size; + + info_location = process->GetImageInfoAddress(); + + if (info_location == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + info_addr = 0; + size = process->DoReadMemory(info_location, &info_addr, + process->GetAddressByteSize(), error); + if (size != process->GetAddressByteSize() || error.Fail()) + return LLDB_INVALID_ADDRESS; + + if (info_addr == 0) + return LLDB_INVALID_ADDRESS; + + return info_addr; +} + +DYLDRendezvous::DYLDRendezvous(Process *process) + : m_process(process), + m_rendezvous_addr(LLDB_INVALID_ADDRESS), + m_current(), + m_previous(), + m_soentries(), + m_added_soentries(), + m_removed_soentries() +{ + // Cache a copy of the executable path + if (m_process) + { + Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer(); + if (exe_mod) + exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX); + } +} + +bool +DYLDRendezvous::Resolve() +{ + const size_t word_size = 4; + Rendezvous info; + size_t address_size; + size_t padding; + addr_t info_addr; + addr_t cursor; + + address_size = m_process->GetAddressByteSize(); + padding = address_size - word_size; + + if (m_rendezvous_addr == LLDB_INVALID_ADDRESS) + cursor = info_addr = ResolveRendezvousAddress(m_process); + else + cursor = info_addr = m_rendezvous_addr; + + if (cursor == LLDB_INVALID_ADDRESS) + return false; + + if (!(cursor = ReadMemory(cursor, &info.version, word_size))) + return false; + + if (!(cursor = ReadMemory(cursor + padding, &info.map_addr, address_size))) + return false; + + if (!(cursor = ReadMemory(cursor, &info.brk, address_size))) + return false; + + if (!(cursor = ReadMemory(cursor, &info.state, word_size))) + return false; + + if (!(cursor = ReadMemory(cursor + padding, &info.ldbase, address_size))) + return false; + + // The rendezvous was successfully read. Update our internal state. + m_rendezvous_addr = info_addr; + m_previous = m_current; + m_current = info; + + return UpdateSOEntries(); +} + +bool +DYLDRendezvous::IsValid() +{ + return m_rendezvous_addr != LLDB_INVALID_ADDRESS; +} + +bool +DYLDRendezvous::UpdateSOEntries() +{ + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + // When the previous and current states are consistent this is the first + // time we have been asked to update. Just take a snapshot of the currently + // loaded modules. + if (m_previous.state == eConsistent && m_current.state == eConsistent) + return TakeSnapshot(m_soentries); + + // If we are about to add or remove a shared object clear out the current + // state and take a snapshot of the currently loaded images. + if (m_current.state == eAdd || m_current.state == eDelete) + { + assert(m_previous.state == eConsistent); + m_soentries.clear(); + m_added_soentries.clear(); + m_removed_soentries.clear(); + return TakeSnapshot(m_soentries); + } + assert(m_current.state == eConsistent); + + // Otherwise check the previous state to determine what to expect and update + // accordingly. + if (m_previous.state == eAdd) + return UpdateSOEntriesForAddition(); + else if (m_previous.state == eDelete) + return UpdateSOEntriesForDeletion(); + + return false; +} + +bool +DYLDRendezvous::UpdateSOEntriesForAddition() +{ + SOEntry entry; + iterator pos; + + assert(m_previous.state == eAdd); + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) + { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + // Only add shared libraries and not the executable. + // On Linux this is indicated by an empty path in the entry. + // On FreeBSD it is the name of the executable. + if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0) + continue; + + pos = std::find(m_soentries.begin(), m_soentries.end(), entry); + if (pos == m_soentries.end()) + { + m_soentries.push_back(entry); + m_added_soentries.push_back(entry); + } + } + + return true; +} + +bool +DYLDRendezvous::UpdateSOEntriesForDeletion() +{ + SOEntryList entry_list; + iterator pos; + + assert(m_previous.state == eDelete); + + if (!TakeSnapshot(entry_list)) + return false; + + for (iterator I = begin(); I != end(); ++I) + { + pos = std::find(entry_list.begin(), entry_list.end(), *I); + if (pos == entry_list.end()) + m_removed_soentries.push_back(*I); + } + + m_soentries = entry_list; + return true; +} + +bool +DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) +{ + SOEntry entry; + + if (m_current.map_addr == 0) + return false; + + for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) + { + if (!ReadSOEntryFromMemory(cursor, entry)) + return false; + + // Only add shared libraries and not the executable. + // On Linux this is indicated by an empty path in the entry. + // On FreeBSD it is the name of the executable. + if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0) + continue; + + entry_list.push_back(entry); + } + + return true; +} + +addr_t +DYLDRendezvous::ReadMemory(addr_t addr, void *dst, size_t size) +{ + size_t bytes_read; + Error error; + + bytes_read = m_process->DoReadMemory(addr, dst, size, error); + if (bytes_read != size || error.Fail()) + return 0; + + return addr + bytes_read; +} + +std::string +DYLDRendezvous::ReadStringFromMemory(addr_t addr) +{ + std::string str; + Error error; + size_t size; + char c; + + if (addr == LLDB_INVALID_ADDRESS) + return std::string(); + + for (;;) { + size = m_process->DoReadMemory(addr, &c, 1, error); + if (size != 1 || error.Fail()) + return std::string(); + if (c == 0) + break; + else { + str.push_back(c); + addr++; + } + } + + return str; +} + +bool +DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) +{ + size_t address_size = m_process->GetAddressByteSize(); + + entry.clear(); + + if (!(addr = ReadMemory(addr, &entry.base_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.path_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.dyn_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.next, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.prev, address_size))) + return false; + + entry.path = ReadStringFromMemory(entry.path_addr); + + return true; +} + +void +DYLDRendezvous::DumpToLog(Log *log) const +{ + int state = GetState(); + + if (!log) + return; + + log->PutCString("DYLDRendezvous:"); + log->Printf(" Address: %" PRIx64, GetRendezvousAddress()); + log->Printf(" Version: %" PRIu64, GetVersion()); + log->Printf(" Link : %" PRIx64, GetLinkMapAddress()); + log->Printf(" Break : %" PRIx64, GetBreakAddress()); + log->Printf(" LDBase : %" PRIx64, GetLDBase()); + log->Printf(" State : %s", + (state == eConsistent) ? "consistent" : + (state == eAdd) ? "add" : + (state == eDelete) ? "delete" : "unknown"); + + iterator I = begin(); + iterator E = end(); + + if (I != E) + log->PutCString("DYLDRendezvous SOEntries:"); + + for (int i = 1; I != E; ++I, ++i) + { + log->Printf("\n SOEntry [%d] %s", i, I->path.c_str()); + log->Printf(" Base : %" PRIx64, I->base_addr); + log->Printf(" Path : %" PRIx64, I->path_addr); + log->Printf(" Dyn : %" PRIx64, I->dyn_addr); + log->Printf(" Next : %" PRIx64, I->next); + log->Printf(" Prev : %" PRIx64, I->prev); + } +} diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h new file mode 100644 index 000000000000..67e7228a38de --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h @@ -0,0 +1,230 @@ +//===-- DYLDRendezvous.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Rendezvous_H_ +#define liblldb_Rendezvous_H_ + +// C Includes +// C++ Includes +#include <list> +#include <string> + +// Other libraries and framework includes +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +class Process; +} + +/// @class DYLDRendezvous +/// @brief Interface to the runtime linker. +/// +/// A structure is present in a processes memory space which is updated by the +/// runtime liker each time a module is loaded or unloaded. This class provides +/// an interface to this structure and maintains a consistent snapshot of the +/// currently loaded modules. +class DYLDRendezvous { + + // This structure is used to hold the contents of the debug rendezvous + // information (struct r_debug) as found in the inferiors memory. Note that + // the layout of this struct is not binary compatible, it is simply large + // enough to hold the information on both 32 and 64 bit platforms. + struct Rendezvous { + uint64_t version; + lldb::addr_t map_addr; + lldb::addr_t brk; + uint64_t state; + lldb::addr_t ldbase; + + Rendezvous() + : version(0), map_addr(0), brk(0), state(0), ldbase(0) { } + }; + +public: + DYLDRendezvous(lldb_private::Process *process); + + /// Update the internal snapshot of runtime linker rendezvous and recompute + /// the currently loaded modules. + /// + /// This method should be called once one start up, then once each time the + /// runtime linker enters the function given by GetBreakAddress(). + /// + /// @returns true on success and false on failure. + /// + /// @see GetBreakAddress(). + bool + Resolve(); + + /// @returns true if this rendezvous has been located in the inferiors + /// address space and false otherwise. + bool + IsValid(); + + /// @returns the address of the rendezvous structure in the inferiors + /// address space. + lldb::addr_t + GetRendezvousAddress() const { return m_rendezvous_addr; } + + /// @returns the version of the rendezvous protocol being used. + uint64_t + GetVersion() const { return m_current.version; } + + /// @returns address in the inferiors address space containing the linked + /// list of shared object descriptors. + lldb::addr_t + GetLinkMapAddress() const { return m_current.map_addr; } + + /// A breakpoint should be set at this address and Resolve called on each + /// hit. + /// + /// @returns the address of a function called by the runtime linker each + /// time a module is loaded/unloaded, or about to be loaded/unloaded. + /// + /// @see Resolve() + lldb::addr_t + GetBreakAddress() const { return m_current.brk; } + + /// Returns the current state of the rendezvous structure. + uint64_t + GetState() const { return m_current.state; } + + /// @returns the base address of the runtime linker in the inferiors address + /// space. + lldb::addr_t + GetLDBase() const { return m_current.ldbase; } + + /// @returns true if modules have been loaded into the inferior since the + /// last call to Resolve(). + bool + ModulesDidLoad() const { return !m_added_soentries.empty(); } + + /// @returns true if modules have been unloaded from the inferior since the + /// last call to Resolve(). + bool + ModulesDidUnload() const { return !m_removed_soentries.empty(); } + + void + DumpToLog(lldb_private::Log *log) const; + + /// @brief Constants describing the state of the rendezvous. + /// + /// @see GetState(). + enum RendezvousState { + eConsistent, + eAdd, + eDelete + }; + + /// @brief Structure representing the shared objects currently loaded into + /// the inferior process. + /// + /// This object is a rough analogue to the struct link_map object which + /// actually lives in the inferiors memory. + struct SOEntry { + lldb::addr_t base_addr; ///< Base address of the loaded object. + lldb::addr_t path_addr; ///< String naming the shared object. + lldb::addr_t dyn_addr; ///< Dynamic section of shared object. + lldb::addr_t next; ///< Address of next so_entry. + lldb::addr_t prev; ///< Address of previous so_entry. + std::string path; ///< File name of shared object. + + SOEntry() { clear(); } + + bool operator ==(const SOEntry &entry) { + return this->path == entry.path; + } + + void clear() { + base_addr = 0; + path_addr = 0; + dyn_addr = 0; + next = 0; + prev = 0; + path.clear(); + } + }; + +protected: + typedef std::list<SOEntry> SOEntryList; + +public: + typedef SOEntryList::const_iterator iterator; + + /// Iterators over all currently loaded modules. + iterator begin() const { return m_soentries.begin(); } + iterator end() const { return m_soentries.end(); } + + /// Iterators over all modules loaded into the inferior since the last call + /// to Resolve(). + iterator loaded_begin() const { return m_added_soentries.begin(); } + iterator loaded_end() const { return m_added_soentries.end(); } + + /// Iterators over all modules unloaded from the inferior since the last + /// call to Resolve(). + iterator unloaded_begin() const { return m_removed_soentries.begin(); } + iterator unloaded_end() const { return m_removed_soentries.end(); } + +protected: + lldb_private::Process *m_process; + + // Cached copy of executable pathname + char m_exe_path[PATH_MAX]; + + /// Location of the r_debug structure in the inferiors address space. + lldb::addr_t m_rendezvous_addr; + + /// Current and previous snapshots of the rendezvous structure. + Rendezvous m_current; + Rendezvous m_previous; + + /// List of SOEntry objects corresponding to the current link map state. + SOEntryList m_soentries; + + /// List of SOEntry's added to the link map since the last call to Resolve(). + SOEntryList m_added_soentries; + + /// List of SOEntry's removed from the link map since the last call to + /// Resolve(). + SOEntryList m_removed_soentries; + + /// Reads @p size bytes from the inferiors address space starting at @p + /// addr. + /// + /// @returns addr + size if the read was successful and false otherwise. + lldb::addr_t + ReadMemory(lldb::addr_t addr, void *dst, size_t size); + + /// Reads a null-terminated C string from the memory location starting at @p + /// addr. + std::string + ReadStringFromMemory(lldb::addr_t addr); + + /// Reads an SOEntry starting at @p addr. + bool + ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry); + + /// Updates the current set of SOEntries, the set of added entries, and the + /// set of removed entries. + bool + UpdateSOEntries(); + + bool + UpdateSOEntriesForAddition(); + + bool + UpdateSOEntriesForDeletion(); + + /// Reads the current list of shared objects according to the link map + /// supplied by the runtime linker. + bool + TakeSnapshot(SOEntryList &entry_list); +}; + +#endif diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp new file mode 100644 index 000000000000..91c7cd3dfca7 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -0,0 +1,481 @@ +//===-- DynamicLoaderPOSIX.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Breakpoint/BreakpointLocation.h" + +#include "AuxVector.h" +#include "DynamicLoaderPOSIXDYLD.h" + +using namespace lldb; +using namespace lldb_private; + +void +DynamicLoaderPOSIXDYLD::Initialize() +{ + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderPOSIXDYLD::Terminate() +{ +} + +lldb_private::ConstString +DynamicLoaderPOSIXDYLD::GetPluginName() +{ + return GetPluginNameStatic(); +} + +lldb_private::ConstString +DynamicLoaderPOSIXDYLD::GetPluginNameStatic() +{ + static ConstString g_name("linux-dyld"); + return g_name; +} + +const char * +DynamicLoaderPOSIXDYLD::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in POSIX processes."; +} + +void +DynamicLoaderPOSIXDYLD::GetPluginCommandHelp(const char *command, Stream *strm) +{ +} + +uint32_t +DynamicLoaderPOSIXDYLD::GetPluginVersion() +{ + return 1; +} + +DynamicLoader * +DynamicLoaderPOSIXDYLD::CreateInstance(Process *process, bool force) +{ + bool create = force; + if (!create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::Linux || + triple_ref.getOS() == llvm::Triple::FreeBSD) + create = true; + } + + if (create) + return new DynamicLoaderPOSIXDYLD (process); + return NULL; +} + +DynamicLoaderPOSIXDYLD::DynamicLoaderPOSIXDYLD(Process *process) + : DynamicLoader(process), + m_rendezvous(process), + m_load_offset(LLDB_INVALID_ADDRESS), + m_entry_point(LLDB_INVALID_ADDRESS), + m_auxv(), + m_dyld_bid(LLDB_INVALID_BREAK_ID) +{ +} + +DynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() +{ + if (m_dyld_bid != LLDB_INVALID_BREAK_ID) + { + m_process->GetTarget().RemoveBreakpointByID (m_dyld_bid); + m_dyld_bid = LLDB_INVALID_BREAK_ID; + } +} + +void +DynamicLoaderPOSIXDYLD::DidAttach() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = GetTargetExecutable(); + load_offset = ComputeLoadOffset(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + LoadAllCurrentModules(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +void +DynamicLoaderPOSIXDYLD::DidLaunch() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = GetTargetExecutable(); + load_offset = ComputeLoadOffset(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + ProbeEntry(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +ModuleSP +DynamicLoaderPOSIXDYLD::GetTargetExecutable() +{ + Target &target = m_process->GetTarget(); + ModuleSP executable = target.GetExecutableModule(); + + if (executable.get()) + { + if (executable->GetFileSpec().Exists()) + { + ModuleSpec module_spec (executable->GetFileSpec(), executable->GetArchitecture()); + ModuleSP module_sp (new Module (module_spec)); + + // Check if the executable has changed and set it to the target executable if they differ. + if (module_sp.get() && module_sp->GetUUID().IsValid() && executable->GetUUID().IsValid()) + { + if (module_sp->GetUUID() != executable->GetUUID()) + executable.reset(); + } + else if (executable->FileHasChanged()) + { + executable.reset(); + } + + if (!executable.get()) + { + executable = target.GetSharedModule(module_spec); + if (executable.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded + const bool get_dependent_images = false; + target.SetExecutableModule(executable, get_dependent_images); + } + } + } + } + return executable; +} + +Error +DynamicLoaderPOSIXDYLD::ExecutePluginCommand(Args &command, Stream *strm) +{ + return Error(); +} + +Log * +DynamicLoaderPOSIXDYLD::EnablePluginLogging(Stream *strm, Args &command) +{ + return NULL; +} + +Error +DynamicLoaderPOSIXDYLD::CanLoadImage() +{ + return Error(); +} + +void +DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module, addr_t base_addr) +{ + ObjectFile *obj_file = module->GetObjectFile(); + SectionList *sections = obj_file->GetSectionList(); + SectionLoadList &load_list = m_process->GetTarget().GetSectionLoadList(); + const size_t num_sections = sections->GetSize(); + + for (unsigned i = 0; i < num_sections; ++i) + { + SectionSP section_sp (sections->GetSectionAtIndex(i)); + lldb::addr_t new_load_addr = section_sp->GetFileAddress() + base_addr; + lldb::addr_t old_load_addr = load_list.GetSectionLoadAddress(section_sp); + + // If the file address of the section is zero then this is not an + // allocatable/loadable section (property of ELF sh_addr). Skip it. + if (new_load_addr == base_addr) + continue; + + if (old_load_addr == LLDB_INVALID_ADDRESS || + old_load_addr != new_load_addr) + load_list.SetSectionLoadAddress(section_sp, new_load_addr); + } +} + +void +DynamicLoaderPOSIXDYLD::ProbeEntry() +{ + Breakpoint *entry_break; + addr_t entry; + + if ((entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + return; + + entry_break = m_process->GetTarget().CreateBreakpoint(entry, true).get(); + entry_break->SetCallback(EntryBreakpointHit, this, true); + entry_break->SetBreakpointKind("shared-library-event"); +} + +// The runtime linker has run and initialized the rendezvous structure once the +// process has hit its entry point. When we hit the corresponding breakpoint we +// interrogate the rendezvous structure to get the load addresses of all +// dependent modules for the process. Similarly, we can discover the runtime +// linker function and setup a breakpoint to notify us of any dynamically loaded +// modules (via dlopen). +bool +DynamicLoaderPOSIXDYLD::EntryBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderPOSIXDYLD* dyld_instance; + + dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); + dyld_instance->LoadAllCurrentModules(); + dyld_instance->SetRendezvousBreakpoint(); + return false; // Continue running. +} + +void +DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() +{ + addr_t break_addr = m_rendezvous.GetBreakAddress(); + Target &target = m_process->GetTarget(); + + if (m_dyld_bid == LLDB_INVALID_BREAK_ID) + { + Breakpoint *dyld_break = target.CreateBreakpoint (break_addr, true).get(); + dyld_break->SetCallback(RendezvousBreakpointHit, this, true); + dyld_break->SetBreakpointKind ("shared-library-event"); + m_dyld_bid = dyld_break->GetID(); + } + + // Make sure our breakpoint is at the right address. + assert (target.GetBreakpointByID(m_dyld_bid)->FindLocationByAddress(break_addr)->GetBreakpoint().GetID() == m_dyld_bid); +} + +bool +DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderPOSIXDYLD* dyld_instance; + + dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); + dyld_instance->RefreshModules(); + + // Return true to stop the target, false to just let the target run. + return dyld_instance->GetStopWhenImagesChange(); +} + +void +DynamicLoaderPOSIXDYLD::RefreshModules() +{ + if (!m_rendezvous.Resolve()) + return; + + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + + ModuleList &loaded_modules = m_process->GetTarget().GetImages(); + + if (m_rendezvous.ModulesDidLoad()) + { + ModuleList new_modules; + + E = m_rendezvous.loaded_end(); + for (I = m_rendezvous.loaded_begin(); I != E; ++I) + { + FileSpec file(I->path.c_str(), true); + ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + if (module_sp.get()) + loaded_modules.AppendIfNeeded(module_sp); + } + } + + if (m_rendezvous.ModulesDidUnload()) + { + ModuleList old_modules; + + E = m_rendezvous.unloaded_end(); + for (I = m_rendezvous.unloaded_begin(); I != E; ++I) + { + FileSpec file(I->path.c_str(), true); + ModuleSpec module_spec (file); + ModuleSP module_sp = + loaded_modules.FindFirstModule (module_spec); + if (module_sp.get()) + old_modules.Append(module_sp); + } + loaded_modules.Remove(old_modules); + } +} + +ThreadPlanSP +DynamicLoaderPOSIXDYLD::GetStepThroughTrampolinePlan(Thread &thread, bool stop) +{ + ThreadPlanSP thread_plan_sp; + + StackFrame *frame = thread.GetStackFrameAtIndex(0).get(); + const SymbolContext &context = frame->GetSymbolContext(eSymbolContextSymbol); + Symbol *sym = context.symbol; + + if (sym == NULL || !sym->IsTrampoline()) + return thread_plan_sp; + + const ConstString &sym_name = sym->GetMangled().GetName(Mangled::ePreferMangled); + if (!sym_name) + return thread_plan_sp; + + SymbolContextList target_symbols; + Target &target = thread.GetProcess()->GetTarget(); + const ModuleList &images = target.GetImages(); + + images.FindSymbolsWithNameAndType(sym_name, eSymbolTypeCode, target_symbols); + size_t num_targets = target_symbols.GetSize(); + if (!num_targets) + return thread_plan_sp; + + typedef std::vector<lldb::addr_t> AddressVector; + AddressVector addrs; + for (size_t i = 0; i < num_targets; ++i) + { + SymbolContext context; + AddressRange range; + if (target_symbols.GetContextAtIndex(i, context)) + { + context.GetAddressRange(eSymbolContextEverything, 0, false, range); + lldb::addr_t addr = range.GetBaseAddress().GetLoadAddress(&target); + if (addr != LLDB_INVALID_ADDRESS) + addrs.push_back(addr); + } + } + + if (addrs.size() > 0) + { + AddressVector::iterator start = addrs.begin(); + AddressVector::iterator end = addrs.end(); + + std::sort(start, end); + addrs.erase(std::unique(start, end), end); + thread_plan_sp.reset(new ThreadPlanRunToAddress(thread, addrs, stop)); + } + + return thread_plan_sp; +} + +void +DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() +{ + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + ModuleList module_list; + + if (!m_rendezvous.Resolve()) + return; + + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) + { + FileSpec file(I->path.c_str(), false); + ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + if (module_sp.get()) + module_list.Append(module_sp); + } + + m_process->GetTarget().ModulesDidLoad(module_list); +} + +ModuleSP +DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(const FileSpec &file, addr_t base_addr) +{ + Target &target = m_process->GetTarget(); + ModuleList &modules = target.GetImages(); + ModuleSP module_sp; + + ModuleSpec module_spec (file, target.GetArchitecture()); + if ((module_sp = modules.FindFirstModule (module_spec))) + { + UpdateLoadedSections(module_sp, base_addr); + } + else if ((module_sp = target.GetSharedModule(module_spec))) + { + UpdateLoadedSections(module_sp, base_addr); + } + + return module_sp; +} + +addr_t +DynamicLoaderPOSIXDYLD::ComputeLoadOffset() +{ + addr_t virt_entry; + + if (m_load_offset != LLDB_INVALID_ADDRESS) + return m_load_offset; + + if ((virt_entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + ModuleSP module = m_process->GetTarget().GetExecutableModule(); + if (!module) + return LLDB_INVALID_ADDRESS; + + ObjectFile *exe = module->GetObjectFile(); + Address file_entry = exe->GetEntryPointAddress(); + + if (!file_entry.IsValid()) + return LLDB_INVALID_ADDRESS; + + m_load_offset = virt_entry - file_entry.GetFileAddress(); + return m_load_offset; +} + +addr_t +DynamicLoaderPOSIXDYLD::GetEntryPoint() +{ + if (m_entry_point != LLDB_INVALID_ADDRESS) + return m_entry_point; + + if (m_auxv.get() == NULL) + return LLDB_INVALID_ADDRESS; + + AuxVector::iterator I = m_auxv->FindEntry(AuxVector::AT_ENTRY); + + if (I == m_auxv->end()) + return LLDB_INVALID_ADDRESS; + + m_entry_point = static_cast<addr_t>(I->value); + return m_entry_point; +} diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h new file mode 100644 index 000000000000..0476e45d0465 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -0,0 +1,170 @@ +//===-- DynamicLoaderPOSIX.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderPOSIX_H_ +#define liblldb_DynamicLoaderPOSIX_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/DynamicLoader.h" + +#include "DYLDRendezvous.h" + +class AuxVector; + +class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader +{ +public: + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance(lldb_private::Process *process, bool force); + + DynamicLoaderPOSIXDYLD(lldb_private::Process *process); + + virtual + ~DynamicLoaderPOSIXDYLD(); + + //------------------------------------------------------------------ + // DynamicLoader protocol + //------------------------------------------------------------------ + + virtual void + DidAttach(); + + virtual void + DidLaunch(); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others); + + virtual lldb_private::Error + CanLoadImage(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp(const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand(lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging(lldb_private::Stream *strm, lldb_private::Args &command); + +protected: + /// Runtime linker rendezvous structure. + DYLDRendezvous m_rendezvous; + + /// Virtual load address of the inferior process. + lldb::addr_t m_load_offset; + + /// Virtual entry address of the inferior process. + lldb::addr_t m_entry_point; + + /// Auxiliary vector of the inferior process. + std::unique_ptr<AuxVector> m_auxv; + + /// Rendezvous breakpoint. + lldb::break_id_t m_dyld_bid; + + /// Enables a breakpoint on a function called by the runtime + /// linker each time a module is loaded or unloaded. + void + SetRendezvousBreakpoint(); + + /// Callback routine which updates the current list of loaded modules based + /// on the information supplied by the runtime linker. + static bool + RendezvousBreakpointHit(void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + /// Helper method for RendezvousBreakpointHit. Updates LLDB's current set + /// of loaded modules. + void + RefreshModules(); + + /// Updates the load address of every allocatable section in @p module. + /// + /// @param module The module to traverse. + /// + /// @param base_addr The virtual base address @p module is loaded at. + void + UpdateLoadedSections(lldb::ModuleSP module, + lldb::addr_t base_addr = 0); + + /// Locates or creates a module given by @p file and updates/loads the + /// resulting module at the virtual base address @p base_addr. + lldb::ModuleSP + LoadModuleAtAddress(const lldb_private::FileSpec &file, lldb::addr_t base_addr); + + /// Resolves the entry point for the current inferior process and sets a + /// breakpoint at that address. + void + ProbeEntry(); + + /// Callback routine invoked when we hit the breakpoint on process entry. + /// + /// This routine is responsible for resolving the load addresses of all + /// dependent modules required by the inferior and setting up the rendezvous + /// breakpoint. + static bool + EntryBreakpointHit(void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + /// Helper for the entry breakpoint callback. Resolves the load addresses + /// of all dependent modules. + void + LoadAllCurrentModules(); + + /// Computes a value for m_load_offset returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t + ComputeLoadOffset(); + + /// Computes a value for m_entry_point returning the computed address on + /// success and LLDB_INVALID_ADDRESS on failure. + lldb::addr_t + GetEntryPoint(); + + /// Checks to see if the target module has changed, updates the target + /// accordingly and returns the target executable module. + lldb::ModuleSP + GetTargetExecutable(); + +private: + DISALLOW_COPY_AND_ASSIGN(DynamicLoaderPOSIXDYLD); +}; + +#endif // liblldb_DynamicLoaderPOSIXDYLD_H_ diff --git a/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp b/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp new file mode 100644 index 000000000000..274ba328ad1f --- /dev/null +++ b/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp @@ -0,0 +1,209 @@ +//===-- DynamicLoaderStatic.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +#include "DynamicLoaderStatic.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Create an instance of this class. This function is filled into +// the plugin info class that gets handed out by the plugin factory and +// allows the lldb to instantiate an instance of this class. +//---------------------------------------------------------------------- +DynamicLoader * +DynamicLoaderStatic::CreateInstance (Process* process, bool force) +{ + bool create = force; + if (!create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + const llvm::Triple::OSType os_type = triple_ref.getOS(); + if ((os_type == llvm::Triple::UnknownOS)) + create = true; + } + + if (!create) + { + Module *exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *object_file = exe_module->GetObjectFile(); + if (object_file) + { + create = (object_file->GetStrata() == ObjectFile::eStrataRawImage); + } + } + } + + if (create) + return new DynamicLoaderStatic (process); + return NULL; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DynamicLoaderStatic::DynamicLoaderStatic (Process* process) : + DynamicLoader(process) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoaderStatic::~DynamicLoaderStatic() +{ +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderStatic::DidAttach () +{ + LoadAllImagesAtFileAddresses(); +} + +//------------------------------------------------------------------ +/// Called after attaching a process. +/// +/// Allow DynamicLoader plug-ins to execute some code after +/// attaching to a process. +//------------------------------------------------------------------ +void +DynamicLoaderStatic::DidLaunch () +{ + LoadAllImagesAtFileAddresses(); +} + +void +DynamicLoaderStatic::LoadAllImagesAtFileAddresses () +{ + const ModuleList &module_list = m_process->GetTarget().GetImages(); + + ModuleList loaded_module_list; + + // Disable JIT for static dynamic loader targets + m_process->SetCanJIT(false); + + Mutex::Locker mutex_locker(module_list.GetMutex()); + + const size_t num_modules = module_list.GetSize(); + for (uint32_t idx = 0; idx < num_modules; ++idx) + { + ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (idx)); + if (module_sp) + { + bool changed = false; + ObjectFile *image_object_file = module_sp->GetObjectFile(); + if (image_object_file) + { + SectionList *section_list = image_object_file->GetSectionList (); + if (section_list) + { + // All sections listed in the dyld image info structure will all + // either be fixed up already, or they will all be off by a single + // slide amount that is determined by finding the first segment + // that is at file offset zero which also has bytes (a file size + // that is greater than zero) in the object file. + + // Determine the slide amount (if any) + const size_t num_sections = section_list->GetSize(); + size_t sect_idx = 0; + for (sect_idx = 0; sect_idx < num_sections; ++sect_idx) + { + // Iterate through the object file sections to find the + // first section that starts of file offset zero and that + // has bytes in the file... + SectionSP section_sp (section_list->GetSectionAtIndex (sect_idx)); + if (section_sp) + { + if (m_process->GetTarget().GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress())) + changed = true; + } + } + } + } + + if (changed) + loaded_module_list.AppendIfNeeded (module_sp); + } + } + + m_process->GetTarget().ModulesDidLoad (loaded_module_list); +} + +ThreadPlanSP +DynamicLoaderStatic::GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) +{ + return ThreadPlanSP(); +} + +Error +DynamicLoaderStatic::CanLoadImage () +{ + Error error; + error.SetErrorString ("can't load images on with a static debug session"); + return error; +} + +void +DynamicLoaderStatic::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderStatic::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +DynamicLoaderStatic::GetPluginNameStatic() +{ + static ConstString g_name("static"); + return g_name; +} + +const char * +DynamicLoaderStatic::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that will load any images at the static addresses contained in each image."; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +DynamicLoaderStatic::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DynamicLoaderStatic::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h b/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h new file mode 100644 index 000000000000..a99435fa32ad --- /dev/null +++ b/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h @@ -0,0 +1,88 @@ +//===-- DynamicLoaderStatic.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderStatic_h_ +#define liblldb_DynamicLoaderStatic_h_ + +// C Includes +// C++ Includes +#include <map> +#include <vector> +#include <string> + +// Other libraries and framework includes +#include "llvm/Support/MachO.h" + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" + +class DynamicLoaderStatic : public lldb_private::DynamicLoader +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance (lldb_private::Process *process, bool force); + + DynamicLoaderStatic (lldb_private::Process *process); + + virtual + ~DynamicLoaderStatic (); + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + virtual void + DidAttach (); + + virtual void + DidLaunch (); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (lldb_private::Thread &thread, + bool stop_others); + + virtual lldb_private::Error + CanLoadImage (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +private: + void + LoadAllImagesAtFileAddresses (); + + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderStatic); +}; + +#endif // liblldb_DynamicLoaderStatic_h_ diff --git a/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp new file mode 100644 index 000000000000..2dd04dd8733d --- /dev/null +++ b/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp @@ -0,0 +1,13625 @@ +//===-- EmulateInstructionARM.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> + +#include "EmulateInstructionARM.h" +#include "EmulationStateARM.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Symbol/UnwindPlan.h" + +#include "Plugins/Process/Utility/ARMDefines.h" +#include "Plugins/Process/Utility/ARMUtils.h" +#include "Utility/ARM_DWARF_Registers.h" + +#include "llvm/Support/MathExtras.h" // for SignExtend32 template function + // and countTrailingZeros function + +using namespace lldb; +using namespace lldb_private; + +// Convenient macro definitions. +#define APSR_C Bit32(m_opcode_cpsr, CPSR_C_POS) +#define APSR_V Bit32(m_opcode_cpsr, CPSR_V_POS) + +#define AlignPC(pc_val) (pc_val & 0xFFFFFFFC) + +//---------------------------------------------------------------------- +// +// ITSession implementation +// +//---------------------------------------------------------------------- + +// A8.6.50 +// Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition. +static uint32_t +CountITSize (uint32_t ITMask) { + // First count the trailing zeros of the IT mask. + uint32_t TZ = llvm::countTrailingZeros(ITMask); + if (TZ > 3) + { +#ifdef LLDB_CONFIGURATION_DEBUG + printf("Encoding error: IT Mask '0000'\n"); +#endif + return 0; + } + return (4 - TZ); +} + +// Init ITState. Note that at least one bit is always 1 in mask. +bool ITSession::InitIT(uint32_t bits7_0) +{ + ITCounter = CountITSize(Bits32(bits7_0, 3, 0)); + if (ITCounter == 0) + return false; + + // A8.6.50 IT + unsigned short FirstCond = Bits32(bits7_0, 7, 4); + if (FirstCond == 0xF) + { +#ifdef LLDB_CONFIGURATION_DEBUG + printf("Encoding error: IT FirstCond '1111'\n"); +#endif + return false; + } + if (FirstCond == 0xE && ITCounter != 1) + { +#ifdef LLDB_CONFIGURATION_DEBUG + printf("Encoding error: IT FirstCond '1110' && Mask != '1000'\n"); +#endif + return false; + } + + ITState = bits7_0; + return true; +} + +// Update ITState if necessary. +void ITSession::ITAdvance() +{ + //assert(ITCounter); + --ITCounter; + if (ITCounter == 0) + ITState = 0; + else + { + unsigned short NewITState4_0 = Bits32(ITState, 4, 0) << 1; + SetBits32(ITState, 4, 0, NewITState4_0); + } +} + +// Return true if we're inside an IT Block. +bool ITSession::InITBlock() +{ + return ITCounter != 0; +} + +// Return true if we're the last instruction inside an IT Block. +bool ITSession::LastInITBlock() +{ + return ITCounter == 1; +} + +// Get condition bits for the current thumb instruction. +uint32_t ITSession::GetCond() +{ + if (InITBlock()) + return Bits32(ITState, 7, 4); + else + return COND_AL; +} + +// ARM constants used during decoding +#define REG_RD 0 +#define LDM_REGLIST 1 +#define SP_REG 13 +#define LR_REG 14 +#define PC_REG 15 +#define PC_REGLIST_BIT 0x8000 + +#define ARMv4 (1u << 0) +#define ARMv4T (1u << 1) +#define ARMv5T (1u << 2) +#define ARMv5TE (1u << 3) +#define ARMv5TEJ (1u << 4) +#define ARMv6 (1u << 5) +#define ARMv6K (1u << 6) +#define ARMv6T2 (1u << 7) +#define ARMv7 (1u << 8) +#define ARMv7S (1u << 9) +#define ARMv8 (1u << 10) +#define ARMvAll (0xffffffffu) + +#define ARMV4T_ABOVE (ARMv4T|ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV5_ABOVE (ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV5TE_ABOVE (ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV5J_ABOVE (ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV6_ABOVE (ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV6T2_ABOVE (ARMv6T2|ARMv7|ARMv7S|ARMv8) +#define ARMV7_ABOVE (ARMv7|ARMv7S|ARMv8) + +#define No_VFP 0 +#define VFPv1 (1u << 1) +#define VFPv2 (1u << 2) +#define VFPv3 (1u << 3) +#define AdvancedSIMD (1u << 4) + +#define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD) +#define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD) +#define VFPv2v3 (VFPv2 | VFPv3) + +//---------------------------------------------------------------------- +// +// EmulateInstructionARM implementation +// +//---------------------------------------------------------------------- + +void +EmulateInstructionARM::Initialize () +{ + PluginManager::RegisterPlugin (GetPluginNameStatic (), + GetPluginDescriptionStatic (), + CreateInstance); +} + +void +EmulateInstructionARM::Terminate () +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +ConstString +EmulateInstructionARM::GetPluginNameStatic () +{ + static ConstString g_name("arm"); + return g_name; +} + +const char * +EmulateInstructionARM::GetPluginDescriptionStatic () +{ + return "Emulate instructions for the ARM architecture."; +} + +EmulateInstruction * +EmulateInstructionARM::CreateInstance (const ArchSpec &arch, InstructionType inst_type) +{ + if (EmulateInstructionARM::SupportsEmulatingIntructionsOfTypeStatic(inst_type)) + { + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + std::unique_ptr<EmulateInstructionARM> emulate_insn_ap (new EmulateInstructionARM (arch)); + + if (emulate_insn_ap.get()) + return emulate_insn_ap.release(); + } + else if (arch.GetTriple().getArch() == llvm::Triple::thumb) + { + std::unique_ptr<EmulateInstructionARM> emulate_insn_ap (new EmulateInstructionARM (arch)); + + if (emulate_insn_ap.get()) + return emulate_insn_ap.release(); + } + } + + return NULL; +} + +bool +EmulateInstructionARM::SetTargetTriple (const ArchSpec &arch) +{ + if (arch.GetTriple().getArch () == llvm::Triple::arm) + return true; + else if (arch.GetTriple().getArch () == llvm::Triple::thumb) + return true; + + return false; +} + +// Write "bits (32) UNKNOWN" to memory address "address". Helper function for many ARM instructions. +bool +EmulateInstructionARM::WriteBits32UnknownToMemory (addr_t address) +{ + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextWriteMemoryRandomBits; + context.SetNoArgs (); + + uint32_t random_data = rand (); + const uint32_t addr_byte_size = GetAddressByteSize(); + + if (!MemAWrite (context, address, random_data, addr_byte_size)) + return false; + + return true; +} + +// Write "bits (32) UNKNOWN" to register n. Helper function for many ARM instructions. +bool +EmulateInstructionARM::WriteBits32Unknown (int n) +{ + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextWriteRegisterRandomBits; + context.SetNoArgs (); + + bool success; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + + return true; +} + +bool +EmulateInstructionARM::GetRegisterInfo (uint32_t reg_kind, uint32_t reg_num, RegisterInfo ®_info) +{ + if (reg_kind == eRegisterKindGeneric) + { + switch (reg_num) + { + case LLDB_REGNUM_GENERIC_PC: reg_kind = eRegisterKindDWARF; reg_num = dwarf_pc; break; + case LLDB_REGNUM_GENERIC_SP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_sp; break; + case LLDB_REGNUM_GENERIC_FP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_r7; break; + case LLDB_REGNUM_GENERIC_RA: reg_kind = eRegisterKindDWARF; reg_num = dwarf_lr; break; + case LLDB_REGNUM_GENERIC_FLAGS: reg_kind = eRegisterKindDWARF; reg_num = dwarf_cpsr; break; + default: return false; + } + } + + if (reg_kind == eRegisterKindDWARF) + return GetARMDWARFRegisterInfo(reg_num, reg_info); + return false; +} + +uint32_t +EmulateInstructionARM::GetFramePointerRegisterNumber () const +{ + if (m_opcode_mode == eModeThumb) + { + switch (m_arch.GetTriple().getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + return 7; + default: + break; + } + } + return 11; +} + +uint32_t +EmulateInstructionARM::GetFramePointerDWARFRegisterNumber () const +{ + if (m_opcode_mode == eModeThumb) + { + switch (m_arch.GetTriple().getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + return dwarf_r7; + default: + break; + } + } + return dwarf_r11; +} + +// Push Multiple Registers stores multiple registers to the stack, storing to +// consecutive memory locations ending just below the address in SP, and updates +// SP to point to the start of the stored data. +bool +EmulateInstructionARM::EmulatePUSH (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + NullCheckIfThumbEE(13); + address = SP - 4*BitCount(registers); + + for (i = 0 to 14) + { + if (registers<i> == '1') + { + if i == 13 && i != LowestSetBit(registers) // Only possible for encoding A1 + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + } + } + + if (registers<15> == '1') // Only possible for encoding A1 or A2 + MemA[address,4] = PCStoreValue(); + + SP = SP - 4*BitCount(registers); + } +#endif + + bool conditional = false; + bool success = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t registers = 0; + uint32_t Rt; // the source register + switch (encoding) { + case eEncodingT1: + registers = Bits32(opcode, 7, 0); + // The M bit represents LR. + if (Bit32(opcode, 8)) + registers |= (1u << 14); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // Ignore bits 15 & 13. + registers = Bits32(opcode, 15, 0) & ~0xa000; + // if BitCount(registers) < 2 then UNPREDICTABLE; + if (BitCount(registers) < 2) + return false; + break; + case eEncodingT3: + Rt = Bits32(opcode, 15, 12); + // if BadReg(t) then UNPREDICTABLE; + if (BadReg(Rt)) + return false; + registers = (1u << Rt); + break; + case eEncodingA1: + registers = Bits32(opcode, 15, 0); + // Instead of return false, let's handle the following case as well, + // which amounts to pushing one reg onto the full descending stacks. + // if BitCount(register_list) < 2 then SEE STMDB / STMFD; + break; + case eEncodingA2: + Rt = Bits32(opcode, 15, 12); + // if t == 13 then UNPREDICTABLE; + if (Rt == dwarf_sp) + return false; + registers = (1u << Rt); + break; + default: + return false; + } + addr_t sp_offset = addr_byte_size * BitCount (registers); + addr_t addr = sp - sp_offset; + uint32_t i; + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + context.type = EmulateInstruction::eContextPushRegisterOnStack; + RegisterInfo reg_info; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + for (i=0; i<15; ++i) + { + if (BitIsSet (registers, i)) + { + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, reg_info); + context.SetRegisterToRegisterPlusOffset (reg_info, sp_reg, addr - sp); + uint32_t reg_value = ReadCoreReg(i, &success); + if (!success) + return false; + if (!MemAWrite (context, addr, reg_value, addr_byte_size)) + return false; + addr += addr_byte_size; + } + } + + if (BitIsSet (registers, 15)) + { + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, reg_info); + context.SetRegisterToRegisterPlusOffset (reg_info, sp_reg, addr - sp); + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + if (!MemAWrite (context, addr, pc, addr_byte_size)) + return false; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (-sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp - sp_offset)) + return false; + } + return true; +} + +// Pop Multiple Registers loads multiple registers from the stack, loading from +// consecutive memory locations staring at the address in SP, and updates +// SP to point just above the loaded data. +bool +EmulateInstructionARM::EmulatePOP (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(13); + address = SP; + for i = 0 to 14 + if registers<i> == '1' then + R[i] = if UnalignedAllowed then MemU[address,4] else MemA[address,4]; address = address + 4; + if registers<15> == '1' then + if UnalignedAllowed then + LoadWritePC(MemU[address,4]); + else + LoadWritePC(MemA[address,4]); + if registers<13> == '0' then SP = SP + 4*BitCount(registers); + if registers<13> == '1' then SP = bits(32) UNKNOWN; + } +#endif + + bool success = false; + + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t registers = 0; + uint32_t Rt; // the destination register + switch (encoding) { + case eEncodingT1: + registers = Bits32(opcode, 7, 0); + // The P bit represents PC. + if (Bit32(opcode, 8)) + registers |= (1u << 15); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // Ignore bit 13. + registers = Bits32(opcode, 15, 0) & ~0x2000; + // if BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; + if (BitCount(registers) < 2 || (Bit32(opcode, 15) && Bit32(opcode, 14))) + return false; + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingT3: + Rt = Bits32(opcode, 15, 12); + // if t == 13 || (t == 15 && InITBlock() && !LastInITBlock()) then UNPREDICTABLE; + if (Rt == 13) + return false; + if (Rt == 15 && InITBlock() && !LastInITBlock()) + return false; + registers = (1u << Rt); + break; + case eEncodingA1: + registers = Bits32(opcode, 15, 0); + // Instead of return false, let's handle the following case as well, + // which amounts to popping one reg from the full descending stacks. + // if BitCount(register_list) < 2 then SEE LDM / LDMIA / LDMFD; + + // if registers<13> == '1' && ArchVersion() >= 7 then UNPREDICTABLE; + if (BitIsSet(opcode, 13) && ArchVersion() >= ARMv7) + return false; + break; + case eEncodingA2: + Rt = Bits32(opcode, 15, 12); + // if t == 13 then UNPREDICTABLE; + if (Rt == dwarf_sp) + return false; + registers = (1u << Rt); + break; + default: + return false; + } + addr_t sp_offset = addr_byte_size * BitCount (registers); + addr_t addr = sp; + uint32_t i, data; + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + + for (i=0; i<15; ++i) + { + if (BitIsSet (registers, i)) + { + context.SetRegisterPlusOffset (sp_reg, addr - sp); + data = MemARead(context, addr, 4, 0, &success); + if (!success) + return false; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + addr += addr_byte_size; + } + } + + if (BitIsSet (registers, 15)) + { + context.SetRegisterPlusOffset (sp_reg, addr - sp); + data = MemARead(context, addr, 4, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + //addr += addr_byte_size; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp + sp_offset)) + return false; + } + return true; +} + +// Set r7 or ip to point to saved value residing within the stack. +// ADD (SP plus immediate) +bool +EmulateInstructionARM::EmulateADDRdSPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t Rd; // the destination register + uint32_t imm32; + switch (encoding) { + case eEncodingT1: + Rd = 7; + imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32) + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t sp_offset = imm32; + addr_t addr = sp + sp_offset; // a pointer to the stack area + + EmulateInstruction::Context context; + context.type = eContextSetFramePointer; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + context.SetRegisterPlusOffset (sp_reg, sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rd, addr)) + return false; + } + return true; +} + +// Set r7 or ip to the current stack pointer. +// MOV (register) +bool +EmulateInstructionARM::EmulateMOVRdSP (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = R[m]; + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + // APSR.C unchanged + // APSR.V unchanged + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t Rd; // the destination register + switch (encoding) { + case eEncodingT1: + Rd = 7; + break; + case eEncodingA1: + Rd = 12; + break; + default: + return false; + } + + EmulateInstruction::Context context; + if (Rd == GetFramePointerRegisterNumber()) + context.type = EmulateInstruction::eContextSetFramePointer; + else + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + context.SetRegisterPlusOffset (sp_reg, 0); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rd, sp)) + return false; + } + return true; +} + +// Move from high register (r8-r15) to low register (r0-r7). +// MOV (register) +bool +EmulateInstructionARM::EmulateMOVLowHigh (const uint32_t opcode, const ARMEncoding encoding) +{ + return EmulateMOVRdRm (opcode, encoding); +} + +// Move from register to register. +// MOV (register) +bool +EmulateInstructionARM::EmulateMOVRdRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = R[m]; + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + // APSR.C unchanged + // APSR.V unchanged + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rm; // the source register + uint32_t Rd; // the destination register + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + setflags = false; + if (Rd == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingT2: + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = true; + if (InITBlock()) + return false; + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + // if setflags && (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (setflags && (BadReg(Rd) || BadReg(Rm))) + return false; + // if !setflags && (d == 15 || m == 15 || (d == 13 && m == 13)) then UNPREDICTABLE; + if (!setflags && (Rd == 15 || Rm == 15 || (Rd == 13 && Rm == 13))) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + uint32_t result = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // The context specifies that Rm is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterLoad; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags)) + return false; + } + return true; +} + +// Move (immediate) writes an immediate value to the destination register. It +// can optionally update the condition flags based on the value. +// MOV (immediate) +bool +EmulateInstructionARM::EmulateMOVRdImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) + { + uint32_t Rd; // the destination register + uint32_t imm32; // the immediate value to be written to Rd + uint32_t carry = 0; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C. + // for setflags == false, this value is a don't care + // initialized to 0 to silence the static analyzer + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) + carry = APSR_C; + + break; + + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); + if (BadReg(Rd)) + return false; + + break; + + case eEncodingT3: + { + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:i:imm3:imm8, 32); + Rd = Bits32 (opcode, 11, 8); + setflags = false; + uint32_t imm4 = Bits32 (opcode, 19, 16); + uint32_t imm3 = Bits32 (opcode, 14, 12); + uint32_t i = Bit32 (opcode, 26); + uint32_t imm8 = Bits32 (opcode, 7, 0); + imm32 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + + // if BadReg(d) then UNPREDICTABLE; + if (BadReg (Rd)) + return false; + } + break; + + case eEncodingA1: + // d = UInt(Rd); setflags = (S == Ô1Õ); (imm32, carry) = ARMExpandImm_C(imm12, APSR.C); + Rd = Bits32 (opcode, 15, 12); + setflags = BitIsSet (opcode, 20); + imm32 = ARMExpandImm_C (opcode, APSR_C, carry); + + // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; + if ((Rd == 15) && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + + break; + + case eEncodingA2: + { + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:imm12, 32); + Rd = Bits32 (opcode, 15, 12); + setflags = false; + uint32_t imm4 = Bits32 (opcode, 19, 16); + uint32_t imm12 = Bits32 (opcode, 11, 0); + imm32 = (imm4 << 12) | imm12; + + // if d == 15 then UNPREDICTABLE; + if (Rd == 15) + return false; + } + break; + + default: + return false; + } + uint32_t result = imm32; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// MUL multiplies two register values. The least significant 32 bits of the result are written to the destination +// register. These 32 bits do not depend on whether the source register values are considered to be signed values or +// unsigned values. +// +// Optionally, it can update the condition flags based on the result. In the Thumb instruction set, this option is +// limited to only a few forms of the instruction. +bool +EmulateInstructionARM::EmulateMUL (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results + operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results + result = operand1 * operand2; + R[d] = result<31:0>; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + if ArchVersion() == 4 then + APSR.C = bit UNKNOWN; + // else APSR.C unchanged + // APSR.V always unchanged +#endif + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t n; + uint32_t m; + bool setflags; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rdm); n = UInt(Rn); m = UInt(Rdm); setflags = !InITBlock(); + d = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 2, 0); + setflags = !InITBlock(); + + // if ArchVersion() < 6 && d == n then UNPREDICTABLE; + if ((ArchVersion() < ARMv6) && (d == n)) + return false; + + break; + + case eEncodingT2: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = FALSE; + d = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + setflags = false; + + // if BadReg(d) || BadReg(n) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (n) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1'); + d = Bits32 (opcode, 19, 16); + n = Bits32 (opcode, 3, 0); + m = Bits32 (opcode, 11, 8); + setflags = BitIsSet (opcode, 20); + + // if d == 15 || n == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (n == 15) || (m == 15)) + return false; + + // if ArchVersion() < 6 && d == n then UNPREDICTABLE; + if ((ArchVersion() < ARMv6) && (d == n)) + return false; + + break; + + default: + return false; + } + + bool success = false; + + // operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results + uint64_t operand1 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results + uint64_t operand2 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // result = operand1 * operand2; + uint64_t result = operand1 * operand2; + + // R[d] = result<31:0>; + RegisterInfo op1_reg; + RegisterInfo op2_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, op1_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, op2_reg); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + context.SetRegisterRegisterOperands (op1_reg, op2_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (0x0000ffff & result))) + return false; + + // if setflags then + if (setflags) + { + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + m_new_inst_cpsr = m_opcode_cpsr; + SetBit32 (m_new_inst_cpsr, CPSR_N_POS, Bit32 (result, 31)); + SetBit32 (m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0); + if (m_new_inst_cpsr != m_opcode_cpsr) + { + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) + return false; + } + + // if ArchVersion() == 4 then + // APSR.C = bit UNKNOWN; + } + } + return true; +} + +// Bitwise NOT (immediate) writes the bitwise inverse of an immediate value to the destination register. +// It can optionally update the condition flags based on the value. +bool +EmulateInstructionARM::EmulateMVNImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = NOT(imm32); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) + { + uint32_t Rd; // the destination register + uint32_t imm32; // the output after ThumbExpandImm_C or ARMExpandImm_C + uint32_t carry; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + uint32_t result = ~imm32; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise NOT (register) writes the bitwise inverse of a register value to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateMVNReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = NOT(shifted); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) + { + uint32_t Rm; // the source register + uint32_t Rd; // the destination register + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; // the carry bit after the shift operation + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + if (InITBlock()) + return false; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (BadReg(Rd) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + bool success = false; + uint32_t value = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(value, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = ~shifted; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// PC relative immediate load into register, possibly followed by ADD (SP plus register). +// LDR (literal) +bool +EmulateInstructionARM::EmulateLDRRtPCRelative (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,4]; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + if CurrentInstrSet() == InstrSet_ARM then + R[t] = ROR(data, 8*UInt(address<1:0>)); + else + R[t] = bits(32) UNKNOWN; + } +#endif + + if (ConditionPassed(opcode)) + { + bool success = false; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + // PC relative immediate load context + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 0); + + uint32_t Rt; // the destination register + uint32_t imm32; // immediate offset from the PC + bool add; // +imm32 or -imm32? + addr_t base; // the base address + addr_t address; // the PC relative address + uint32_t data; // the literal data value from the PC relative load + switch (encoding) { + case eEncodingT1: + Rt = Bits32(opcode, 10, 8); + imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32); + add = true; + break; + case eEncodingT2: + Rt = Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 11, 0) << 2; // imm32 = ZeroExtend(imm12, 32); + add = BitIsSet(opcode, 23); + if (Rt == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + default: + return false; + } + + base = Align(pc, 4); + if (add) + address = base + imm32; + else + address = base - imm32; + + context.SetRegisterPlusOffset(pc_reg, address - base); + data = MemURead(context, address, 4, 0, &success); + if (!success) + return false; + + if (Rt == 15) + { + if (Bits32(address, 1, 0) == 0) + { + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + else + return false; + } + else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rt, data)) + return false; + } + else // We don't handle ARM for now. + return false; + + } + return true; +} + +// An add operation to adjust the SP. +// ADD (SP plus immediate) +bool +EmulateInstructionARM::EmulateADDSPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t imm32; // the immediate operand + uint32_t d; + //bool setflags = false; // Add this back if/when support eEncodingT3 eEncodingA1 + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm8:'00', 32); + d = Bits32 (opcode, 10, 8); + imm32 = (Bits32 (opcode, 7, 0) << 2); + + break; + + case eEncodingT2: + // d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32); + d = 13; + imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) + + break; + + default: + return false; + } + addr_t sp_offset = imm32; + addr_t addr = sp + sp_offset; // the adjusted stack pointer value + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAdjustStackPointer; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + context.SetRegisterPlusOffset (sp_reg, sp_offset); + + if (d == 15) + { + if (!ALUWritePC (context, addr)) + return false; + } + else + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, addr)) + return false; + + // Add this back if/when support eEncodingT3 eEncodingA1 + //if (setflags) + //{ + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + //} + } + } + return true; +} + +// An add operation to adjust the SP. +// ADD (SP plus register) +bool +EmulateInstructionARM::EmulateADDSPRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(SP, shifted, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t Rm; // the second operand + switch (encoding) { + case eEncodingT2: + Rm = Bits32(opcode, 6, 3); + break; + default: + return false; + } + int32_t reg_value = ReadCoreReg(Rm, &success); + if (!success) + return false; + + addr_t addr = (int32_t)sp + reg_value; // the adjusted stack pointer value + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + + RegisterInfo other_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, other_reg); + context.SetRegisterRegisterOperands (sp_reg, other_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, addr)) + return false; + } + return true; +} + +// Branch with Link and Exchange Instruction Sets (immediate) calls a subroutine +// at a PC-relative address, and changes instruction set from ARM to Thumb, or +// from Thumb to ARM. +// BLX (immediate) +bool +EmulateInstructionARM::EmulateBLXImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + if CurrentInstrSet() == InstrSet_ARM then + LR = PC - 4; + else + LR = PC<31:1> : '1'; + if targetInstrSet == InstrSet_ARM then + targetAddress = Align(PC,4) + imm32; + else + targetAddress = PC + imm32; + SelectInstrSet(targetInstrSet); + BranchWritePC(targetAddress); + } +#endif + + bool success = true; + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + addr_t lr; // next instruction address + addr_t target; // target address + int32_t imm32; // PC-relative offset + switch (encoding) { + case eEncodingT1: + { + lr = pc | 1u; // return address + uint32_t S = Bit32(opcode, 26); + uint32_t imm10 = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<25>(imm25); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + if (InITBlock() && !LastInITBlock()) + return false; + break; + } + case eEncodingT2: + { + lr = pc | 1u; // return address + uint32_t S = Bit32(opcode, 26); + uint32_t imm10H = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm10L = Bits32(opcode, 10, 1); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10H << 12) | (imm10L << 2); + imm32 = llvm::SignExtend32<25>(imm25); + target = Align(pc, 4) + imm32; + context.SetISAAndImmediateSigned (eModeARM, 4 + imm32); + if (InITBlock() && !LastInITBlock()) + return false; + break; + } + case eEncodingA1: + lr = pc - 4; // return address + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); + target = Align(pc, 4) + imm32; + context.SetISAAndImmediateSigned (eModeARM, 8 + imm32); + break; + case eEncodingA2: + lr = pc - 4; // return address + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2 | Bits32(opcode, 24, 24) << 1); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 8 + imm32); + break; + default: + return false; + } + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) + return false; + if (!BranchWritePC(context, target)) + return false; + } + return true; +} + +// Branch with Link and Exchange (register) calls a subroutine at an address and +// instruction set specified by a register. +// BLX (register) +bool +EmulateInstructionARM::EmulateBLXRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + target = R[m]; + if CurrentInstrSet() == InstrSet_ARM then + next_instr_addr = PC - 4; + LR = next_instr_addr; + else + next_instr_addr = PC - 2; + LR = next_instr_addr<31:1> : '1'; + BXWritePC(target); + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + addr_t lr; // next instruction address + if (!success) + return false; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + lr = (pc - 2) | 1u; // return address + Rm = Bits32(opcode, 6, 3); + // if m == 15 then UNPREDICTABLE; + if (Rm == 15) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + lr = pc - 4; // return address + Rm = Bits32(opcode, 3, 0); + // if m == 15 then UNPREDICTABLE; + if (Rm == 15) + return false; + break; + default: + return false; + } + addr_t target = ReadCoreReg (Rm, &success); + if (!success) + return false; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) + return false; + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Branch and Exchange causes a branch to an address and instruction set specified by a register. +bool +EmulateInstructionARM::EmulateBXRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + BXWritePC(R[m]); + } +#endif + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + Rm = Bits32(opcode, 6, 3); + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rm = Bits32(opcode, 3, 0); + break; + default: + return false; + } + bool success = false; + addr_t target = ReadCoreReg (Rm, &success); + if (!success) + return false; + + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Branch and Exchange Jazelle attempts to change to Jazelle state. If the attempt fails, it branches to an +// address and instruction set specified by a register as though it were a BX instruction. +// +// TODO: Emulate Jazelle architecture? +// We currently assume that switching to Jazelle state fails, thus treating BXJ as a BX operation. +bool +EmulateInstructionARM::EmulateBXJRm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + if JMCR.JE == '0' || CurrentInstrSet() == InstrSet_ThumbEE then + BXWritePC(R[m]); + else + if JazelleAcceptsExecution() then + SwitchToJazelleExecution(); + else + SUBARCHITECTURE_DEFINED handler call; + } +#endif + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + Rm = Bits32(opcode, 19, 16); + if (BadReg(Rm)) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rm = Bits32(opcode, 3, 0); + if (Rm == 15) + return false; + break; + default: + return false; + } + bool success = false; + addr_t target = ReadCoreReg (Rm, &success); + if (!success) + return false; + + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Set r7 to point to some ip offset. +// SUB (immediate) +bool +EmulateInstructionARM::EmulateSUBR7IPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + if (ConditionPassed(opcode)) + { + bool success = false; + const addr_t ip = ReadCoreReg (12, &success); + if (!success) + return false; + uint32_t imm32; + switch (encoding) { + case eEncodingA1: + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t ip_offset = imm32; + addr_t addr = ip - ip_offset; // the adjusted ip value + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r12, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, -ip_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r7, addr)) + return false; + } + return true; +} + +// Set ip to point to some stack offset. +// SUB (SP minus immediate) +bool +EmulateInstructionARM::EmulateSUBIPSPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + if (ConditionPassed(opcode)) + { + bool success = false; + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t imm32; + switch (encoding) { + case eEncodingA1: + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t sp_offset = imm32; + addr_t addr = sp - sp_offset; // the adjusted stack pointer value + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, -sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r12, addr)) + return false; + } + return true; +} + +// This instruction subtracts an immediate value from the SP value, and writes +// the result to the destination register. +// +// If Rd == 13 => A sub operation to adjust the SP -- allocate space for local storage. +bool +EmulateInstructionARM::EmulateSUBSPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + if (ConditionPassed(opcode)) + { + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + + uint32_t Rd; + bool setflags; + uint32_t imm32; + switch (encoding) { + case eEncodingT1: + Rd = 13; + setflags = false; + imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rd == 15 && setflags) + return EmulateCMPImm(opcode, eEncodingT2); + if (Rd == 15 && !setflags) + return false; + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + setflags = false; + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + if (Rd == 15) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + AddWithCarryResult res = AddWithCarry(sp, ~imm32, 1); + + EmulateInstruction::Context context; + if (Rd == 13) + { + uint64_t imm64 = imm32; // Need to expand it to 64 bits before attempting to negate it, or the wrong + // value gets passed down to context.SetImmediateSigned. + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (-imm64); // the stack pointer offset + } + else + { + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + } + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// A store operation to the stack that also updates the SP. +bool +EmulateInstructionARM::EmulateSTRRtSP (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + if wback then R[n] = offset_addr; + } +#endif + + bool conditional = false; + bool success = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + uint32_t Rt; // the source register + uint32_t imm12; + uint32_t Rn; // This function assumes Rn is the SP, but we should verify that. + + bool index; + bool add; + bool wback; + switch (encoding) { + case eEncodingA1: + Rt = Bits32(opcode, 15, 12); + imm12 = Bits32(opcode, 11, 0); + Rn = Bits32 (opcode, 19, 16); + + if (Rn != 13) // 13 is the SP reg on ARM. Verify that Rn == SP. + return false; + + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + if (wback && ((Rn == 15) || (Rn == Rt))) + return false; + break; + default: + return false; + } + addr_t offset_addr; + if (add) + offset_addr = sp + imm12; + else + offset_addr = sp - imm12; + + addr_t addr; + if (index) + addr = offset_addr; + else + addr = sp; + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + context.type = EmulateInstruction::eContextPushRegisterOnStack; + RegisterInfo sp_reg; + RegisterInfo dwarf_reg; + + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rt, dwarf_reg); + context.SetRegisterToRegisterPlusOffset ( dwarf_reg, sp_reg, addr - sp); + if (Rt != 15) + { + uint32_t reg_value = ReadCoreReg(Rt, &success); + if (!success) + return false; + if (!MemUWrite (context, addr, reg_value, addr_byte_size)) + return false; + } + else + { + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + if (!MemUWrite (context, addr, pc, addr_byte_size)) + return false; + } + + + if (wback) + { + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (addr - sp); + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, offset_addr)) + return false; + } + } + return true; +} + +// Vector Push stores multiple extension registers to the stack. +// It also updates SP to point to the start of the stored data. +bool +EmulateInstructionARM::EmulateVPUSH (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13); + address = SP - imm32; + SP = SP - imm32; + if single_regs then + for r = 0 to regs-1 + MemA[address,4] = S[d+r]; address = address+4; + else + for r = 0 to regs-1 + // Store as two word-aligned words in the correct order for current endianness. + MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; + address = address+8; + } +#endif + + bool success = false; + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + bool single_regs; + uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register + uint32_t imm32; // stack offset + uint32_t regs; // number of registers + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + single_regs = false; + d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + // If UInt(imm8) is odd, see "FSTMX". + regs = Bits32(opcode, 7, 0) / 2; + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + case eEncodingT2: + case eEncodingA2: + single_regs = true; + d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + regs = Bits32(opcode, 7, 0); + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + default: + return false; + } + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; + addr_t sp_offset = imm32; + addr_t addr = sp - sp_offset; + uint32_t i; + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + context.type = EmulateInstruction::eContextPushRegisterOnStack; + RegisterInfo dwarf_reg; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + for (i=0; i<regs; ++i) + { + GetRegisterInfo (eRegisterKindDWARF, start_reg + d + i, dwarf_reg); + context.SetRegisterToRegisterPlusOffset ( dwarf_reg, sp_reg, addr - sp); + // uint64_t to accommodate 64-bit registers. + uint64_t reg_value = ReadRegisterUnsigned (&dwarf_reg, 0, &success); + if (!success) + return false; + if (!MemAWrite (context, addr, reg_value, reg_byte_size)) + return false; + addr += reg_byte_size; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (-sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp - sp_offset)) + return false; + } + return true; +} + +// Vector Pop loads multiple extension registers from the stack. +// It also updates SP to point just above the loaded data. +bool +EmulateInstructionARM::EmulateVPOP (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13); + address = SP; + SP = SP + imm32; + if single_regs then + for r = 0 to regs-1 + S[d+r] = MemA[address,4]; address = address+4; + else + for r = 0 to regs-1 + word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; + // Combine the word-aligned words in the correct order for current endianness. + D[d+r] = if BigEndian() then word1:word2 else word2:word1; + } +#endif + + bool success = false; + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + bool single_regs; + uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register + uint32_t imm32; // stack offset + uint32_t regs; // number of registers + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + single_regs = false; + d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + // If UInt(imm8) is odd, see "FLDMX". + regs = Bits32(opcode, 7, 0) / 2; + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + case eEncodingT2: + case eEncodingA2: + single_regs = true; + d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + regs = Bits32(opcode, 7, 0); + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + default: + return false; + } + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; + addr_t sp_offset = imm32; + addr_t addr = sp; + uint32_t i; + uint64_t data; // uint64_t to accomodate 64-bit registers. + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + RegisterInfo dwarf_reg; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + for (i=0; i<regs; ++i) + { + GetRegisterInfo (eRegisterKindDWARF, start_reg + d + i, dwarf_reg); + context.SetRegisterPlusOffset (sp_reg, addr - sp); + data = MemARead(context, addr, reg_byte_size, 0, &success); + if (!success) + return false; + if (!WriteRegisterUnsigned(context, &dwarf_reg, data)) + return false; + addr += reg_byte_size; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned (sp_offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp + sp_offset)) + return false; + } + return true; +} + +// SVC (previously SWI) +bool +EmulateInstructionARM::EmulateSVC (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + CallSupervisor(); + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t pc = ReadCoreReg(PC_REG, &success); + addr_t lr; // next instruction address + if (!success) + return false; + uint32_t imm32; // the immediate constant + uint32_t mode; // ARM or Thumb mode + switch (encoding) { + case eEncodingT1: + lr = (pc + 2) | 1u; // return address + imm32 = Bits32(opcode, 7, 0); + mode = eModeThumb; + break; + case eEncodingA1: + lr = pc + 4; // return address + imm32 = Bits32(opcode, 23, 0); + mode = eModeARM; + break; + default: + return false; + } + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextSupervisorCall; + context.SetISAAndImmediate (mode, imm32); + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) + return false; + } + return true; +} + +// If Then makes up to four following instructions (the IT block) conditional. +bool +EmulateInstructionARM::EmulateIT (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + EncodingSpecificOperations(); + ITSTATE.IT<7:0> = firstcond:mask; +#endif + + m_it_session.InitIT(Bits32(opcode, 7, 0)); + return true; +} + +bool +EmulateInstructionARM::EmulateNop (const uint32_t opcode, const ARMEncoding encoding) +{ + // NOP, nothing to do... + return true; +} + +// Branch causes a branch to a target address. +bool +EmulateInstructionARM::EmulateB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + BranchWritePC(PC + imm32); + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + addr_t target; // target address + int32_t imm32; // PC-relative offset + switch (encoding) { + case eEncodingT1: + // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). + imm32 = llvm::SignExtend32<9>(Bits32(opcode, 7, 0) << 1); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + case eEncodingT2: + imm32 = llvm::SignExtend32<12>(Bits32(opcode, 10, 0)); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + case eEncodingT3: + // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). + { + uint32_t S = Bit32(opcode, 26); + uint32_t imm6 = Bits32(opcode, 21, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t imm21 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<21>(imm21); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + } + case eEncodingT4: + { + uint32_t S = Bit32(opcode, 26); + uint32_t imm10 = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<25>(imm25); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + } + case eEncodingA1: + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeARM, 8 + imm32); + break; + default: + return false; + } + if (!BranchWritePC(context, target)) + return false; + } + return true; +} + +// Compare and Branch on Nonzero and Compare and Branch on Zero compare the value in a register with +// zero and conditionally branch forward a constant value. They do not affect the condition flags. +// CBNZ, CBZ +bool +EmulateInstructionARM::EmulateCB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + EncodingSpecificOperations(); + if nonzero ^ IsZero(R[n]) then + BranchWritePC(PC + imm32); +#endif + + bool success = false; + + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Bits32(opcode, 2, 0), &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + addr_t target; // target address + uint32_t imm32; // PC-relative offset to branch forward + bool nonzero; + switch (encoding) { + case eEncodingT1: + imm32 = Bit32(opcode, 9) << 6 | Bits32(opcode, 7, 3) << 1; + nonzero = BitIsSet(opcode, 11); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + default: + return false; + } + if (nonzero ^ (reg_val == 0)) + if (!BranchWritePC(context, target)) + return false; + + return true; +} + +// Table Branch Byte causes a PC-relative forward branch using a table of single byte offsets. +// A base register provides a pointer to the table, and a second register supplies an index into the table. +// The branch length is twice the value of the byte returned from the table. +// +// Table Branch Halfword causes a PC-relative forward branch using a table of single halfword offsets. +// A base register provides a pointer to the table, and a second register supplies an index into the table. +// The branch length is twice the value of the halfword returned from the table. +// TBB, TBH +bool +EmulateInstructionARM::EmulateTB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + if is_tbh then + halfwords = UInt(MemU[R[n]+LSL(R[m],1), 2]); + else + halfwords = UInt(MemU[R[n]+R[m], 1]); + BranchWritePC(PC + 2*halfwords); +#endif + + bool success = false; + + uint32_t Rn; // the base register which contains the address of the table of branch lengths + uint32_t Rm; // the index register which contains an integer pointing to a byte/halfword in the table + bool is_tbh; // true if table branch halfword + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + is_tbh = BitIsSet(opcode, 4); + if (Rn == 13 || BadReg(Rm)) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + default: + return false; + } + + // Read the address of the table from the operand register Rn. + // The PC can be used, in which case the table immediately follows this instruction. + uint32_t base = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // the table index + uint32_t index = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // the offsetted table address + addr_t addr = base + (is_tbh ? index*2 : index); + + // PC-relative offset to branch forward + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextTableBranchReadMemory; + uint32_t offset = MemURead(context, addr, is_tbh ? 2 : 1, 0, &success) * 2; + if (!success) + return false; + + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + // target address + addr_t target = pc + offset; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + context.SetISAAndImmediateSigned (eModeThumb, 4 + offset); + + if (!BranchWritePC(context, target)) + return false; + + return true; +} + +// This instruction adds an immediate value to a register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateADDImmThumb (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t n; + bool setflags; + uint32_t imm32; + uint32_t carry_out; + + //EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); n = UInt(Rn); setflags = !InITBlock(); imm32 = ZeroExtend(imm3, 32); + d = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + setflags = !InITBlock(); + imm32 = Bits32 (opcode, 8,6); + + break; + + case eEncodingT2: + // d = UInt(Rdn); n = UInt(Rdn); setflags = !InITBlock(); imm32 = ZeroExtend(imm8, 32); + d = Bits32 (opcode, 10, 8); + n = Bits32 (opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32 (opcode, 7, 0); + + break; + + case eEncodingT3: + // if Rd == '1111' && S == '1' then SEE CMN (immediate); + // if Rn == '1101' then SEE ADD (SP plus immediate); + // d = UInt(Rd); n = UInt(Rn); setflags = (S == '1'); imm32 = ThumbExpandImm(i:imm3:imm8); + d = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + setflags = BitIsSet (opcode, 20); + imm32 = ThumbExpandImm_C (opcode, APSR_C, carry_out); + + // if BadReg(d) || n == 15 then UNPREDICTABLE; + if (BadReg (d) || (n == 15)) + return false; + + break; + + case eEncodingT4: + { + // if Rn == '1111' then SEE ADR; + // if Rn == '1101' then SEE ADD (SP plus immediate); + // d = UInt(Rd); n = UInt(Rn); setflags = FALSE; imm32 = ZeroExtend(i:imm3:imm8, 32); + d = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + setflags = false; + uint32_t i = Bit32 (opcode, 26); + uint32_t imm3 = Bits32 (opcode, 14, 12); + uint32_t imm8 = Bits32 (opcode, 7, 0); + imm32 = (i << 11) | (imm3 << 8) | imm8; + + // if BadReg(d) then UNPREDICTABLE; + if (BadReg (d)) + return false; + + break; + } + default: + return false; + } + + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + //(result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + AddWithCarryResult res = AddWithCarry (Rn, imm32, 0); + + RegisterInfo reg_n; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + context.SetRegisterPlusOffset (reg_n, imm32); + + //R[d] = result; + //if setflags then + //APSR.N = result<31>; + //APSR.Z = IsZeroBit(result); + //APSR.C = carry; + //APSR.V = overflow; + if (!WriteCoreRegOptionalFlags (context, res.result, d, setflags, res.carry_out, res.overflow)) + return false; + + } + return true; +} + +// This instruction adds an immediate value to a register value, and writes the result to the destination +// register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateADDImmARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + bool setflags; + switch (encoding) + { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(val1, imm32, 0); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, Rn, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, imm32); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// This instruction adds a register value and an optionally-shifted register value, and writes the result +// to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateADDReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + Rm = Bits32(opcode, 8, 6); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + setflags = false; + shift_t = SRType_LSL; + shift_n = 0; + if (Rn == 15 && Rm == 15) + return false; + if (Rd == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, 0); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo op1_reg; + RegisterInfo op2_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rn, op1_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, op2_reg); + context.SetRegisterRegisterOperands (op1_reg, op2_reg); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// Compare Negative (immediate) adds a register value and an immediate value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateCMNImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t imm32; // the immediate value to be compared with + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rn == 15) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, imm32, 0); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Compare Negative (register) adds a register value and an optionally-shifted register value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateCMNReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if n == 15 || BadReg(m) then UNPREDICTABLE; + if (Rn == 15 || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, 0); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Compare (immediate) subtracts an immediate value from a register value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateCMPImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t imm32; // the immediate value to be compared with + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 10, 8); + imm32 = Bits32(opcode, 7, 0); + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rn == 15) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Compare (register) subtracts an optionally-shifted register value from a register value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateCMPReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + shift_t = SRType_LSL; + shift_n = 0; + if (Rn < 8 && Rm < 8) + return false; + if (Rn == 15 || Rm == 15) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, ~shifted, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Arithmetic Shift Right (immediate) shifts a register value right by an immediate number of bits, +// shifting in copies of its sign bit, and writes the result to the destination register. It can +// optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateASRImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_ASR); +} + +// Arithmetic Shift Right (register) shifts a register value right by a variable number of bits, +// shifting in copies of its sign bit, and writes the result to the destination register. +// The variable number of bits is read from the bottom byte of a register. It can optionally update +// the condition flags based on the result. +bool +EmulateInstructionARM::EmulateASRReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg (opcode, encoding, SRType_ASR); +} + +// Logical Shift Left (immediate) shifts a register value left by an immediate number of bits, +// shifting in zeros, and writes the result to the destination register. It can optionally +// update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateLSLImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_LSL); +} + +// Logical Shift Left (register) shifts a register value left by a variable number of bits, +// shifting in zeros, and writes the result to the destination register. The variable number +// of bits is read from the bottom byte of a register. It can optionally update the condition +// flags based on the result. +bool +EmulateInstructionARM::EmulateLSLReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg (opcode, encoding, SRType_LSL); +} + +// Logical Shift Right (immediate) shifts a register value right by an immediate number of bits, +// shifting in zeros, and writes the result to the destination register. It can optionally +// update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateLSRImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_LSR); +} + +// Logical Shift Right (register) shifts a register value right by a variable number of bits, +// shifting in zeros, and writes the result to the destination register. The variable number +// of bits is read from the bottom byte of a register. It can optionally update the condition +// flags based on the result. +bool +EmulateInstructionARM::EmulateLSRReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg (opcode, encoding, SRType_LSR); +} + +// Rotate Right (immediate) provides the value of the contents of a register rotated by a constant value. +// The bits that are rotated off the right end are inserted into the vacated bit positions on the left. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateRORImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_ROR); +} + +// Rotate Right (register) provides the value of the contents of a register rotated by a variable number of bits. +// The bits that are rotated off the right end are inserted into the vacated bit positions on the left. +// The variable number of bits is read from the bottom byte of a register. It can optionally update the condition +// flags based on the result. +bool +EmulateInstructionARM::EmulateRORReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg (opcode, encoding, SRType_ROR); +} + +// Rotate Right with Extend provides the value of the contents of a register shifted right by one place, +// with the carry flag shifted into bit [31]. +// +// RRX can optionally update the condition flags based on the result. +// In that case, bit [0] is shifted into the carry flag. +bool +EmulateInstructionARM::EmulateRRX (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_RRX, 1, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm (opcode, encoding, SRType_RRX); +} + +bool +EmulateInstructionARM::EmulateShiftImm (const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type) +{ +// assert(shift_type == SRType_ASR +// || shift_type == SRType_LSL +// || shift_type == SRType_LSR +// || shift_type == SRType_ROR +// || shift_type == SRType_RRX); + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd; // the destination register + uint32_t Rm; // the first operand register + uint32_t imm5; // encoding for the shift amount + uint32_t carry; // the carry bit after the shift operation + bool setflags; + + // Special case handling! + // A8.6.139 ROR (immediate) -- Encoding T1 + ARMEncoding use_encoding = encoding; + if (shift_type == SRType_ROR && use_encoding == eEncodingT1) + { + // Morph the T1 encoding from the ARM Architecture Manual into T2 encoding to + // have the same decoding of bit fields as the other Thumb2 shift operations. + use_encoding = eEncodingT2; + } + + switch (use_encoding) { + case eEncodingT1: + // Due to the above special case handling! + if (shift_type == SRType_ROR) + return false; + + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm5 = Bits32(opcode, 10, 6); + break; + case eEncodingT2: + // A8.6.141 RRX + // There's no imm form of RRX instructions. + if (shift_type == SRType_RRX) + return false; + + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + imm5 = Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6); + if (BadReg(Rd) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + imm5 = Bits32(opcode, 11, 7); + break; + default: + return false; + } + + // A8.6.139 ROR (immediate) + if (shift_type == SRType_ROR && imm5 == 0) + shift_type = SRType_RRX; + + // Get the first operand. + uint32_t value = ReadCoreReg (Rm, &success); + if (!success) + return false; + + // Decode the shift amount if not RRX. + uint32_t amt = (shift_type == SRType_RRX ? 1 : DecodeImmShift(shift_type, imm5)); + + uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); + if (!success) + return false; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +bool +EmulateInstructionARM::EmulateShiftReg (const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type) +{ + // assert(shift_type == SRType_ASR + // || shift_type == SRType_LSL + // || shift_type == SRType_LSR + // || shift_type == SRType_ROR); + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand register + uint32_t Rm; // the register whose bottom byte contains the amount to shift by + uint32_t carry; // the carry bit after the shift operation + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Rd; + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 3, 0); + Rm = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + if (Rd == 15 || Rn == 15 || Rm == 15) + return false; + break; + default: + return false; + } + + // Get the first operand. + uint32_t value = ReadCoreReg (Rn, &success); + if (!success) + return false; + // Get the Rm register content. + uint32_t val = ReadCoreReg (Rm, &success); + if (!success) + return false; + + // Get the shift amount. + uint32_t amt = Bits32(val, 7, 0); + + uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); + if (!success) + return false; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// LDM loads multiple registers from consecutive memory locations, using an +// address from a base register. Optionally the address just above the highest of those locations +// can be written back to the base register. +bool +EmulateInstructionARM::EmulateLDM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() + EncodingSpecificOperations(); NullCheckIfThumbEE (n); + address = R[n]; + + for i = 0 to 14 + if registers<i> == '1' then + R[i] = MemA[address, 4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC (MemA[address, 4]); + + if wback && registers<n> == '0' then R[n] = R[n] + 4 * BitCount (registers); + if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 + +#endif + + bool success = false; + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) + { + case eEncodingT1: + // n = UInt(Rn); registers = '00000000':register_list; wback = (registers<n> == '0'); + n = Bits32 (opcode, 10, 8); + registers = Bits32 (opcode, 7, 0); + registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. + wback = BitIsClear (registers, n); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // if W == '1' && Rn == '1101' then SEE POP; + // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + registers = registers & 0xdfff; // Make sure bit 13 is zero. + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; + if ((n == 15) + || (BitCount (registers) < 2) + || (BitIsSet (opcode, 14) && BitIsSet (opcode, 15))) + return false; + + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (BitIsSet (registers, 15) && InITBlock() && !LastInITBlock()) + return false; + + // if wback && registers<n> == '1' then UNPREDICTABLE; + if (wback + && BitIsSet (registers, n)) + return false; + break; + + case eEncodingA1: + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + if ((n == 15) + || (BitCount (registers) < 1)) + return false; + break; + default: + return false; + } + + int32_t offset = 0; + const addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, offset); + + for (int i = 0; i < 14; ++i) + { + if (BitIsSet (registers, i)) + { + context.type = EmulateInstruction::eContextRegisterPlusOffset; + context.SetRegisterPlusOffset (dwarf_reg, offset); + if (wback && (n == 13)) // Pop Instruction + { + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + } + + // R[i] = MemA [address, 4]; address = address + 4; + uint32_t data = MemARead (context, base_address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + + offset += addr_byte_size; + } + } + + if (BitIsSet (registers, 15)) + { + //LoadWritePC (MemA [address, 4]); + context.type = EmulateInstruction::eContextRegisterPlusOffset; + context.SetRegisterPlusOffset (dwarf_reg, offset); + uint32_t data = MemARead (context, base_address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + if (wback && BitIsClear (registers, n)) + { + // R[n] = R[n] + 4 * BitCount (registers) + int32_t offset = addr_byte_size * BitCount (registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (dwarf_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, base_address + offset)) + return false; + } + if (wback && BitIsSet (registers, n)) + // R[n] bits(32) UNKNOWN; + return WriteBits32Unknown (n); + } + return true; +} + +// LDMDA loads multiple registers from consecutive memory locations using an address from a base register. +// The consecutive memory locations end at this address and the address just below the lowest of those locations +// can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateLDMDA (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] - 4*BitCount(registers) + 4; + + for i = 0 to 14 + if registers<i> == '1' then + R[i] = MemA[address,4]; address = address + 4; + + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers); + if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + + break; + + default: + return false; + } + // address = R[n] - 4*BitCount(registers) + 4; + + int32_t offset = 0; + addr_t Rn = ReadCoreReg (n, &success); + + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount (registers)) + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, offset); + + // for i = 0 to 14 + for (int i = 0; i < 14; ++i) + { + // if registers<i> == '1' then + if (BitIsSet (registers, i)) + { + // R[i] = MemA[address,4]; address = address + 4; + context.SetRegisterPlusOffset (dwarf_reg, Rn - (address + offset)); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet (registers, 15)) + { + context.SetRegisterPlusOffset (dwarf_reg, offset); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers); + if (wback && BitIsClear (registers, n)) + { + if (!success) + return false; + + offset = (addr_byte_size * BitCount (registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) + return false; + } + + // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; + if (wback && BitIsSet (registers, n)) + return WriteBits32Unknown (n); + } + return true; +} + +// LDMDB loads multiple registers from consecutive memory locations using an address from a base register. The +// consecutive memory lcoations end just below this address, and the address of the lowest of those locations can +// be optionally written back to the base register. +bool +EmulateInstructionARM::EmulateLDMDB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] - 4*BitCount(registers); + + for i = 0 to 14 + if registers<i> == '1' then + R[i] = MemA[address,4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers); + if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) + { + case eEncodingT1: + // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + registers = registers & 0xdfff; // Make sure bit 13 is a zero. + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; + if ((n == 15) + || (BitCount (registers) < 2) + || (BitIsSet (opcode, 14) && BitIsSet (opcode, 15))) + return false; + + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (BitIsSet (registers, 15) && InITBlock() && !LastInITBlock()) + return false; + + // if wback && registers<n> == '1' then UNPREDICTABLE; + if (wback && BitIsSet (registers, n)) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + + break; + + default: + return false; + } + + // address = R[n] - 4*BitCount(registers); + + int32_t offset = 0; + addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount (registers)); + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, Rn - address); + + for (int i = 0; i < 14; ++i) + { + if (BitIsSet (registers, i)) + { + // R[i] = MemA[address,4]; address = address + 4; + context.SetRegisterPlusOffset (dwarf_reg, Rn - (address + offset)); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet (registers, 15)) + { + context.SetRegisterPlusOffset (dwarf_reg, offset); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers); + if (wback && BitIsClear (registers, n)) + { + if (!success) + return false; + + offset = (addr_byte_size * BitCount (registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) + return false; + } + + // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 + if (wback && BitIsSet (registers, n)) + return WriteBits32Unknown (n); + } + return true; +} + +// LDMIB loads multiple registers from consecutive memory locations using an address from a base register. The +// consecutive memory locations start just above this address, and thea ddress of the last of those locations can +// optinoally be written back to the base register. +bool +EmulateInstructionARM::EmulateLDMIB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] + 4; + + for i = 0 to 14 + if registers<i> == '1' then + R[i] = MemA[address,4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers); + if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) + { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + + break; + default: + return false; + } + // address = R[n] + 4; + + int32_t offset = 0; + addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + addr_t address = Rn + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); + context.SetRegisterPlusOffset (dwarf_reg, offset); + + for (int i = 0; i < 14; ++i) + { + if (BitIsSet (registers, i)) + { + // R[i] = MemA[address,4]; address = address + 4; + + context.SetRegisterPlusOffset (dwarf_reg, offset + addr_byte_size); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) + return false; + + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet (registers, 15)) + { + context.SetRegisterPlusOffset (dwarf_reg, offset); + uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers); + if (wback && BitIsClear (registers, n)) + { + if (!success) + return false; + + offset = addr_byte_size * BitCount (registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) + return false; + } + + // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 + if (wback && BitIsSet (registers, n)) + return WriteBits32Unknown (n); + } + return true; +} + +// Load Register (immediate) calculates an address from a base register value and +// an immediate offset, loads a word from memory, and writes to a register. +// LDR (immediate, Thumb) +bool +EmulateInstructionARM::EmulateLDRRtRnImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else R[t] = bits(32) UNKNOWN; // Can only apply before ARMv7 + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rt; // the destination register + uint32_t Rn; // the base register + uint32_t imm32; // the immediate offset used to form the address + addr_t offset_addr; // the offset address + addr_t address; // the calculated address + uint32_t data; // the literal data value from memory load + bool add, index, wback; + switch (encoding) { + case eEncodingT1: + Rt = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + imm32 = Bits32(opcode, 10, 6) << 2; // imm32 = ZeroExtend(imm5:'00', 32); + // index = TRUE; add = TRUE; wback = FALSE + add = true; + index = true; + wback = false; + + break; + + case eEncodingT2: + // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); + Rt = Bits32 (opcode, 10, 8); + Rn = 13; + imm32 = Bits32 (opcode, 7, 0) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + break; + + case eEncodingT3: + // if Rn == '1111' then SEE LDR (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + Rt = Bits32 (opcode, 15, 12); + Rn = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if ((Rt == 15) && InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingT4: + // if Rn == '1111' then SEE LDR (literal); + // if P == '1' && U == '1' && W == '0' then SEE LDRT; + // if Rn == '1101' && P == '0' && U == '1' && W == '1' && imm8 == '00000100' then SEE POP; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + Rt = Bits32 (opcode, 15, 12); + Rn = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if (wback && n == t) || (t == 15 && InITBlock() && !LastInITBlock()) then UNPREDICTABLE; + if ((wback && (Rn == Rt)) || ((Rt == 15) && InITBlock() && !LastInITBlock())) + return false; + + break; + + default: + return false; + } + uint32_t base = ReadCoreReg (Rn, &success); + if (!success) + return false; + if (add) + offset_addr = base + imm32; + else + offset_addr = base - imm32; + + address = (index ? offset_addr : base); + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rn, base_reg); + if (wback) + { + EmulateInstruction::Context ctx; + ctx.type = EmulateInstruction::eContextAdjustBaseRegister; + ctx.SetRegisterPlusOffset (base_reg, (int32_t) (offset_addr - base)); + + if (!WriteRegisterUnsigned (ctx, eRegisterKindDWARF, dwarf_r0 + Rn, offset_addr)) + return false; + } + + // Prepare to write to the Rt register. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, (int32_t) (offset_addr - base)); + + // Read memory from the address. + data = MemURead(context, address, 4, 0, &success); + if (!success) + return false; + + if (Rt == 15) + { + if (Bits32(address, 1, 0) == 0) + { + if (!LoadWritePC(context, data)) + return false; + } + else + return false; + } + else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rt, data)) + return false; + } + else + WriteBits32Unknown (Rt); + } + return true; +} + +// STM (Store Multiple Increment After) stores multiple registers to consecutive memory locations using an address +// from a base register. The consecutive memory locations start at this address, and teh address just above the last +// of those locations can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateSTM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n]; + + for i = 0 to 14 + if registers<i> == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1 + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then // Only possible for encoding A1 + MemA[address,4] = PCStoreValue(); + if wback then R[n] = R[n] + 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // n = UInt(Rn); registers = '00000000':register_list; wback = TRUE; + n = Bits32 (opcode, 10, 8); + registers = Bits32 (opcode, 7, 0); + registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. + wback = true; + + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount (registers) < 1) + return false; + + break; + + case eEncodingT2: + // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 2)) + return false; + + // if wback && registers<n> == '1' then UNPREDICTABLE; + if (wback && BitIsSet (registers, n)) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + + break; + + default: + return false; + } + + // address = R[n]; + int32_t offset = 0; + const addr_t address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + // for i = 0 to 14 + uint32_t lowest_set_bit = 14; + for (uint32_t i = 0; i < 14; ++i) + { + // if registers<i> == '1' then + if (BitIsSet (registers, i)) + { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1 + WriteBits32UnknownToMemory (address + offset); + else + { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset); + if (!MemAWrite (context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then // Only possible for encoding A1 + // MemA[address,4] = PCStoreValue(); + if (BitIsSet (registers, 15)) + { + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 8); + const uint32_t pc = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] + 4*BitCount(registers); + if (wback) + { + offset = addr_byte_size * BitCount (registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t data = address + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + } + } + return true; +} + +// STMDA (Store Multiple Decrement After) stores multiple registers to consecutive memory locations using an address +// from a base register. The consecutive memory locations end at this address, and the address just below the lowest +// of those locations can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateSTMDA (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] - 4*BitCount(registers) + 4; + + for i = 0 to 14 + if registers<i> == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] - 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount (registers) < 1)) + return false; + break; + default: + return false; + } + + // address = R[n] - 4*BitCount(registers) + 4; + int32_t offset = 0; + addr_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount (registers)) + 4; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + // for i = 0 to 14 + uint32_t lowest_bit_set = 14; + for (uint32_t i = 0; i < 14; ++i) + { + // if registers<i> == '1' then + if (BitIsSet (registers, i)) + { + if (i < lowest_bit_set) + lowest_bit_set = i; + //if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_bit_set)) + // MemA[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory (address + offset); + else + { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, Rn - (address + offset)); + if (!MemAWrite (context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // MemA[address,4] = PCStoreValue(); + if (BitIsSet (registers, 15)) + { + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 8); + const uint32_t pc = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] - 4*BitCount(registers); + if (wback) + { + offset = (addr_byte_size * BitCount (registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + } + } + return true; +} + +// STMDB (Store Multiple Decrement Before) stores multiple registers to consecutive memory locations using an address +// from a base register. The consecutive memory locations end just below this address, and the address of the first of +// those locations can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateSTMDB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] - 4*BitCount(registers); + + for i = 0 to 14 + if registers<i> == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1 + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then // Only possible for encoding A1 + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] - 4*BitCount(registers); +#endif + + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if W == '1' && Rn == '1101' then SEE PUSH; + if ((BitIsSet (opcode, 21)) && (Bits32 (opcode, 19, 16) == 13)) + { + // See PUSH + } + // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. + wback = BitIsSet (opcode, 21); + // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; + if ((n == 15) || BitCount (registers) < 2) + return false; + // if wback && registers<n> == '1' then UNPREDICTABLE; + if (wback && BitIsSet (registers, n)) + return false; + break; + + case eEncodingA1: + // if W == '1' && Rn == '1101Õ && BitCount(register_list) >= 2 then SEE PUSH; + if (BitIsSet (opcode, 21) && (Bits32 (opcode, 19, 16) == 13) && BitCount (Bits32 (opcode, 15, 0)) >= 2) + { + // See Push + } + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || BitCount (registers) < 1) + return false; + break; + + default: + return false; + } + + // address = R[n] - 4*BitCount(registers); + + int32_t offset = 0; + addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount (registers)); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + // for i = 0 to 14 + uint32_t lowest_set_bit = 14; + for (uint32_t i = 0; i < 14; ++i) + { + // if registers<i> == '1' then + if (BitIsSet (registers, i)) + { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1 + WriteBits32UnknownToMemory (address + offset); + else + { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, Rn - (address + offset)); + if (!MemAWrite (context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then // Only possible for encoding A1 + // MemA[address,4] = PCStoreValue(); + if (BitIsSet (registers, 15)) + { + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 8); + const uint32_t pc = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] - 4*BitCount(registers); + if (wback) + { + offset = (addr_byte_size * BitCount (registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + } + } + return true; +} + +// STMIB (Store Multiple Increment Before) stores multiple registers to consecutive memory locations using an address +// from a base register. The consecutive memory locations start just above this address, and the address of the last +// of those locations can optionally be written back to the base register. +bool +EmulateInstructionARM::EmulateSTMIB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] + 4; + + for i = 0 to 14 + if registers<i> == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] + 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32 (opcode, 19, 16); + registers = Bits32 (opcode, 15, 0); + wback = BitIsSet (opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) && (BitCount (registers) < 1)) + return false; + break; + default: + return false; + } + // address = R[n] + 4; + + int32_t offset = 0; + addr_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t address = Rn + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t lowest_set_bit = 14; + // for i = 0 to 14 + for (uint32_t i = 0; i < 14; ++i) + { + // if registers<i> == '1' then + if (BitIsSet (registers, i)) + { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory (address + offset); + // else + else + { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset + addr_byte_size); + if (!MemAWrite (context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // MemA[address,4] = PCStoreValue(); + if (BitIsSet (registers, 15)) + { + RegisterInfo pc_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); + context.SetRegisterPlusOffset (pc_reg, 8); + const uint32_t pc = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] + 4*BitCount(registers); + if (wback) + { + offset = addr_byte_size * BitCount (registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned (offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + } + } + return true; +} + +// STR (store immediate) calcualtes an address from a base register value and an immediate offset, and stores a word +// from a register to memory. It can use offset, post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateSTRThumb (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + if UnalignedSupport() || address<1:0> == '00' then + MemU[address,4] = R[t]; + else // Can only occur before ARMv7 + MemU[address,4] = bits(32) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + // EncodingSpecificOperations (); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'00', 32); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + imm32 = Bits32 (opcode, 10, 6) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = false; + wback = false; + break; + + case eEncodingT2: + // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); + t = Bits32 (opcode, 10, 8); + n = 13; + imm32 = Bits32 (opcode, 7, 0) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + break; + + case eEncodingT3: + // if Rn == '1111' then UNDEFINED; + if (Bits32 (opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + + case eEncodingT4: + // if P == '1' && U == '1' && W == '0' then SEE STRT; + // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm8 == '00000100' then SEE PUSH; + // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; + if ((Bits32 (opcode, 19, 16) == 15) + || (BitIsClear (opcode, 10) && BitIsClear (opcode, 8))) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t base_address = ReadCoreReg (n, &success); + if (!success) + return false; + + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + // if UnalignedSupport() || address<1:0> == '00' then + if (UnalignedSupport () || (BitIsClear (address, 1) && BitIsClear (address, 0))) + { + // MemU[address,4] = R[t]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + int32_t offset = address - base_address; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset); + if (!MemUWrite (context, address, data, addr_byte_size)) + return false; + } + else + { + // MemU[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory (address); + } + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextRegisterLoad; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// STR (Store Register) calculates an address from a base register value and an offset register value, stores a +// word from a register to memory. The offset register value can optionally be shifted. +bool +EmulateInstructionARM::EmulateSTRRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + if t == 15 then // Only possible for encoding A1 + data = PCStoreValue(); + else + data = R[t]; + if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then + MemU[address,4] = data; + else // Can only occur before ARMv7 + MemU[address,4] = bits(32) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t m; + ARM_ShifterType shift_t; + uint32_t shift_n; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations (); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + if (Bits32 (opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 15 || BadReg(m) then UNPREDICTABLE; + if ((t == 15) || (BadReg (m))) + return false; + break; + + case eEncodingA1: + { + // if P == '0' && W == '1' then SEE STRT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t typ = Bits32 (opcode, 6, 5); + uint32_t imm5 = Bits32 (opcode, 11, 7); + shift_n = DecodeImmShift(typ, imm5, shift_t); + + // if m == 15 then UNPREDICTABLE; + if (m == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + } + default: + return false; + } + + addr_t offset_addr; + addr_t address; + int32_t offset = 0; + + addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + uint32_t Rm_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset = Shift (Rm_data, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = base_address + offset; + else + offset_addr = base_address - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + uint32_t data; + // if t == 15 then // Only possible for encoding A1 + if (t == 15) + // data = PCStoreValue(); + data = ReadCoreReg (PC_REG, &success); + else + // data = R[t]; + data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + + // if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then + if (UnalignedSupport () + || (BitIsClear (address, 1) && BitIsClear (address, 0)) + || CurrentInstrSet() == eModeARM) + { + // MemU[address,4] = data; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - base_address); + if (!MemUWrite (context, address, data, addr_byte_size)) + return false; + + } + else + // MemU[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory (address); + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextRegisterLoad; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + } + return true; +} + +bool +EmulateInstructionARM::EmulateSTRBThumb (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,1] = R[t]<7:0>; + if wback then R[n] = offset_addr; +#endif + + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + imm32 = Bits32 (opcode, 10, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + if (Bits32 (opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if BadReg(t) then UNPREDICTABLE; + if (BadReg (t)) + return false; + break; + + case eEncodingT3: + // if P == '1' && U == '1' && W == '0' then SEE STRBT; + // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; + if (Bits32 (opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE + if ((BadReg (t)) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + // MemU[address,1] = R[t]<7:0> + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - base_address); + + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + data = Bits32 (data, 7, 0); + + if (!MemUWrite (context, address, data, 1)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextRegisterLoad; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + } + + return true; +} + +// STRH (register) calculates an address from a base register value and an offset register value, and stores a +// halfword from a register to memory. The offset register alue can be shifted left by 0, 1, 2, or 3 bits. +bool +EmulateInstructionARM::EmulateSTRHRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + if UnalignedSupport() || address<0> == '0' then + MemU[address,2] = R[t]<15:0>; + else // Can only occur before ARMv7 + MemU[address,2] = bits(16) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + if (n == 15) + return false; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if BadReg(t) || BadReg(m) then UNPREDICTABLE; + if (BadReg (t) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE STRHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + addr_t offset_addr; + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + // if UnalignedSupport() || address<0> == '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // MemU[address,2] = R[t]<15:0>; + uint32_t Rt = ReadCoreReg (t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); + + if (!MemUWrite (context, address, Bits32 (Rt, 15, 0), 2)) + return false; + } + else // Can only occur before ARMv7 + { + // MemU[address,2] = bits(16) UNKNOWN; + } + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + + return true; +} + +// Add with Carry (immediate) adds an immediate value and the carry flag value to a register value, +// and writes the result to the destination register. It can optionally update the condition flags +// based on the result. +bool +EmulateInstructionARM::EmulateADCImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + bool setflags; + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + int32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(val1, imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// Add with Carry (register) adds a register value, the carry flag value, and an optionally-shifted +// register value, and writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool +EmulateInstructionARM::EmulateADCReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + int32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + int32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// This instruction adds an immediate value to the PC value to form a PC-relative address, +// and writes the result to the destination register. +bool +EmulateInstructionARM::EmulateADR (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = if add then (Align(PC,4) + imm32) else (Align(PC,4) - imm32); + if d == 15 then // Can only occur for ARM encodings + ALUWritePC(result); + else + R[d] = result; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd; + uint32_t imm32; // the immediate value to be added/subtracted to/from the PC + bool add; + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 10, 8); + imm32 = ThumbImm8Scaled(opcode); // imm32 = ZeroExtend(imm8:'00', 32) + add = true; + break; + case eEncodingT2: + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + add = (Bits32(opcode, 24, 21) == 0); // 0b0000 => ADD; 0b0101 => SUB + if (BadReg(Rd)) + return false; + break; + case eEncodingA1: + case eEncodingA2: + Rd = Bits32(opcode, 15, 12); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + add = (Bits32(opcode, 24, 21) == 0x4); // 0b0100 => ADD; 0b0010 => SUB + break; + default: + return false; + } + + // Read the PC value. + uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + uint32_t result = (add ? Align(pc, 4) + imm32 : Align(pc, 4) - imm32); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreReg(context, result, Rd)) + return false; + } + return true; +} + +// This instruction performs a bitwise AND of a register value and an immediate value, and writes the result +// to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateANDImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rd == '1111' && S == '1' then SEE TST (immediate); + if (Rd == 15 && setflags) + return EmulateTSTImm(opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// This instruction performs a bitwise AND of a register value and an optionally-shifted register value, +// and writes the result to the destination register. It can optionally update the condition flags +// based on the result. +bool +EmulateInstructionARM::EmulateANDReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rd == '1111' && S == '1' then SEE TST (register); + if (Rd == 15 && setflags) + return EmulateTSTReg(opcode, eEncodingT2); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Bit Clear (immediate) performs a bitwise AND of a register value and the complement of an +// immediate value, and writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool +EmulateInstructionARM::EmulateBICImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND NOT(imm32); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be bitwise inverted and ANDed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & ~imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Bit Clear (register) performs a bitwise AND of a register value and the complement of an +// optionally-shifted register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateBICReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND NOT(shifted); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & ~shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// LDR (immediate, ARM) calculates an address from a base register value and an immediate offset, loads a word +// from memory, and writes it to a register. It can use offset, post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRImmediateARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + R[t] = ROR(data, 8*UInt(address<1:0>)); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingA1: + // if Rn == '1111' then SEE LDR (literal); + // if P == '0' && W == '1' then SEE LDRT; + // if Rn == '1101' && P == '0' && U == '1' && W == '0' && imm12 == '000000000100' then SEE POP; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // if wback && n == t then UNPREDICTABLE; + if (wback && (n == t)) + return false; + + break; + + default: + return false; + } + + addr_t address; + addr_t offset_addr; + addr_t base_address = ReadCoreReg (n, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + // data = MemU[address,4]; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base_address); + + uint64_t data = MemURead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if t == 15 then + if (t == 15) + { + // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + if (BitIsClear (address, 1) && BitIsClear (address, 0)) + { + // LoadWritePC (data); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base_address); + LoadWritePC (context, data); + } + else + return false; + } + // elsif UnalignedSupport() || address<1:0> = '00' then + else if (UnalignedSupport() || (BitIsClear (address, 1) && BitIsClear (address, 0))) + { + // R[t] = data; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base_address); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + // else // Can only apply before ARMv7 + else + { + // R[t] = ROR(data, 8*UInt(address<1:0>)); + data = ROR (data, Bits32 (address, 1, 0), &success); + if (!success) + return false; + context.type = eContextRegisterLoad; + context.SetImmediate (data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + + } + return true; +} + +// LDR (register) calculates an address from a base register value and an offset register value, loads a word +// from memory, and writes it to a resgister. The offset register value can optionally be shifted. +bool +EmulateInstructionARM::EmulateLDRRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + if CurrentInstrSet() == InstrSet_ARM then + R[t] = ROR(data, 8*UInt(address<1:0>)); + else + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDR (literal); + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if BadReg(m) then UNPREDICTABLE; + if (BadReg (m)) + return false; + + // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if ((t == 15) && InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingA1: + { + // if P == '0' && W == '1' then SEE LDRT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t type = Bits32 (opcode, 6, 5); + uint32_t imm5 = Bits32 (opcode, 11, 7); + shift_n = DecodeImmShift (type, imm5, shift_t); + + // if m == 15 then UNPREDICTABLE; + if (m == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + } + break; + + + default: + return false; + } + + uint32_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); -- Note "The APSR is an application level alias for the CPSR". + addr_t offset = Shift (Rm, shift_t, shift_n, Bit32 (m_opcode_cpsr, APSR_C), &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,4]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemURead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if t == 15 then + if (t == 15) + { + // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + if (BitIsClear (address, 1) && BitIsClear (address, 0)) + { + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + LoadWritePC (context, data); + } + else + return false; + } + // elsif UnalignedSupport() || address<1:0> = '00' then + else if (UnalignedSupport () || (BitIsClear (address, 1) && BitIsClear (address, 0))) + { + // R[t] = data; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + else // Can only apply before ARMv7 + { + // if CurrentInstrSet() == InstrSet_ARM then + if (CurrentInstrSet () == eModeARM) + { + // R[t] = ROR(data, 8*UInt(address<1:0>)); + data = ROR (data, Bits32 (address, 1, 0), &success); + if (!success) + return false; + context.type = eContextRegisterLoad; + context.SetImmediate (data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + else + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + } + return true; +} + +// LDRB (immediate, Thumb) +bool +EmulateInstructionARM::EmulateLDRBImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = ZeroExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + imm32 = Bits32 (opcode, 10, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback= false; + + break; + + case eEncodingT2: + // if Rt == '1111' then SEE PLD; + // if Rn == '1111' then SEE LDRB (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT3: + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD; + // if Rn == '1111' then SEE LDRB (literal); + // if P == '1' && U == '1' && W == '0' then SEE LDRBT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg (t) || (wback && (n == t))) + return false; + + break; + + default: + return false; + } + + uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address; + addr_t offset_addr; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = ZeroExtend(MemU[address,1], 32); + RegisterInfo base_reg; + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + uint64_t data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// LDRB (literal) calculates an address from the PC value and an immediate offset, loads a byte from memory, +// zero-extends it to form a 32-bit word and writes it to a register. +bool +EmulateInstructionARM::EmulateLDRBLiteral (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + R[t] = ZeroExtend(MemU[address,1], 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t imm32; + bool add; + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE PLD; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + // t == UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + + default: + return false; + } + + // base = Align(PC,4); + uint32_t pc_val = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + uint32_t base = AlignPC (pc_val); + + addr_t address; + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // R[t] = ZeroExtend(MemU[address,1], 32); + EmulateInstruction::Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate (address - base); + + uint64_t data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + return true; +} + +// LDRB (register) calculates an address from a base register value and an offset rigister value, loads a byte from +// memory, zero-extends it to form a 32-bit word, and writes it to a register. The offset register value can +// optionally be shifted. +bool +EmulateInstructionARM::EmulateLDRBRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + R[t] = ZeroExtend(MemU[address,1],32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + break; + + case eEncodingT2: + // if Rt == '1111' then SEE PLD; + // if Rn == '1111' then SEE LDRB (literal); + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg (m)) + return false; + break; + + case eEncodingA1: + { + // if P == '0' && W == '1' then SEE LDRBT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t type = Bits32 (opcode, 6, 5); + uint32_t imm5 = Bits32 (opcode, 11, 7); + shift_n = DecodeImmShift (type, imm5, shift_t); + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + } + break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = ZeroExtend(MemU[address,1],32); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// LDRH (immediate, Thumb) calculates an address from a base register value and an immediate offset, loads a +// halfword from memory, zero-extends it to form a 32-bit word, and writes it to a register. It can use offset, +// post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRHImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'0', 32); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + imm32 = Bits32 (opcode, 10, 6) << 1; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + break; + + case eEncodingT2: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // if Rn == '1111' then SEE LDRH (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + break; + + case eEncodingT3: + // if Rn == '1111' then SEE LDRH (literal); + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE "Unallocated memory hints"; + // if P == '1' && U == '1' && W == '0' then SEE LDRHT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg (t) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport () || BitIsClear (address, 0)) + { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRH (literal) caculates an address from the PC value and an immediate offset, loads a halfword from memory, +// zero-extends it to form a 32-bit word, and writes it to a register. +bool +EmulateInstructionARM::EmulateLDRHLiteral (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,2]; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + { + uint32_t imm4H = Bits32 (opcode, 11, 8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet (opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + } + + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + addr_t base = AlignPC (pc_value); + addr_t address; + + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport () || BitIsClear (address, 0)) + { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRH (literal) calculates an address from a base register value and an offset register value, loads a halfword +// from memory, zero-extends it to form a 32-bit word, and writes it to a register. The offset register value can +// be shifted left by 0, 1, 2, or 3 bits. +bool +EmulateInstructionARM::EmulateLDRHRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg (m)) + return false; + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + RegisterInfo base_reg; + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRSB (immediate) calculates an address from a base register value and an immediate offset, loads a byte from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. It can use offset, post-indexed, +// or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRSBImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = SignExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT2: + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // if P == '1' && U == '1' && W == '0' then SEE LDRSBT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (((t == 13) || ((t == 15) + && (BitIsClear (opcode, 10) || BitIsSet (opcode, 9) || BitIsSet (opcode, 8)))) + || (wback && (n == t))) + return false; + + break; + + case eEncodingA1: + { + // if Rn == '1111' then SEE LDRSB (literal); + // if P == '0' && W == '1' then SEE LDRSBT; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + + uint32_t imm4H = Bits32 (opcode, 11, 8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + + break; + } + + default: + return false; + } + + uint64_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = SignExtend(MemU[address,1], 32); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + + return true; +} + +// LDRSB (literal) calculates an address from the PC value and an immediate offset, loads a byte from memory, +// sign-extends it to form a 32-bit word, and writes tit to a register. +bool +EmulateInstructionARM::EmulateLDRSBLiteral (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + R[t] = SignExtend(MemU[address,1], 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE PLI; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + { + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + uint32_t imm4H = Bits32 (opcode, 11, 8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet (opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + break; + } + + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + uint64_t base = AlignPC (pc_value); + + // address = if add then (base + imm32) else (base - imm32); + addr_t address; + if (add) + address = base + imm32; + else + address = base - imm32; + + // R[t] = SignExtend(MemU[address,1], 32); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base); + + uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + } + return true; +} + +// LDRSB (register) calculates an address from a base register value and an offset register value, loadsa byte from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. The offset register value can be +// shifted left by 0, 1, 2, or 3 bits. +bool +EmulateInstructionARM::EmulateLDRSBRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + R[t] = SignExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rt == '1111' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg (m)) + return false; + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRSBT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = SignExtend(MemU[address,1], 32); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// LDRSH (immediate) calculates an address from a base register value and an immediate offset, loads a halfword from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. It can use offset, post-indexed, or +// pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRSHImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE "Unallocated memory hints"; + // if P == '1' && U == '1' && W == '0' then SEE LDRSHT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet (opcode, 10); + add = BitIsSet (opcode, 9); + wback = BitIsSet (opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg (t) || (wback && (n == t))) + return false; + + break; + + case eEncodingA1: + { + // if Rn == '1111' then SEE LDRSH (literal); + // if P == '0' && W == '1' then SEE LDRSHT; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + uint32_t imm4H = Bits32 (opcode, 11,8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + + break; + } + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // R[t] = SignExtend(data, 32); + int64_t signed_data = llvm::SignExtend64<16>(data); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRSH (literal) calculates an address from the PC value and an immediate offset, loads a halfword from memory, +// sign-extends it to from a 32-bit word, and writes it to a register. +bool +EmulateInstructionARM::EmulateLDRSHLiteral (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,2]; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) + { + case eEncodingT1: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + imm32 = Bits32 (opcode, 11, 0); + add = BitIsSet (opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + { + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32 (opcode, 15, 12); + uint32_t imm4H = Bits32 (opcode, 11, 8); + uint32_t imm4L = Bits32 (opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet (opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + break; + } + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + uint64_t base = AlignPC (pc_value); + + addr_t address; + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, imm32); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // R[t] = SignExtend(data, 32); + int64_t signed_data = llvm::SignExtend64<16>(data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// LDRSH (register) calculates an address from a base register value and an offset register value, loads a halfword +// from memory, sign-extends it to form a 32-bit word, and writes it to a register. The offset register value can be +// shifted left by 0, 1, 2, or 3 bits. +bool +EmulateInstructionARM::EmulateLDRSHRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32 (opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRSHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + uint64_t data = MemURead (context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear (address, 0)) + { + // R[t] = SignExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + int64_t signed_data = llvm::SignExtend64<16>(data); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) + return false; + } + else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown (t); + } + } + return true; +} + +// SXTB extracts an 8-bit value from a register, sign-extends it to 32 bits, and writes the result to the destination +// register. You can specifiy a rotation by 0, 8, 16, or 24 bits before extracting the 8-bit value. +bool +EmulateInstructionARM::EmulateSXTB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = SignExtend(rotated<7:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32 (opcode, 2, 0); + m = Bits32 (opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR (Rm, rotation, &success); + if (!success) + return false; + + // R[d] = SignExtend(rotated<7:0>, 32); + int64_t data = llvm::SignExtend64<8>(rotated); + + RegisterInfo source_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister (source_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (uint64_t) data)) + return false; + } + return true; +} + +// SXTH extracts a 16-bit value from a register, sign-extends it to 32 bits, and writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 16-bit value. +bool +EmulateInstructionARM::EmulateSXTH (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = SignExtend(rotated<15:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32 (opcode, 2, 0); + m = Bits32 (opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR (Rm, rotation, &success); + if (!success) + return false; + + // R[d] = SignExtend(rotated<15:0>, 32); + RegisterInfo source_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister (source_reg); + + int64_t data = llvm::SignExtend64<16> (rotated); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (uint64_t) data)) + return false; + } + + return true; +} + +// UXTB extracts an 8-bit value from a register, zero-extneds it to 32 bits, and writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 8-bit value. +bool +EmulateInstructionARM::EmulateUXTB (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = ZeroExtend(rotated<7:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32 (opcode, 2, 0); + m = Bits32 (opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR (Rm, rotation, &success); + if (!success) + return false; + + // R[d] = ZeroExtend(rotated<7:0>, 32); + RegisterInfo source_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister (source_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, Bits32 (rotated, 7, 0))) + return false; + } + return true; +} + +// UXTH extracts a 16-bit value from a register, zero-extends it to 32 bits, and writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 16-bit value. +bool +EmulateInstructionARM::EmulateUXTH (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = ZeroExtend(rotated<15:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + uint32_t rotation; + + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32 (opcode, 2, 0); + m = Bits32 (opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg (d) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + rotation = Bits32 (opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR (Rm, rotation, &success); + if (!success) + return false; + + // R[d] = ZeroExtend(rotated<15:0>, 32); + RegisterInfo source_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister (source_reg); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, Bits32 (rotated, 15, 0))) + return false; + } + return true; +} + +// RFE (Return From Exception) loads the PC and the CPSR from the word at the specified address and the following +// word respectively. +bool +EmulateInstructionARM::EmulateRFE (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then + UNPREDICTABLE; + else + address = if increment then R[n] else R[n]-8; + if wordhigher then address = address+4; + CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); + BranchWritePC(MemA[address,4]); + if wback then R[n] = if increment then R[n]+8 else R[n]-8; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t n; + bool wback; + bool increment; + bool wordhigher; + + // EncodingSpecificOperations(); + switch (encoding) + { + case eEncodingT1: + // n = UInt(Rn); wback = (W == '1'); increment = FALSE; wordhigher = FALSE; + n = Bits32 (opcode, 19, 16); + wback = BitIsSet (opcode, 21); + increment = false; + wordhigher = false; + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingT2: + // n = UInt(Rn); wback = (W == '1'); increment = TRUE; wordhigher = FALSE; + n = Bits32 (opcode, 19, 16); + wback = BitIsSet (opcode, 21); + increment = true; + wordhigher = false; + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); + n = Bits32 (opcode, 19, 16); + + // wback = (W == '1'); inc = (U == '1'); wordhigher = (P == U); + wback = BitIsSet (opcode, 21); + increment = BitIsSet (opcode, 23); + wordhigher = (Bit32 (opcode, 24) == Bit32 (opcode, 23)); + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + break; + + default: + return false; + } + + // if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then + if (!CurrentModeIsPrivileged ()) + // UNPREDICTABLE; + return false; + else + { + uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address; + // address = if increment then R[n] else R[n]-8; + if (increment) + address = Rn; + else + address = Rn - 8; + + // if wordhigher then address = address+4; + if (wordhigher) + address = address + 4; + + // CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextReturnFromException; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint64_t data = MemARead (context, address + 4, 4, 0, &success); + if (!success) + return false; + + CPSRWriteByInstr (data, 15, true); + + // BranchWritePC(MemA[address,4]); + uint64_t data2 = MemARead (context, address, 4, 0, &success); + if (!success) + return false; + + BranchWritePC (context, data2); + + // if wback then R[n] = if increment then R[n]+8 else R[n]-8; + if (wback) + { + context.type = eContextAdjustBaseRegister; + if (increment) + { + context.SetOffset (8); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + 8)) + return false; + } + else + { + context.SetOffset (-8); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn - 8)) + return false; + } + } // if wback + } + } // if ConditionPassed() + return true; +} + +// Bitwise Exclusive OR (immediate) performs a bitwise exclusive OR of a register value and an immediate value, +// and writes the result to the destination register. It can optionally update the condition flags based on +// the result. +bool +EmulateInstructionARM::EmulateEORImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] EOR imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be ORed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rd == '1111' && S == '1' then SEE TEQ (immediate); + if (Rd == 15 && setflags) + return EmulateTEQImm (opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 ^ imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Exclusive OR (register) performs a bitwise exclusive OR of a register value and an +// optionally-shifted register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateEORReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] EOR shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rd == '1111' && S == '1' then SEE TEQ (register); + if (Rd == 15 && setflags) + return EmulateTEQReg (opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 ^ shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise OR (immediate) performs a bitwise (inclusive) OR of a register value and an immediate value, and +// writes the result to the destination register. It can optionally update the condition flags based +// on the result. +bool +EmulateInstructionARM::EmulateORRImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] OR imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be ORed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rn == '1111' then SEE MOV (immediate); + if (Rn == 15) + return EmulateMOVRdImm (opcode, eEncodingT2); + if (BadReg(Rd) || Rn == 13) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 | imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise OR (register) performs a bitwise (inclusive) OR of a register value and an optionally-shifted register +// value, and writes the result to the destination register. It can optionally update the condition flags based +// on the result. +bool +EmulateInstructionARM::EmulateORRReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] OR shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rn == '1111' then SEE MOV (register); + if (Rn == 15) + return EmulateMOVRdRm (opcode, eEncodingT3); + if (BadReg(Rd) || Rn == 13 || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 | shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Reverse Subtract (immediate) subtracts a register value from an immediate value, and writes the result to +// the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateRSBImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm32 = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(~reg_val, imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Reverse Subtract (register) subtracts a register value from an optionally-shifted register value, and writes the +// result to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateRSBReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(~val1, shifted, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Reverse Subtract with Carry (immediate) subtracts a register value and the value of NOT (Carry flag) from +// an immediate value, and writes the result to the destination register. It can optionally update the condition +// flags based on the result. +bool +EmulateInstructionARM::EmulateRSCImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, APSR.C); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(~reg_val, imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Reverse Subtract with Carry (register) subtracts a register value and the value of NOT (Carry flag) from an +// optionally-shifted register value, and writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool +EmulateInstructionARM::EmulateRSCReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, APSR.C); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(~val1, shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Subtract with Carry (immediate) subtracts an immediate value and the value of +// NOT (Carry flag) from a register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateSBCImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Subtract with Carry (register) subtracts an optionally-shifted register value and the value of +// NOT (Carry flag) from a register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateSBCReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, ~shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// This instruction subtracts an immediate value from a register value, and writes the result +// to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateSUBImmThumb (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be subtracted from the value obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 8, 6); // imm32 = ZeroExtend(imm3, 32) + break; + case eEncodingT2: + Rd = Rn = Bits32(opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + + // if Rd == '1111' && S == '1' then SEE CMP (immediate); + if (Rd == 15 && setflags) + return EmulateCMPImm (opcode, eEncodingT2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm (opcode, eEncodingT2); + + // if d == 13 || (d == 15 && S == '0') || n == 15 then UNPREDICTABLE; + if (Rd == 13 || (Rd == 15 && !setflags) || Rn == 15) + return false; + break; + case eEncodingT4: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + + // if Rn == '1111' then SEE ADR; + if (Rn == 15) + return EmulateADR (opcode, eEncodingT2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm (opcode, eEncodingT3); + + if (BadReg(Rd)) + return false; + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// This instruction subtracts an immediate value from a register value, and writes the result +// to the destination register. It can optionally update the condition flags based on the result. +bool +EmulateInstructionARM::EmulateSUBImmARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be subtracted from the value obtained from Rn + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rn == '1111' && S == '0' then SEE ADR; + if (Rn == 15 && !setflags) + return EmulateADR (opcode, eEncodingA2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm (opcode, eEncodingA1); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc (opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// Test Equivalence (immediate) performs a bitwise exclusive OR operation on a register value and an +// immediate value. It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateTEQImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] EOR imm32; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rn; + uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm_C (opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm_C (opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 ^ imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test Equivalence (register) performs a bitwise exclusive OR operation on a register value and an +// optionally-shifted register value. It updates the condition flags based on the result, and discards +// the result. +bool +EmulateInstructionARM::EmulateTEQReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] EOR shifted; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 ^ shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test (immediate) performs a bitwise AND operation on a register value and an immediate value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateTSTImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND imm32; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rn; + uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) + { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test (register) performs a bitwise AND operation on a register value and an optionally-shifted register value. +// It updates the condition flags based on the result, and discards the result. +bool +EmulateInstructionARM::EmulateTSTReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND shifted; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + uint32_t carry; + switch (encoding) + { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// A8.6.216 SUB (SP minus register) +bool +EmulateInstructionARM::EmulateSUBSPReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), Ô1Õ); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t m; + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); setflags = (S == Ô1Õ); + d = Bits32 (opcode, 11, 8); + m = Bits32 (opcode, 3, 0); + setflags = BitIsSet (opcode, 20); + + // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); + shift_n = DecodeImmShiftThumb (opcode, shift_t); + + // if d == 13 && (shift_t != SRType_LSL || shift_n > 3) then UNPREDICTABLE; + if ((d == 13) && ((shift_t != SRType_LSL) || (shift_n > 3))) + return false; + + // if d == 15 || BadReg(m) then UNPREDICTABLE; + if ((d == 15) || BadReg (m)) + return false; + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); setflags = (S == Ô1Õ); + d = Bits32 (opcode, 15, 12); + m = Bits32 (opcode, 3, 0); + setflags = BitIsSet (opcode, 20); + + // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; + if (d == 15 && setflags) + EmulateSUBSPcLrEtc (opcode, encoding); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + shift_n = DecodeImmShiftARM (opcode, shift_t); + break; + + default: + return false; + } + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), Ô1Õ); + uint32_t sp_val = ReadCoreReg (SP_REG, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry (sp_val, ~shifted, 1); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, dwarf_reg); + context.SetRegisterRegisterOperands (sp_reg, dwarf_reg); + + if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + + +// A8.6.7 ADD (register-shifted register) +bool +EmulateInstructionARM::EmulateADDRegShift (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[s]<7:0>); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, Ô0Õ); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t n; + uint32_t m; + uint32_t s; + bool setflags; + ARM_ShifterType shift_t; + + switch (encoding) + { + case eEncodingA1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); s = UInt(Rs); + d = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + s = Bits32 (opcode, 11, 8); + + // setflags = (S == Ô1Õ); shift_t = DecodeRegShift(type); + setflags = BitIsSet (opcode, 20); + shift_t = DecodeRegShift (Bits32 (opcode, 6, 5)); + + // if d == 15 || n == 15 || m == 15 || s == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15) || (m == 15) || (s == 15)) + return false; + break; + + default: + return false; + } + + // shift_n = UInt(R[s]<7:0>); + uint32_t Rs = ReadCoreReg (s, &success); + if (!success) + return false; + + uint32_t shift_n = Bits32 (Rs, 7, 0); + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(R[n], shifted, Ô0Õ); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry (Rn, shifted, 0); + + // R[d] = result; + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo reg_n; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); + RegisterInfo reg_m; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, reg_m); + + context.SetRegisterRegisterOperands (reg_n, reg_m); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, res.result)) + return false; + + // if setflags then + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + if (setflags) + return WriteFlags (context, res.result, res.carry_out, res.overflow); + } + return true; +} + +// A8.6.213 SUB (register) +bool +EmulateInstructionARM::EmulateSUBReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), Ô1Õ); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t n; + uint32_t m; + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = !InITBlock(); + d = Bits32 (opcode, 2, 0); + n = Bits32 (opcode, 5, 3); + m = Bits32 (opcode, 8, 6); + setflags = !InITBlock(); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rd == Ô1111Õ && S == Ô1Õ then SEE CMP (register); + // if Rn == Ô1101Õ then SEE SUB (SP minus register); + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == Ô1Õ); + d = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + setflags = BitIsSet (opcode, 20); + + // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); + shift_n = DecodeImmShiftThumb (opcode, shift_t); + + // if d == 13 || (d == 15 && S == '0') || n == 15 || BadReg(m) then UNPREDICTABLE; + if ((d == 13) || ((d == 15) && BitIsClear (opcode, 20)) || (n == 15) || BadReg (m)) + return false; + + break; + + case eEncodingA1: + // if Rn == Ô1101Õ then SEE SUB (SP minus register); + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == Ô1Õ); + d = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + setflags = BitIsSet (opcode, 20); + + // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; + if ((d == 15) && setflags) + EmulateSUBSPcLrEtc (opcode, encoding); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + shift_n = DecodeImmShiftARM (opcode, shift_t); + + break; + + default: + return false; + } + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), Ô1Õ); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry (Rn, ~shifted, 1); + + // if d == 15 then // Can only occur for ARM encoding + // ALUWritePC(result); // setflags is always FALSE here + // else + // R[d] = result; + // if setflags then + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + RegisterInfo reg_n; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); + RegisterInfo reg_m; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, reg_m); + context.SetRegisterRegisterOperands (reg_n, reg_m); + + if (!WriteCoreRegOptionalFlags (context, res.result, dwarf_r0 + d, setflags, res.carry_out, res.overflow)) + return false; + } + return true; +} + +// A8.6.202 STREX +// Store Register Exclusive calculates an address from a base register value and an immediate offset, and stores a +// word from a register to memory if the executing processor has exclusive access to the memory addressed. +bool +EmulateInstructionARM::EmulateSTREX (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] + imm32; + if ExclusiveMonitorsPass(address,4) then + MemA[address,4] = R[t]; + R[d] = 0; + else + R[d] = 1; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t d; + uint32_t t; + uint32_t n; + uint32_t imm32; + const uint32_t addr_byte_size = GetAddressByteSize(); + + switch (encoding) + { + case eEncodingT1: + // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + d = Bits32 (opcode, 11, 8); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // if BadReg(d) || BadReg(t) || n == 15 then UNPREDICTABLE; + if (BadReg (d) || BadReg (t) || (n == 15)) + return false; + + // if d == n || d == t then UNPREDICTABLE; + if ((d == n) || (d == t)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = Zeros(32); // Zero offset + d = Bits32 (opcode, 15, 12); + t = Bits32 (opcode, 3, 0); + n = Bits32 (opcode, 19, 16); + imm32 = 0; + + // if d == 15 || t == 15 || n == 15 then UNPREDICTABLE; + if ((d == 15) || (t == 15) || (n == 15)) + return false; + + // if d == n || d == t then UNPREDICTABLE; + if ((d == n) || (d == t)) + return false; + + break; + + default: + return false; + } + + // address = R[n] + imm32; + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t address = Rn + imm32; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, imm32); + + // if ExclusiveMonitorsPass(address,4) then + // if (ExclusiveMonitorsPass (address, addr_byte_size)) -- For now, for the sake of emulation, we will say this + // always return true. + if (true) + { + // MemA[address,4] = R[t]; + uint32_t Rt = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + if (!MemAWrite (context, address, Rt, addr_byte_size)) + return false; + + // R[d] = 0; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 0)) + return false; + } + else + { + // R[d] = 1; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1)) + return false; + } + } + return true; +} + +// A8.6.197 STRB (immediate, ARM) +bool +EmulateInstructionARM::EmulateSTRBImmARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,1] = R[t]<7:0>; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingA1: + // if P == Ô0Õ && W == Ô1Õ then SEE STRBT; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + // MemU[address,1] = R[t]<7:0>; + uint32_t Rt = ReadCoreReg (t, &success); + if (!success) + return false; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + if (!MemUWrite (context, address, Bits32 (Rt, 7, 0), 1)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// A8.6.194 STR (immediate, ARM) +bool +EmulateInstructionARM::EmulateSTRImmARM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + const uint32_t addr_byte_size = GetAddressByteSize(); + + switch (encoding) + { + case eEncodingA1: + // if P == Ô0Õ && W == Ô1Õ then SEE STRT; + // if Rn == Ô1101Õ && P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && imm12 == Ô000000000100Õ then SEE PUSH; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 11, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + // MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + uint32_t Rt = ReadCoreReg (t, &success); + if (!success) + return false; + + if (t == 15) + { + uint32_t pc_value = ReadCoreReg (PC_REG, &success); + if (!success) + return false; + + if (!MemUWrite (context, address, pc_value, addr_byte_size)) + return false; + } + else + { + if (!MemUWrite (context, address, Rt, addr_byte_size)) + return false; + } + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetImmediate (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// A8.6.66 LDRD (immediate) +// Load Register Dual (immediate) calculates an address from a base register value and an immediate offset, loads two +// words from memory, and writes them to two registers. It can use offset, post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRDImmediate (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = MemA[address,4]; + R[t2] = MemA[address+4,4]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingT1: + //if P == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + //if Rn == Ô1111Õ then SEE LDRD (literal); + //t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + t = Bits32 (opcode, 15, 12); + t2 = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + //index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + + //if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + //if BadReg(t) || BadReg(t2) || t == t2 then UNPREDICTABLE; + if (BadReg (t) || BadReg (t2) || (t == t2)) + return false; + + break; + + case eEncodingA1: + //if Rn == Ô1111Õ then SEE LDRD (literal); + //if Rt<0> == Ô1Õ then UNPREDICTABLE; + //t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32 (opcode, 15, 12); + if (BitIsSet (t, 0)) + return false; + t2 = t + 1; + n = Bits32 (opcode, 19, 16); + imm32 = (Bits32 (opcode, 11, 8) << 4) | Bits32 (opcode, 3, 0); + + //index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + //if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; + if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) + return false; + + //if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + //if t2 == 15 then UNPREDICTABLE; + if (t2 == 15) + return false; + + break; + + default: + return false; + } + + //offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + //address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + //R[t] = MemA[address,4]; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + //R[t2] = MemA[address+4,4]; + + context.SetRegisterPlusOffset (base_reg, (address + 4) - Rn); + data = MemARead (context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t2, data)) + return false; + + //if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// A8.6.68 LDRD (register) +// Load Register Dual (register) calculates an address from a base register value and a register offset, loads two +// words from memory, and writes them to two registers. It can use offset, post-indexed or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateLDRDRegister (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + address = if index then offset_addr else R[n]; + R[t] = MemA[address,4]; + R[t2] = MemA[address+4,4]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingA1: + // if Rt<0> == Ô1Õ then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + if (BitIsSet (t, 0)) + return false; + t2 = t + 1; + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; + if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) + return false; + + // if t2 == 15 || m == 15 || m == t || m == t2 then UNPREDICTABLE; + if ((t2 == 15) || (m == 15) || (m == t) || (m == t2)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; + if ((ArchVersion() < 6) && wback && (m == n)) + return false; + break; + + default: + return false; + } + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + + // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + addr_t offset_addr; + if (add) + offset_addr = Rn + Rm; + else + offset_addr = Rn - Rm; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + // R[t] = MemA[address,4]; + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // R[t2] = MemA[address+4,4]; + + data = MemARead (context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t2, data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + +// A8.6.200 STRD (immediate) +// Store Register Dual (immediate) calculates an address from a base register value and an immediate offset, and +// stores two words from two registers to memory. It can use offset, post-indexed, or pre-indexed addressing. +bool +EmulateInstructionARM::EmulateSTRDImm (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemA[address,4] = R[t]; + MemA[address+4,4] = R[t2]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingT1: + // if P == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + t = Bits32 (opcode, 15, 12); + t2 = Bits32 (opcode, 11, 8); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + + // if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + // if n == 15 || BadReg(t) || BadReg(t2) then UNPREDICTABLE; + if ((n == 15) || BadReg (t) || BadReg (t2)) + return false; + + break; + + case eEncodingA1: + // if Rt<0> == Ô1Õ then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32 (opcode, 15, 12); + if (BitIsSet (t, 0)) + return false; + + t2 = t + 1; + n = Bits32 (opcode, 19, 16); + imm32 = (Bits32 (opcode, 11, 8) << 4) | Bits32 (opcode, 3, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; + if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if t2 == 15 then UNPREDICTABLE; + if (t2 == 15) + return false; + + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + //offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + //address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + //MemA[address,4] = R[t]; + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + + uint32_t data = ReadCoreReg (t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + const uint32_t addr_byte_size = GetAddressByteSize(); + + if (!MemAWrite (context, address, data, addr_byte_size)) + return false; + + //MemA[address+4,4] = R[t2]; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t2, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + + data = ReadCoreReg (t2, &success); + if (!success) + return false; + + if (!MemAWrite (context, address + 4, data, addr_byte_size)) + return false; + + //if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + } + } + return true; +} + + +// A8.6.201 STRD (register) +bool +EmulateInstructionARM::EmulateSTRDReg (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + address = if index then offset_addr else R[n]; + MemA[address,4] = R[t]; + MemA[address+4,4] = R[t2]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + + switch (encoding) + { + case eEncodingA1: + // if Rt<0> == Ô1Õ then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); + t = Bits32 (opcode, 15, 12); + if (BitIsSet (t, 0)) + return false; + + t2 = t+1; + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); + index = BitIsSet (opcode, 24); + add = BitIsSet (opcode, 23); + wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); + + // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; + if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) + return false; + + // if t2 == 15 || m == 15 then UNPREDICTABLE; + if ((t2 == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; + if ((ArchVersion() < 6) && wback && (m == n)) + return false; + + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + RegisterInfo offset_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); + RegisterInfo data_reg; + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + addr_t offset_addr; + if (add) + offset_addr = Rn + Rm; + else + offset_addr = Rn - Rm; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + // MemA[address,4] = R[t]; + uint32_t Rt = ReadCoreReg (t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); + context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); + + const uint32_t addr_byte_size = GetAddressByteSize(); + + if (!MemAWrite (context, address, Rt, addr_byte_size)) + return false; + + // MemA[address+4,4] = R[t2]; + uint32_t Rt2 = ReadCoreReg (t2, &success); + if (!success) + return false; + + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t2, data_reg); + + context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); + + if (!MemAWrite (context, address + 4, Rt2, addr_byte_size)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) + { + context.type = eContextAdjustBaseRegister; + context.SetAddress (offset_addr); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) + return false; + + } + } + return true; +} + +// A8.6.319 VLDM +// Vector Load Multiple loads multiple extension registers from consecutive memory locations using an address from +// an ARM core register. +bool +EmulateInstructionARM::EmulateVLDM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then R[n] else R[n]-imm32; + if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + for r = 0 to regs-1 + if single_regs then + S[d+r] = MemA[address,4]; address = address+4; + else + word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; + // Combine the word-aligned words in the correct order for current endianness. + D[d+r] = if BigEndian() then word1:word2 else word2:word1; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) + { + bool single_regs; + bool add; + bool wback; + uint32_t d; + uint32_t n; + uint32_t imm32; + uint32_t regs; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // if P == Ô0Õ && U == Ô1Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPOP; + // if P == Ô1Õ && W == Ô0Õ then SEE VLDR; + // if P == U && W == Ô1Õ then UNDEFINED; + if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) + // single_regs = FALSE; add = (U == Ô1Õ); wback = (W == Ô1Õ); + single_regs = false; + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + + // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see ÒFLDMXÓ. + regs = Bits32 (opcode, 7, 0) / 2; + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; + if (n == 15 && (wback || CurrentInstrSet() != eModeARM)) + return false; + + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // if P == Ô0Õ && U == Ô1Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPOP; + // if P == Ô1Õ && W == Ô0Õ then SEE VLDR; + // if P == U && W == Ô1Õ then UNDEFINED; + if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) + // single_regs = TRUE; add = (U == Ô1Õ); wback = (W == Ô1Õ); d = UInt(Vd:D); n = UInt(Rn); + single_regs = true; + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); + n = Bits32 (opcode, 19, 16); + + // imm32 = ZeroExtend(imm8:Õ00Õ, 32); regs = UInt(imm8); + imm32 = Bits32 (opcode, 7, 0) << 2; + regs = Bits32 (opcode, 7, 0); + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) + return false; + + // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || ((d + regs) > 32)) + return false; + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = if add then R[n] else R[n]-imm32; + addr_t address; + if (add) + address = Rn; + else + address = Rn - imm32; + + // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + EmulateInstruction::Context context; + + if (wback) + { + uint32_t value; + if (add) + value = Rn + imm32; + else + value = Rn - imm32; + + context.type = eContextAdjustBaseRegister; + context.SetImmediateSigned (value - Rn); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) + return false; + + } + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + + context.type = eContextRegisterLoad; + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + if (single_regs) + { + // S[d+r] = MemA[address,4]; address = address+4; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d + r, data)) + return false; + + address = address + 4; + } + else + { + // word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; + context.SetRegisterPlusOffset (base_reg, address - Rn); + uint32_t word1 = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + context.SetRegisterPlusOffset (base_reg, (address + 4) - Rn); + uint32_t word2 = MemARead (context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + address = address + 8; + // // Combine the word-aligned words in the correct order for current endianness. + // D[d+r] = if BigEndian() then word1:word2 else word2:word1; + uint64_t data; + if (GetByteOrder() == eByteOrderBig) + { + data = word1; + data = (data << 32) | word2; + } + else + { + data = word2; + data = (data << 32) | word1; + } + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d + r, data)) + return false; + } + } + } + return true; +} + +// A8.6.399 VSTM +// Vector Store Multiple stores multiple extension registers to consecutive memory locations using an address from an +// ARM core register. +bool +EmulateInstructionARM::EmulateVSTM (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then R[n] else R[n]-imm32; + if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + for r = 0 to regs-1 + if single_regs then + MemA[address,4] = S[d+r]; address = address+4; + else + // Store as two word-aligned words in the correct order for current endianness. + MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; + address = address+8; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + bool single_regs; + bool add; + bool wback; + uint32_t d; + uint32_t n; + uint32_t imm32; + uint32_t regs; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // if P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPUSH; + // if P == Ô1Õ && W == Ô0Õ then SEE VSTR; + // if P == U && W == Ô1Õ then UNDEFINED; + if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) + // single_regs = FALSE; add = (U == Ô1Õ); wback = (W == Ô1Õ); + single_regs = false; + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + + // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see ÒFSTMXÓ. + regs = Bits32 (opcode, 7, 0) / 2; + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) + return false; + + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; + // if P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPUSH; + // if P == Ô1Õ && W == Ô0Õ then SEE VSTR; + // if P == U && W == Ô1Õ then UNDEFINED; + if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) + // single_regs = TRUE; add = (U == Ô1Õ); wback = (W == Ô1Õ); d = UInt(Vd:D); n = UInt(Rn); + single_regs = true; + add = BitIsSet (opcode, 23); + wback = BitIsSet (opcode, 21); + d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); + n = Bits32 (opcode, 19, 16); + + // imm32 = ZeroExtend(imm8:Õ00Õ, 32); regs = UInt(imm8); + imm32 = Bits32 (opcode, 7, 0) << 2; + regs = Bits32 (opcode, 7, 0); + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet () != eModeARM))) + return false; + + // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || ((d + regs) > 32)) + return false; + + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = if add then R[n] else R[n]-imm32; + addr_t address; + if (add) + address = Rn; + else + address = Rn - imm32; + + EmulateInstruction::Context context; + // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + if (wback) + { + uint32_t value; + if (add) + value = Rn + imm32; + else + value = Rn - imm32; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, value - Rn); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) + return false; + } + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + + context.type = eContextRegisterStore; + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + + if (single_regs) + { + // MemA[address,4] = S[d+r]; address = address+4; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d + r, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, start_reg + d + r, data_reg); + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + if (!MemAWrite (context, address, data, addr_byte_size)) + return false; + + address = address + 4; + } + else + { + // // Store as two word-aligned words in the correct order for current endianness. + // MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; + // MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; + uint64_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d + r, 0, &success); + if (!success) + return false; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, start_reg + d + r, data_reg); + + if (GetByteOrder() == eByteOrderBig) + { + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + if (!MemAWrite (context, address, Bits64 (data, 63, 32), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + if (!MemAWrite (context, address+ 4, Bits64 (data, 31, 0), addr_byte_size)) + return false; + } + else + { + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + if (!MemAWrite (context, address, Bits64 (data, 31, 0), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + if (!MemAWrite (context, address + 4, Bits64 (data, 63, 32), addr_byte_size)) + return false; + } + // address = address+8; + address = address + 8; + } + } + } + return true; +} + +// A8.6.320 +// This instruciton loads a single extension register fronm memory, using an address from an ARM core register, with +// an optional offset. +bool +EmulateInstructionARM::EmulateVLDR (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + base = if n == 15 then Align(PC,4) else R[n]; + address = if add then (base + imm32) else (base - imm32); + if single_reg then + S[d] = MemA[address,4]; + else + word1 = MemA[address,4]; word2 = MemA[address+4,4]; + // Combine the word-aligned words in the correct order for current endianness. + D[d] = if BigEndian() then word1:word2 else word2:word1; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + bool single_reg; + bool add; + uint32_t imm32; + uint32_t d; + uint32_t n; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + // single_reg = FALSE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + single_reg = false; + add = BitIsSet (opcode, 23); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // d = UInt(D:Vd); n = UInt(Rn); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + + break; + + case eEncodingT2: + case eEncodingA2: + // single_reg = TRUE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + single_reg = true; + add = BitIsSet (opcode, 23); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // d = UInt(Vd:D); n = UInt(Rn); + d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); + n = Bits32 (opcode, 19, 16); + + break; + + default: + return false; + } + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // base = if n == 15 then Align(PC,4) else R[n]; + uint32_t base; + if (n == 15) + base = AlignPC (Rn); + else + base = Rn; + + // address = if add then (base + imm32) else (base - imm32); + addr_t address; + if (add) + address = base + imm32; + else + address = base - imm32; + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - base); + + if (single_reg) + { + // S[d] = MemA[address,4]; + uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d, data)) + return false; + } + else + { + // word1 = MemA[address,4]; word2 = MemA[address+4,4]; + uint32_t word1 = MemARead (context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + context.SetRegisterPlusOffset (base_reg, (address + 4) - base); + uint32_t word2 = MemARead (context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + // // Combine the word-aligned words in the correct order for current endianness. + // D[d] = if BigEndian() then word1:word2 else word2:word1; + uint64_t data64; + if (GetByteOrder() == eByteOrderBig) + { + data64 = word1; + data64 = (data64 << 32) | word2; + } + else + { + data64 = word2; + data64 = (data64 << 32) | word1; + } + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d, data64)) + return false; + } + } + return true; +} + +// A8.6.400 VSTR +// This instruction stores a signle extension register to memory, using an address from an ARM core register, with an +// optional offset. +bool +EmulateInstructionARM::EmulateVSTR (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then (R[n] + imm32) else (R[n] - imm32); + if single_reg then + MemA[address,4] = S[d]; + else + // Store as two word-aligned words in the correct order for current endianness. + MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + bool single_reg; + bool add; + uint32_t imm32; + uint32_t d; + uint32_t n; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + // single_reg = FALSE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + single_reg = false; + add = BitIsSet (opcode, 23); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // d = UInt(D:Vd); n = UInt(Rn); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + + // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; + if ((n == 15) && (CurrentInstrSet() != eModeARM)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // single_reg = TRUE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); + single_reg = true; + add = BitIsSet (opcode, 23); + imm32 = Bits32 (opcode, 7, 0) << 2; + + // d = UInt(Vd:D); n = UInt(Rn); + d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); + n = Bits32 (opcode, 19, 16); + + // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; + if ((n == 15) && (CurrentInstrSet() != eModeARM)) + return false; + + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = if add then (R[n] + imm32) else (R[n] - imm32); + addr_t address; + if (add) + address = Rn + imm32; + else + address = Rn - imm32; + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, start_reg + d, data_reg); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + if (single_reg) + { + // MemA[address,4] = S[d]; + uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d, 0, &success); + if (!success) + return false; + + if (!MemAWrite (context, address, data, addr_byte_size)) + return false; + } + else + { + // // Store as two word-aligned words in the correct order for current endianness. + // MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; + // MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; + uint64_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d, 0, &success); + if (!success) + return false; + + if (GetByteOrder() == eByteOrderBig) + { + if (!MemAWrite (context, address, Bits64 (data, 63, 32), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + if (!MemAWrite (context, address + 4, Bits64 (data, 31, 0), addr_byte_size)) + return false; + } + else + { + if (!MemAWrite (context, address, Bits64 (data, 31, 0), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); + if (!MemAWrite (context, address + 4, Bits64 (data, 63, 32), addr_byte_size)) + return false; + } + } + } + return true; +} + +// A8.6.307 VLDI1 (multiple single elements) +// This instruction loads elements from memory into one, two, three or four registers, without de-interleaving. Every +// element of each register is loaded. +bool +EmulateInstructionARM::EmulateVLD1Multiple (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + for r = 0 to regs-1 + for e = 0 to elements-1 + Elem[D[d+r],e,esize] = MemU[address,ebytes]; + address = address + ebytes; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t regs; + uint32_t alignment; + uint32_t ebytes; + uint32_t esize; + uint32_t elements; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + // case type of + // when Ô0111Õ + // regs = 1; if align<1> == Ô1Õ then UNDEFINED; + // when Ô1010Õ + // regs = 2; if align == Ô11Õ then UNDEFINED; + // when Ô0110Õ + // regs = 3; if align<1> == Ô1Õ then UNDEFINED; + // when Ô0010Õ + // regs = 4; + // otherwise + // SEE ÒRelated encodingsÓ; + uint32_t type = Bits32 (opcode, 11, 8); + uint32_t align = Bits32 (opcode, 5, 4); + if (type == 7) // '0111' + { + regs = 1; + if (BitIsSet (align, 1)) + return false; + } + else if (type == 10) // '1010' + { + regs = 2; + if (align == 3) + return false; + + } + else if (type == 6) // '0110' + { + regs = 3; + if (BitIsSet (align, 1)) + return false; + } + else if (type == 2) // '0010' + { + regs = 4; + } + else + return false; + + // alignment = if align == Ô00Õ then 1 else 4 << UInt(align); + if (align == 0) + alignment = 1; + else + alignment = 4 << align; + + // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; + ebytes = 1 << Bits32 (opcode, 7, 6); + esize = 8 * ebytes; + elements = 8 / ebytes; + + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 15); + m = Bits32 (opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + // if d+regs > 32 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = 8 * regs; + + uint32_t value = Rn + offset; + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) + return false; + + } + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + // for e = 0 to elements-1 + uint64_t assembled_data = 0; + for (uint32_t e = 0; e < elements; ++e) + { + // Elem[D[d+r],e,esize] = MemU[address,ebytes]; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + uint64_t data = MemURead (context, address, ebytes, 0, &success); + if (!success) + return false; + + assembled_data = (data << (e * esize)) | assembled_data; // New data goes to the left of existing data + + // address = address + ebytes; + address = address + ebytes; + } + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_d0 + d + r, assembled_data)) + return false; + } + } + return true; +} + +// A8.6.308 VLD1 (single element to one lane) +// +bool +EmulateInstructionARM::EmulateVLD1Single (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + Elem[D[d],index,esize] = MemU[address,ebytes]; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t ebytes; + uint32_t esize; + uint32_t index; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + uint32_t size = Bits32 (opcode, 11, 10); + uint32_t index_align = Bits32 (opcode, 7, 4); + // if size == Ô11Õ then SEE VLD1 (single element to all lanes); + if (size == 3) + return EmulateVLD1SingleAll (opcode, encoding); + // case size of + if (size == 0) // when '00' + { + // if index_align<0> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 0)) + return false; + + // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; + ebytes = 1; + esize = 8; + index = Bits32 (index_align, 3, 1); + alignment = 1; + } + else if (size == 1) // when Ô01Õ + { + // if index_align<1> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 1)) + return false; + + // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); + ebytes = 2; + esize = 16; + index = Bits32 (index_align, 3, 2); + + // alignment = if index_align<0> == Ô0Õ then 1 else 2; + if (BitIsClear (index_align, 0)) + alignment = 1; + else + alignment = 2; + } + else if (size == 2) // when Ô10Õ + { + // if index_align<2> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 2)) + return false; + + // if index_align<1:0> != Ô00Õ && index_align<1:0> != Ô11Õ then UNDEFINED; + if ((Bits32 (index_align, 1, 0) != 0) && (Bits32 (index_align, 1, 0) != 3)) + return false; + + // ebytes = 4; esize = 32; index = UInt(index_align<3>); + ebytes = 4; + esize = 32; + index = Bit32 (index_align, 3); + + // alignment = if index_align<1:0> == Ô00Õ then 1 else 4; + if (Bits32 (index_align, 1, 0) == 0) + alignment = 1; + else + alignment = 4; + } + else + { + return false; + } + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 then UNPREDICTABLE; + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + if (n == 15) + return false; + + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + uint32_t value = Rn + offset; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) + return false; + } + + // Elem[D[d],index,esize] = MemU[address,ebytes]; + uint32_t element = MemURead (context, address, esize, 0, &success); + if (!success) + return false; + + element = element << (index * esize); + + uint64_t reg_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d, 0, &success); + if (!success) + return false; + + uint64_t all_ones = -1; + uint64_t mask = all_ones << ((index+1) * esize); // mask is all 1's to left of where 'element' goes, & all 0's + // at element & to the right of element. + if (index > 0) + mask = mask | Bits64 (all_ones, (index * esize) - 1, 0); // add 1's to the right of where 'element' goes. + // now mask should be 0's where element goes & 1's + // everywhere else. + + uint64_t masked_reg = reg_data & mask; // Take original reg value & zero out 'element' bits + reg_data = masked_reg & element; // Put 'element' into those bits in reg_data. + + context.type = eContextRegisterLoad; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, reg_data)) + return false; + } + return true; +} + +// A8.6.391 VST1 (multiple single elements) +// Vector Store (multiple single elements) stores elements to memory from one, two, three, or four regsiters, without +// interleaving. Every element of each register is stored. +bool +EmulateInstructionARM::EmulateVST1Multiple (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + for r = 0 to regs-1 + for e = 0 to elements-1 + MemU[address,ebytes] = Elem[D[d+r],e,esize]; + address = address + ebytes; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t regs; + uint32_t alignment; + uint32_t ebytes; + uint32_t esize; + uint32_t elements; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + uint32_t type = Bits32 (opcode, 11, 8); + uint32_t align = Bits32 (opcode, 5, 4); + + // case type of + if (type == 7) // when Ô0111Õ + { + // regs = 1; if align<1> == Ô1Õ then UNDEFINED; + regs = 1; + if (BitIsSet (align, 1)) + return false; + } + else if (type == 10) // when Ô1010Õ + { + // regs = 2; if align == Ô11Õ then UNDEFINED; + regs = 2; + if (align == 3) + return false; + } + else if (type == 6) // when Ô0110Õ + { + // regs = 3; if align<1> == Ô1Õ then UNDEFINED; + regs = 3; + if (BitIsSet (align, 1)) + return false; + } + else if (type == 2) // when Ô0010Õ + // regs = 4; + regs = 4; + else // otherwise + // SEE ÒRelated encodingsÓ; + return false; + + // alignment = if align == Ô00Õ then 1 else 4 << UInt(align); + if (align == 0) + alignment = 1; + else + alignment = 4 << align; + + // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; + ebytes = 1 << Bits32 (opcode,7, 6); + esize = 8 * ebytes; + elements = 8 / ebytes; + + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + + if (n == 15) + return false; + + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = 8 * regs; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) + return false; + } + + RegisterInfo data_reg; + context.type = eContextRegisterStore; + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + GetRegisterInfo (eRegisterKindDWARF, dwarf_d0 + d + r, data_reg); + uint64_t register_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d + r, 0, &success); + if (!success) + return false; + + // for e = 0 to elements-1 + for (uint32_t e = 0; e < elements; ++e) + { + // MemU[address,ebytes] = Elem[D[d+r],e,esize]; + uint64_t word = Bits64 (register_data, ((e + 1) * esize) - 1, e * esize); + + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + if (!MemUWrite (context, address, word, ebytes)) + return false; + + // address = address + ebytes; + address = address + ebytes; + } + } + } + return true; +} + +// A8.6.392 VST1 (single element from one lane) +// This instruction stores one element to memory from one element of a register. +bool +EmulateInstructionARM::EmulateVST1Single (const uint32_t opcode, ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + MemU[address,ebytes] = Elem[D[d],index,esize]; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t ebytes; + uint32_t esize; + uint32_t index; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + uint32_t size = Bits32 (opcode, 11, 10); + uint32_t index_align = Bits32 (opcode, 7, 4); + + // if size == Ô11Õ then UNDEFINED; + if (size == 3) + return false; + + // case size of + if (size == 0) // when Ô00Õ + { + // if index_align<0> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 0)) + return false; + // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; + ebytes = 1; + esize = 8; + index = Bits32 (index_align, 3, 1); + alignment = 1; + } + else if (size == 1) // when Ô01Õ + { + // if index_align<1> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 1)) + return false; + + // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); + ebytes = 2; + esize = 16; + index = Bits32 (index_align, 3, 2); + + // alignment = if index_align<0> == Ô0Õ then 1 else 2; + if (BitIsClear (index_align, 0)) + alignment = 1; + else + alignment = 2; + } + else if (size == 2) // when Ô10Õ + { + // if index_align<2> != Ô0Õ then UNDEFINED; + if (BitIsClear (index_align, 2)) + return false; + + // if index_align<1:0> != Ô00Õ && index_align<1:0> != Ô11Õ then UNDEFINED; + if ((Bits32 (index_align, 1, 0) != 0) && (Bits32 (index_align, 1, 0) != 3)) + return false; + + // ebytes = 4; esize = 32; index = UInt(index_align<3>); + ebytes = 4; + esize = 32; + index = Bit32 (index_align, 3); + + // alignment = if index_align<1:0> == Ô00Õ then 1 else 4; + if (Bits32 (index_align, 1, 0) == 0) + alignment = 1; + else + alignment = 4; + } + else + { + return false; + } + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 then UNPREDICTABLE; + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + if (n == 15) + return false; + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) + return false; + } + + // MemU[address,ebytes] = Elem[D[d],index,esize]; + uint64_t register_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d, 0, &success); + if (!success) + return false; + + uint64_t word = Bits64 (register_data, ((index + 1) * esize) - 1, index * esize); + + RegisterInfo data_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_d0 + d, data_reg); + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); + + if (!MemUWrite (context, address, word, ebytes)) + return false; + } + return true; +} + +// A8.6.309 VLD1 (single element to all lanes) +// This instruction loads one element from memory into every element of one or two vectors. +bool +EmulateInstructionARM::EmulateVLD1SingleAll (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + replicated_element = Replicate(MemU[address,ebytes], elements); + for r = 0 to regs-1 + D[d+r] = replicated_element; +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t ebytes; + uint32_t elements; + uint32_t regs; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) + { + case eEncodingT1: + case eEncodingA1: + { + //if size == Ô11Õ || (size == Ô00Õ && a == Ô1Õ) then UNDEFINED; + uint32_t size = Bits32 (opcode, 7, 6); + if ((size == 3) || ((size == 0) && BitIsSet (opcode, 4))) + return false; + + //ebytes = 1 << UInt(size); elements = 8 DIV ebytes; regs = if T == Ô0Õ then 1 else 2; + ebytes = 1 << size; + elements = 8 / ebytes; + if (BitIsClear (opcode, 5)) + regs = 1; + else + regs = 2; + + //alignment = if a == Ô0Õ then 1 else ebytes; + if (BitIsClear (opcode, 4)) + alignment = 1; + else + alignment = ebytes; + + //d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + + //wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + //if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + + if (n == 15) + return false; + } + break; + + default: + return false; + } + + RegisterInfo base_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset (base_reg, offset); + + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) + return false; + } + + // replicated_element = Replicate(MemU[address,ebytes], elements); + + context.type = eContextRegisterLoad; + uint64_t word = MemURead (context, address, ebytes, 0, &success); + if (!success) + return false; + + uint64_t replicated_element = 0; + uint32_t esize = ebytes * 8; + for (uint32_t e = 0; e < elements; ++e) + replicated_element = (replicated_element << esize) | Bits64 (word, esize - 1, 0); + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) + { + // D[d+r] = replicated_element; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_d0 + d + r, replicated_element)) + return false; + } + } + return true; +} + +// B6.2.13 SUBS PC, LR and related instructions +//The SUBS PC, LR, #<const? instruction provides an exception return without the use of the stack. It subtracts the +// immediate constant from the LR, branches to the resulting address, and also copies the SPSR to the CPSR. +bool +EmulateInstructionARM::EmulateSUBSPcLrEtc (const uint32_t opcode, const ARMEncoding encoding) +{ +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + if CurrentInstrSet() == InstrSet_ThumbEE then + UNPREDICTABLE; + operand2 = if register_form then Shift(R[m], shift_t, shift_n, APSR.C) else imm32; + case opcode of + when Ô0000Õ result = R[n] AND operand2; // AND + when Ô0001Õ result = R[n] EOR operand2; // EOR + when Ô0010Õ (result, -, -) = AddWithCarry(R[n], NOT(operand2), Ô1Õ); // SUB + when Ô0011Õ (result, -, -) = AddWithCarry(NOT(R[n]), operand2, Ô1Õ); // RSB + when Ô0100Õ (result, -, -) = AddWithCarry(R[n], operand2, Ô0Õ); // ADD + when Ô0101Õ (result, -, -) = AddWithCarry(R[n], operand2, APSR.c); // ADC + when Ô0110Õ (result, -, -) = AddWithCarry(R[n], NOT(operand2), APSR.C); // SBC + when Ô0111Õ (result, -, -) = AddWithCarry(NOT(R[n]), operand2, APSR.C); // RSC + when Ô1100Õ result = R[n] OR operand2; // ORR + when Ô1101Õ result = operand2; // MOV + when Ô1110Õ result = R[n] AND NOT(operand2); // BIC + when Ô1111Õ result = NOT(operand2); // MVN + CPSRWriteByInstr(SPSR[], Ô1111Õ, TRUE); + BranchWritePC(result); +#endif + + bool success = false; + + if (ConditionPassed (opcode)) + { + uint32_t n; + uint32_t m; + uint32_t imm32; + bool register_form; + ARM_ShifterType shift_t; + uint32_t shift_n; + uint32_t code; + + switch (encoding) + { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then UNPREDICTABLE + // n = 14; imm32 = ZeroExtend(imm8, 32); register_form = FALSE; opcode = Ô0010Õ; // = SUB + n = 14; + imm32 = Bits32 (opcode, 7, 0); + register_form = false; + code = 2; + + // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); imm32 = ARMExpandImm(imm12); register_form = FALSE; + n = Bits32 (opcode, 19, 16); + imm32 = ARMExpandImm (opcode); + register_form = false; + code = Bits32 (opcode, 24, 21); + + break; + + case eEncodingA2: + // n = UInt(Rn); m = UInt(Rm); register_form = TRUE; + n = Bits32 (opcode, 19, 16); + m = Bits32 (opcode, 3, 0); + register_form = true; + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + shift_n = DecodeImmShiftARM (opcode, shift_t); + + break; + + default: + return false; + } + + // operand2 = if register_form then Shift(R[m], shift_t, shift_n, APSR.C) else imm32; + uint32_t operand2; + if (register_form) + { + uint32_t Rm = ReadCoreReg (m, &success); + if (!success) + return false; + + operand2 = Shift (Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + } + else + { + operand2 = imm32; + } + + uint32_t Rn = ReadCoreReg (n, &success); + if (!success) + return false; + + AddWithCarryResult result; + + // case opcode of + switch (code) + { + case 0: // when Ô0000Õ + // result = R[n] AND operand2; // AND + result.result = Rn & operand2; + break; + + case 1: // when Ô0001Õ + // result = R[n] EOR operand2; // EOR + result.result = Rn ^ operand2; + break; + + case 2: // when Ô0010Õ + // (result, -, -) = AddWithCarry(R[n], NOT(operand2), Ô1Õ); // SUB + result = AddWithCarry (Rn, ~(operand2), 1); + break; + + case 3: // when Ô0011Õ + // (result, -, -) = AddWithCarry(NOT(R[n]), operand2, Ô1Õ); // RSB + result = AddWithCarry (~(Rn), operand2, 1); + break; + + case 4: // when Ô0100Õ + // (result, -, -) = AddWithCarry(R[n], operand2, Ô0Õ); // ADD + result = AddWithCarry (Rn, operand2, 0); + break; + + case 5: // when Ô0101Õ + // (result, -, -) = AddWithCarry(R[n], operand2, APSR.c); // ADC + result = AddWithCarry (Rn, operand2, APSR_C); + break; + + case 6: // when Ô0110Õ + // (result, -, -) = AddWithCarry(R[n], NOT(operand2), APSR.C); // SBC + result = AddWithCarry (Rn, ~(operand2), APSR_C); + break; + + case 7: // when Ô0111Õ + // (result, -, -) = AddWithCarry(NOT(R[n]), operand2, APSR.C); // RSC + result = AddWithCarry (~(Rn), operand2, APSR_C); + break; + + case 10: // when Ô1100Õ + // result = R[n] OR operand2; // ORR + result.result = Rn | operand2; + break; + + case 11: // when Ô1101Õ + // result = operand2; // MOV + result.result = operand2; + break; + + case 12: // when Ô1110Õ + // result = R[n] AND NOT(operand2); // BIC + result.result = Rn & ~(operand2); + break; + + case 15: // when Ô1111Õ + // result = NOT(operand2); // MVN + result.result = ~(operand2); + break; + + default: + return false; + } + // CPSRWriteByInstr(SPSR[], Ô1111Õ, TRUE); + + // For now, in emulation mode, we don't have access to the SPSR, so we will use the CPSR instead, and hope for + // the best. + uint32_t spsr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_cpsr, 0, &success); + if (!success) + return false; + + CPSRWriteByInstr (spsr, 15, true); + + // BranchWritePC(result); + EmulateInstruction::Context context; + context.type = eContextAdjustPC; + context.SetImmediate (result.result); + + BranchWritePC (context, result.result); + } + return true; +} + +EmulateInstructionARM::ARMOpcode* +EmulateInstructionARM::GetARMOpcodeForInstruction (const uint32_t opcode, uint32_t arm_isa) +{ + static ARMOpcode + g_arm_opcodes[] = + { + //---------------------------------------------------------------------- + // Prologue instructions + //---------------------------------------------------------------------- + + // push register(s) + { 0x0fff0000, 0x092d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulatePUSH, "push <registers>" }, + { 0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePUSH, "push <register>" }, + + // set r7 to point to a stack offset + { 0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #<const>" }, + { 0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBR7IPImm, "sub r7, ip, #<const>"}, + // copy the stack pointer to ip + { 0x0fffffff, 0x01a0c00d, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdSP, "mov ip, sp" }, + { 0x0ffff000, 0x028dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add ip, sp, #<const>" }, + { 0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBIPSPImm, "sub ip, sp, #<const>"}, + + // adjust the stack pointer + { 0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #<const>"}, + { 0x0fef0010, 0x004d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPReg, "sub{s}<c> <Rd>, sp, <Rm>{,<shift>}" }, + + // push one register + // if Rn == '1101' && imm12 == '000000000100' then SEE PUSH; + { 0x0e5f0000, 0x040d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRRtSP, "str Rt, [sp, #-imm12]!" }, + + // vector push consecutive extension register(s) + { 0x0fbf0f00, 0x0d2d0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"}, + { 0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"}, + + //---------------------------------------------------------------------- + // Epilogue instructions + //---------------------------------------------------------------------- + + { 0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop <registers>"}, + { 0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop <register>"}, + { 0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"}, + { 0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"}, + + //---------------------------------------------------------------------- + // Supervisor Call (previously Software Interrupt) + //---------------------------------------------------------------------- + { 0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSVC, "svc #imm24"}, + + //---------------------------------------------------------------------- + // Branch instructions + //---------------------------------------------------------------------- + { 0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateB, "b #imm24"}, + // To resolve ambiguity, "blx <label>" should come before "bl <label>". + { 0xfe000000, 0xfa000000, ARMV5_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"}, + { 0x0f000000, 0x0b000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"}, + { 0x0ffffff0, 0x012fff30, ARMV5_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"}, + // for example, "bx lr" + { 0x0ffffff0, 0x012fff10, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"}, + // bxj + { 0x0ffffff0, 0x012fff20, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBXJRm, "bxj <Rm>"}, + + //---------------------------------------------------------------------- + // Data-processing instructions + //---------------------------------------------------------------------- + // adc (immediate) + { 0x0fe00000, 0x02a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADCImm, "adc{s}<c> <Rd>, <Rn>, #const"}, + // adc (register) + { 0x0fe00010, 0x00a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADCReg, "adc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // add (immediate) + { 0x0fe00000, 0x02800000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDImmARM, "add{s}<c> <Rd>, <Rn>, #const"}, + // add (register) + { 0x0fe00010, 0x00800000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDReg, "add{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // add (register-shifted register) + { 0x0fe00090, 0x00800010, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRegShift, "add{s}<c> <Rd>, <Rn>, <Rm>, <type> <RS>"}, + // adr + { 0x0fff0000, 0x028f0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"}, + { 0x0fff0000, 0x024f0000, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateADR, "sub<c> <Rd>, PC, #<const>"}, + // and (immediate) + { 0x0fe00000, 0x02000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateANDImm, "and{s}<c> <Rd>, <Rn>, #const"}, + // and (register) + { 0x0fe00010, 0x00000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateANDReg, "and{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // bic (immediate) + { 0x0fe00000, 0x03c00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBICImm, "bic{s}<c> <Rd>, <Rn>, #const"}, + // bic (register) + { 0x0fe00010, 0x01c00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBICReg, "bic{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // eor (immediate) + { 0x0fe00000, 0x02200000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateEORImm, "eor{s}<c> <Rd>, <Rn>, #const"}, + // eor (register) + { 0x0fe00010, 0x00200000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateEORReg, "eor{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // orr (immediate) + { 0x0fe00000, 0x03800000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateORRImm, "orr{s}<c> <Rd>, <Rn>, #const"}, + // orr (register) + { 0x0fe00010, 0x01800000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateORRReg, "orr{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // rsb (immediate) + { 0x0fe00000, 0x02600000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRSBImm, "rsb{s}<c> <Rd>, <Rn>, #<const>"}, + // rsb (register) + { 0x0fe00010, 0x00600000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRSBReg, "rsb{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // rsc (immediate) + { 0x0fe00000, 0x02e00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRSCImm, "rsc{s}<c> <Rd>, <Rn>, #<const>"}, + // rsc (register) + { 0x0fe00010, 0x00e00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRSCReg, "rsc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // sbc (immediate) + { 0x0fe00000, 0x02c00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSBCImm, "sbc{s}<c> <Rd>, <Rn>, #<const>"}, + // sbc (register) + { 0x0fe00010, 0x00c00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSBCReg, "sbc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // sub (immediate, ARM) + { 0x0fe00000, 0x02400000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBImmARM, "sub{s}<c> <Rd>, <Rn>, #<const>"}, + // sub (sp minus immediate) + { 0x0fef0000, 0x024d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "sub{s}<c> <Rd>, sp, #<const>"}, + // sub (register) + { 0x0fe00010, 0x00400000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBReg, "sub{s}<c> <Rd>, <Rn>, <Rm>{,<shift>}"}, + // teq (immediate) + { 0x0ff0f000, 0x03300000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTEQImm, "teq<c> <Rn>, #const"}, + // teq (register) + { 0x0ff0f010, 0x01300000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTEQReg, "teq<c> <Rn>, <Rm> {,<shift>}"}, + // tst (immediate) + { 0x0ff0f000, 0x03100000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTSTImm, "tst<c> <Rn>, #const"}, + // tst (register) + { 0x0ff0f010, 0x01100000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTSTReg, "tst<c> <Rn>, <Rm> {,<shift>}"}, + + // mov (immediate) + { 0x0fef0000, 0x03a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdImm, "mov{s}<c> <Rd>, #<const>"}, + { 0x0ff00000, 0x03000000, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdImm, "movw<c> <Rd>, #<imm16>" }, + // mov (register) + { 0x0fef0ff0, 0x01a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdRm, "mov{s}<c> <Rd>, <Rm>"}, + // mvn (immediate) + { 0x0fef0000, 0x03e00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMVNImm, "mvn{s}<c> <Rd>, #<const>"}, + // mvn (register) + { 0x0fef0010, 0x01e00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMVNReg, "mvn{s}<c> <Rd>, <Rm> {,<shift>}"}, + // cmn (immediate) + { 0x0ff0f000, 0x03700000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateCMNImm, "cmn<c> <Rn>, #<const>"}, + // cmn (register) + { 0x0ff0f010, 0x01700000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm> {,<shift>}"}, + // cmp (immediate) + { 0x0ff0f000, 0x03500000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateCMPImm, "cmp<c> <Rn>, #<const>"}, + // cmp (register) + { 0x0ff0f010, 0x01500000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm> {,<shift>}"}, + // asr (immediate) + { 0x0fef0070, 0x01a00040, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateASRImm, "asr{s}<c> <Rd>, <Rm>, #imm"}, + // asr (register) + { 0x0fef00f0, 0x01a00050, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateASRReg, "asr{s}<c> <Rd>, <Rn>, <Rm>"}, + // lsl (immediate) + { 0x0fef0070, 0x01a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLSLImm, "lsl{s}<c> <Rd>, <Rm>, #imm"}, + // lsl (register) + { 0x0fef00f0, 0x01a00010, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLSLReg, "lsl{s}<c> <Rd>, <Rn>, <Rm>"}, + // lsr (immediate) + { 0x0fef0070, 0x01a00020, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLSRImm, "lsr{s}<c> <Rd>, <Rm>, #imm"}, + // lsr (register) + { 0x0fef00f0, 0x01a00050, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLSRReg, "lsr{s}<c> <Rd>, <Rn>, <Rm>"}, + // rrx is a special case encoding of ror (immediate) + { 0x0fef0ff0, 0x01a00060, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRRX, "rrx{s}<c> <Rd>, <Rm>"}, + // ror (immediate) + { 0x0fef0070, 0x01a00060, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRORImm, "ror{s}<c> <Rd>, <Rm>, #imm"}, + // ror (register) + { 0x0fef00f0, 0x01a00070, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRORReg, "ror{s}<c> <Rd>, <Rn>, <Rm>"}, + // mul + { 0x0fe000f0, 0x00000090, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMUL, "mul{s}<c> <Rd>,<R>,<Rm>" }, + + // subs pc, lr and related instructions + { 0x0e10f000, 0x0210f000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPcLrEtc, "<opc>S<c> PC,#<const> | <Rn>,#<const>" }, + { 0x0e10f010, 0x0010f000, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPcLrEtc, "<opc>S<c> PC,<Rn>,<Rm{,<shift>}" }, + + //---------------------------------------------------------------------- + // Load instructions + //---------------------------------------------------------------------- + { 0x0fd00000, 0x08900000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>" }, + { 0x0fd00000, 0x08100000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDMDA, "ldmda<c> <Rn>{!} <registers>" }, + { 0x0fd00000, 0x09100000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>" }, + { 0x0fd00000, 0x09900000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDMIB, "ldmib<c> <Rn<{!} <registers>" }, + { 0x0e500000, 0x04100000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRImmediateARM, "ldr<c> <Rt> [<Rn> {#+/-<imm12>}]" }, + { 0x0e500010, 0x06100000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRRegister, "ldr<c> <Rt> [<Rn> +/-<Rm> {<shift>}] {!}" }, + { 0x0e5f0000, 0x045f0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRBLiteral, "ldrb<c> <Rt>, [...]"}, + { 0xfe500010, 0x06500000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRBRegister, "ldrb<c> <Rt>, [<Rn>,+/-<Rm>{, <shift>}]{!}" }, + { 0x0e5f00f0, 0x005f00b0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRHLiteral, "ldrh<c> <Rt>, <label>" }, + { 0x0e5000f0, 0x001000b0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRHRegister, "ldrh<c> <Rt>,[<Rn>,+/-<Rm>]{!}" }, + { 0x0e5000f0, 0x005000d0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSBImmediate, "ldrsb<c> <Rt>, [<Rn>{,#+/-<imm8>}]" }, + { 0x0e5f00f0, 0x005f00d0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSBLiteral, "ldrsb<c> <Rt> <label>" }, + { 0x0e5000f0, 0x001000d0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSBRegister, "ldrsb<c> <Rt>,[<Rn>,+/-<Rm>]{!}" }, + { 0x0e5000f0, 0x005000f0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSHImmediate, "ldrsh<c> <Rt>,[<Rn>{,#+/-<imm8>}]"}, + { 0x0e5f00f0, 0x005f00f0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSHLiteral, "ldrsh<c> <Rt>,<label>" }, + { 0x0e5000f0, 0x001000f0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSHRegister, "ldrsh<c> <Rt>,[<Rn>,+/-<Rm>]{!}" }, + { 0x0e5000f0, 0x004000d0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRDImmediate, "ldrd<c> <Rt>, <Rt2>, [<Rn>,#+/-<imm8>]!"}, + { 0x0e500ff0, 0x000000d0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRDRegister, "ldrd<c> <Rt>, <Rt2>, [<Rn>, +/-<Rm>]{!}"}, + { 0x0e100f00, 0x0c100b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32, &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"}, + { 0x0e100f00, 0x0c100a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32, &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"}, + { 0x0f300f00, 0x0d100b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32, &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Dd>, [<Rn>{,#+/-<imm>}]"}, + { 0x0f300f00, 0x0d100a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32, &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Sd>, [<Rn>{,#+/-<imm>}]"}, + { 0xffb00000, 0xf4200000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVLD1Multiple, "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + { 0xffb00300, 0xf4a00000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVLD1Single, "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + { 0xffb00f00, 0xf4a00c00, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVLD1SingleAll, "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + + //---------------------------------------------------------------------- + // Store instructions + //---------------------------------------------------------------------- + { 0x0fd00000, 0x08800000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>" }, + { 0x0fd00000, 0x08000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTMDA, "stmda<c> <Rn>{!} <registers>" }, + { 0x0fd00000, 0x09000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTMDB, "stmdb<c> <Rn>{!} <registers>" }, + { 0x0fd00000, 0x09800000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTMIB, "stmib<c> <Rn>{!} <registers>" }, + { 0x0e500010, 0x06000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRRegister, "str<c> <Rt> [<Rn> +/-<Rm> {<shift>}]{!}" }, + { 0x0e5000f0, 0x000000b0, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRHRegister, "strh<c> <Rt>,[<Rn>,+/-<Rm>[{!}" }, + { 0x0ff00ff0, 0x01800f90, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTREX, "strex<c> <Rd>, <Rt>, [<Rn>]"}, + { 0x0e500000, 0x04400000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRBImmARM, "strb<c> <Rt>,[<Rn>,#+/-<imm12>]!"}, + { 0x0e500000, 0x04000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRImmARM, "str<c> <Rt>,[<Rn>,#+/-<imm12>]!"}, + { 0x0e5000f0, 0x004000f0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRDImm, "strd<c> <Rt>, <Rt2>, [<Rn> #+/-<imm8>]!"}, + { 0x0e500ff0, 0x000000f0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRDReg, "strd<c> <Rt>, <Rt2>, [<Rn>, +/-<Rm>]{!}"}, + { 0x0e100f00, 0x0c000b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32, &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!} <list>"}, + { 0x0e100f00, 0x0c000a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32, &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!} <list>"}, + { 0x0f300f00, 0x0d000b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32, &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Dd> [<Rn>{,#+/-<imm>}]"}, + { 0x0f300f00, 0x0d000a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32, &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Sd> [<Rn>{,#+/-<imm>}]"}, + { 0xffb00000, 0xf4000000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVST1Multiple, "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + { 0xffb00300, 0xf4800000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVST1Single, "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + + //---------------------------------------------------------------------- + // Other instructions + //---------------------------------------------------------------------- + { 0x0fff00f0, 0x06af00f0, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSXTB, "sxtb<c> <Rd>,<Rm>{,<rotation>}" }, + { 0x0fff00f0, 0x06bf0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSXTH, "sxth<c> <Rd>,<Rm>{,<rotation>}" }, + { 0x0fff00f0, 0x06ef0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateUXTB, "uxtb<c> <Rd>,<Rm>{,<rotation>}" }, + { 0x0fff00f0, 0x06ff0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateUXTH, "uxth<c> <Rd>,<Rm>{,<rotation>}" }, + { 0xfe500000, 0xf8100000, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRFE, "rfe{<amode>} <Rn>{!}" } + + }; + static const size_t k_num_arm_opcodes = sizeof(g_arm_opcodes)/sizeof(ARMOpcode); + + for (size_t i=0; i<k_num_arm_opcodes; ++i) + { + if ((g_arm_opcodes[i].mask & opcode) == g_arm_opcodes[i].value && + (g_arm_opcodes[i].variants & arm_isa) != 0) + return &g_arm_opcodes[i]; + } + return NULL; +} + + +EmulateInstructionARM::ARMOpcode* +EmulateInstructionARM::GetThumbOpcodeForInstruction (const uint32_t opcode, uint32_t arm_isa) +{ + + static ARMOpcode + g_thumb_opcodes[] = + { + //---------------------------------------------------------------------- + // Prologue instructions + //---------------------------------------------------------------------- + + // push register(s) + { 0xfffffe00, 0x0000b400, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulatePUSH, "push <registers>" }, + { 0xffff0000, 0xe92d0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePUSH, "push.w <registers>" }, + { 0xffff0fff, 0xf84d0d04, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulatePUSH, "push.w <register>" }, + + // set r7 to point to a stack offset + { 0xffffff00, 0x0000af00, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #imm" }, + // copy the stack pointer to r7 + { 0xffffffff, 0x0000466f, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateMOVRdSP, "mov r7, sp" }, + // move from high register to low register (comes after "mov r7, sp" to resolve ambiguity) + { 0xffffffc0, 0x00004640, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateMOVLowHigh, "mov r0-r7, r8-r15" }, + + // PC-relative load into register (see also EmulateADDSPRm) + { 0xfffff800, 0x00004800, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRRtPCRelative, "ldr <Rt>, [PC, #imm]"}, + + // adjust the stack pointer + { 0xffffff87, 0x00004485, ARMvAll, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateADDSPRm, "add sp, <Rm>"}, + { 0xffffff80, 0x0000b080, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #imm"}, + { 0xfbef8f00, 0xf1ad0d00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "sub.w sp, sp, #<const>"}, + { 0xfbff8f00, 0xf2ad0d00, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "subw sp, sp, #imm12"}, + { 0xffef8000, 0xebad0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPReg, "sub{s}<c> <Rd>, sp, <Rm>{,<shift>}" }, + + // vector push consecutive extension register(s) + { 0xffbf0f00, 0xed2d0b00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"}, + { 0xffbf0f00, 0xed2d0a00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"}, + + //---------------------------------------------------------------------- + // Epilogue instructions + //---------------------------------------------------------------------- + + { 0xfffff800, 0x0000a800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateADDSPImm, "add<c> <Rd>, sp, #imm"}, + { 0xffffff80, 0x0000b000, ARMvAll, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateADDSPImm, "add sp, #imm"}, + { 0xfffffe00, 0x0000bc00, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulatePOP, "pop <registers>"}, + { 0xffff0000, 0xe8bd0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop.w <registers>" }, + { 0xffff0fff, 0xf85d0d04, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop.w <register>" }, + { 0xffbf0f00, 0xecbd0b00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"}, + { 0xffbf0f00, 0xecbd0a00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"}, + + //---------------------------------------------------------------------- + // Supervisor Call (previously Software Interrupt) + //---------------------------------------------------------------------- + { 0xffffff00, 0x0000df00, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSVC, "svc #imm8"}, + + //---------------------------------------------------------------------- + // If Then makes up to four following instructions conditional. + //---------------------------------------------------------------------- + // The next 5 opcode _must_ come before the if then instruction + { 0xffffffff, 0x0000bf00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateNop, "nop"}, + { 0xffffffff, 0x0000bf10, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateNop, "nop YIELD (yield hint)"}, + { 0xffffffff, 0x0000bf20, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateNop, "nop WFE (wait for event hint)"}, + { 0xffffffff, 0x0000bf30, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateNop, "nop WFI (wait for interrupt hint)"}, + { 0xffffffff, 0x0000bf40, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateNop, "nop SEV (send event hint)"}, + { 0xffffff00, 0x0000bf00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateIT, "it{<x>{<y>{<z>}}} <firstcond>"}, + + //---------------------------------------------------------------------- + // Branch instructions + //---------------------------------------------------------------------- + // To resolve ambiguity, "b<c> #imm8" should come after "svc #imm8". + { 0xfffff000, 0x0000d000, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateB, "b<c> #imm8 (outside IT)"}, + { 0xfffff800, 0x0000e000, ARMvAll, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateB, "b<c> #imm11 (outside or last in IT)"}, + { 0xf800d000, 0xf0008000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateB, "b<c>.w #imm8 (outside IT)"}, + { 0xf800d000, 0xf0009000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, &EmulateInstructionARM::EmulateB, "b<c>.w #imm8 (outside or last in IT)"}, + // J1 == J2 == 1 + { 0xf800d000, 0xf000d000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"}, + // J1 == J2 == 1 + { 0xf800d001, 0xf000c000, ARMV5_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"}, + { 0xffffff87, 0x00004780, ARMV5_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"}, + // for example, "bx lr" + { 0xffffff87, 0x00004700, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"}, + // bxj + { 0xfff0ffff, 0xf3c08f00, ARMV5J_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBXJRm, "bxj <Rm>"}, + // compare and branch + { 0xfffff500, 0x0000b100, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateCB, "cb{n}z <Rn>, <label>"}, + // table branch byte + { 0xfff0fff0, 0xe8d0f000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTB, "tbb<c> <Rn>, <Rm>"}, + // table branch halfword + { 0xfff0fff0, 0xe8d0f010, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTB, "tbh<c> <Rn>, <Rm>, lsl #1"}, + + //---------------------------------------------------------------------- + // Data-processing instructions + //---------------------------------------------------------------------- + // adc (immediate) + { 0xfbe08000, 0xf1400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADCImm, "adc{s}<c> <Rd>, <Rn>, #<const>"}, + // adc (register) + { 0xffffffc0, 0x00004140, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateADCReg, "adcs|adc<c> <Rdn>, <Rm>"}, + { 0xffe08000, 0xeb400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateADCReg, "adc{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // add (register) + { 0xfffffe00, 0x00001800, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateADDReg, "adds|add<c> <Rd>, <Rn>, <Rm>"}, + // Make sure "add sp, <Rm>" comes before this instruction, so there's no ambiguity decoding the two. + { 0xffffff00, 0x00004400, ARMvAll, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateADDReg, "add<c> <Rdn>, <Rm>"}, + // adr + { 0xfffff800, 0x0000a000, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"}, + { 0xfbff8000, 0xf2af0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateADR, "sub<c> <Rd>, PC, #<const>"}, + { 0xfbff8000, 0xf20f0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"}, + // and (immediate) + { 0xfbe08000, 0xf0000000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateANDImm, "and{s}<c> <Rd>, <Rn>, #<const>"}, + // and (register) + { 0xffffffc0, 0x00004000, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateANDReg, "ands|and<c> <Rdn>, <Rm>"}, + { 0xffe08000, 0xea000000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateANDReg, "and{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // bic (immediate) + { 0xfbe08000, 0xf0200000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateBICImm, "bic{s}<c> <Rd>, <Rn>, #<const>"}, + // bic (register) + { 0xffffffc0, 0x00004380, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateBICReg, "bics|bic<c> <Rdn>, <Rm>"}, + { 0xffe08000, 0xea200000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateBICReg, "bic{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // eor (immediate) + { 0xfbe08000, 0xf0800000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateEORImm, "eor{s}<c> <Rd>, <Rn>, #<const>"}, + // eor (register) + { 0xffffffc0, 0x00004040, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateEORReg, "eors|eor<c> <Rdn>, <Rm>"}, + { 0xffe08000, 0xea800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateEORReg, "eor{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // orr (immediate) + { 0xfbe08000, 0xf0400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateORRImm, "orr{s}<c> <Rd>, <Rn>, #<const>"}, + // orr (register) + { 0xffffffc0, 0x00004300, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateORRReg, "orrs|orr<c> <Rdn>, <Rm>"}, + { 0xffe08000, 0xea400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateORRReg, "orr{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // rsb (immediate) + { 0xffffffc0, 0x00004240, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateRSBImm, "rsbs|rsb<c> <Rd>, <Rn>, #0"}, + { 0xfbe08000, 0xf1c00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateRSBImm, "rsb{s}<c>.w <Rd>, <Rn>, #<const>"}, + // rsb (register) + { 0xffe08000, 0xea400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRSBReg, "rsb{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // sbc (immediate) + { 0xfbe08000, 0xf1600000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSBCImm, "sbc{s}<c> <Rd>, <Rn>, #<const>"}, + // sbc (register) + { 0xffffffc0, 0x00004180, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSBCReg, "sbcs|sbc<c> <Rdn>, <Rm>"}, + { 0xffe08000, 0xeb600000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSBCReg, "sbc{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // add (immediate, Thumb) + { 0xfffffe00, 0x00001c00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateADDImmThumb, "adds|add<c> <Rd>,<Rn>,#<imm3>" }, + { 0xfffff800, 0x00003000, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateADDImmThumb, "adds|add<c> <Rdn>,#<imm8>" }, + { 0xfbe08000, 0xf1000000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDImmThumb, "add{s}<c>.w <Rd>,<Rn>,#<const>" }, + { 0xfbf08000, 0xf2000000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDImmThumb, "addw<c> <Rd>,<Rn>,#<imm12>" }, + // sub (immediate, Thumb) + { 0xfffffe00, 0x00001e00, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSUBImmThumb, "subs|sub<c> <Rd>, <Rn> #imm3"}, + { 0xfffff800, 0x00003800, ARMvAll, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateSUBImmThumb, "subs|sub<c> <Rdn>, #imm8"}, + { 0xfbe08000, 0xf1a00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBImmThumb, "sub{s}<c>.w <Rd>, <Rn>, #<const>"}, + { 0xfbf08000, 0xf2a00000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBImmThumb, "subw<c> <Rd>, <Rn>, #imm12"}, + // sub (sp minus immediate) + { 0xfbef8000, 0xf1ad0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "sub{s}.w <Rd>, sp, #<const>"}, + { 0xfbff8000, 0xf2ad0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "subw<c> <Rd>, sp, #imm12"}, + // sub (register) + { 0xfffffe00, 0x00001a00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSUBReg, "subs|sub<c> <Rd>, <Rn>, <Rm>"}, + { 0xffe08000, 0xeba00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBReg, "sub{s}<c>.w <Rd>, <Rn>, <Rm>{,<shift>}"}, + // teq (immediate) + { 0xfbf08f00, 0xf0900f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTEQImm, "teq<c> <Rn>, #<const>"}, + // teq (register) + { 0xfff08f00, 0xea900f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTEQReg, "teq<c> <Rn>, <Rm> {,<shift>}"}, + // tst (immediate) + { 0xfbf08f00, 0xf0100f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateTSTImm, "tst<c> <Rn>, #<const>"}, + // tst (register) + { 0xffffffc0, 0x00004200, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateTSTReg, "tst<c> <Rdn>, <Rm>"}, + { 0xfff08f00, 0xea100f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateTSTReg, "tst<c>.w <Rn>, <Rm> {,<shift>}"}, + + + // move from high register to high register + { 0xffffff00, 0x00004600, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateMOVRdRm, "mov<c> <Rd>, <Rm>"}, + // move from low register to low register + { 0xffffffc0, 0x00000000, ARMvAll, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateMOVRdRm, "movs <Rd>, <Rm>"}, + // mov{s}<c>.w <Rd>, <Rm> + { 0xffeff0f0, 0xea4f0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdRm, "mov{s}<c>.w <Rd>, <Rm>"}, + // move immediate + { 0xfffff800, 0x00002000, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateMOVRdImm, "movs|mov<c> <Rd>, #imm8"}, + { 0xfbef8000, 0xf04f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdImm, "mov{s}<c>.w <Rd>, #<const>"}, + { 0xfbf08000, 0xf2400000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdImm, "movw<c> <Rd>,#<imm16>"}, + // mvn (immediate) + { 0xfbef8000, 0xf06f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMVNImm, "mvn{s} <Rd>, #<const>"}, + // mvn (register) + { 0xffffffc0, 0x000043c0, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateMVNReg, "mvns|mvn<c> <Rd>, <Rm>"}, + { 0xffef8000, 0xea6f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateMVNReg, "mvn{s}<c>.w <Rd>, <Rm> {,<shift>}"}, + // cmn (immediate) + { 0xfbf08f00, 0xf1100f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateCMNImm, "cmn<c> <Rn>, #<const>"}, + // cmn (register) + { 0xffffffc0, 0x000042c0, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm>"}, + { 0xfff08f00, 0xeb100f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm> {,<shift>}"}, + // cmp (immediate) + { 0xfffff800, 0x00002800, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateCMPImm, "cmp<c> <Rn>, #imm8"}, + { 0xfbf08f00, 0xf1b00f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateCMPImm, "cmp<c>.w <Rn>, #<const>"}, + // cmp (register) (Rn and Rm both from r0-r7) + { 0xffffffc0, 0x00004280, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm>"}, + // cmp (register) (Rn and Rm not both from r0-r7) + { 0xffffff00, 0x00004500, ARMvAll, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm>"}, + // asr (immediate) + { 0xfffff800, 0x00001000, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateASRImm, "asrs|asr<c> <Rd>, <Rm>, #imm"}, + { 0xffef8030, 0xea4f0020, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateASRImm, "asr{s}<c>.w <Rd>, <Rm>, #imm"}, + // asr (register) + { 0xffffffc0, 0x00004100, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateASRReg, "asrs|asr<c> <Rdn>, <Rm>"}, + { 0xffe0f0f0, 0xfa40f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateASRReg, "asr{s}<c>.w <Rd>, <Rn>, <Rm>"}, + // lsl (immediate) + { 0xfffff800, 0x00000000, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLSLImm, "lsls|lsl<c> <Rd>, <Rm>, #imm"}, + { 0xffef8030, 0xea4f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLSLImm, "lsl{s}<c>.w <Rd>, <Rm>, #imm"}, + // lsl (register) + { 0xffffffc0, 0x00004080, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLSLReg, "lsls|lsl<c> <Rdn>, <Rm>"}, + { 0xffe0f0f0, 0xfa00f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLSLReg, "lsl{s}<c>.w <Rd>, <Rn>, <Rm>"}, + // lsr (immediate) + { 0xfffff800, 0x00000800, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLSRImm, "lsrs|lsr<c> <Rd>, <Rm>, #imm"}, + { 0xffef8030, 0xea4f0010, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLSRImm, "lsr{s}<c>.w <Rd>, <Rm>, #imm"}, + // lsr (register) + { 0xffffffc0, 0x000040c0, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLSRReg, "lsrs|lsr<c> <Rdn>, <Rm>"}, + { 0xffe0f0f0, 0xfa20f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLSRReg, "lsr{s}<c>.w <Rd>, <Rn>, <Rm>"}, + // rrx is a special case encoding of ror (immediate) + { 0xffeff0f0, 0xea4f0030, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRRX, "rrx{s}<c>.w <Rd>, <Rm>"}, + // ror (immediate) + { 0xffef8030, 0xea4f0030, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRORImm, "ror{s}<c>.w <Rd>, <Rm>, #imm"}, + // ror (register) + { 0xffffffc0, 0x000041c0, ARMvAll, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateRORReg, "rors|ror<c> <Rdn>, <Rm>"}, + { 0xffe0f0f0, 0xfa60f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateRORReg, "ror{s}<c>.w <Rd>, <Rn>, <Rm>"}, + // mul + { 0xffffffc0, 0x00004340, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateMUL, "muls <Rdm>,<Rn>,<Rdm>" }, + // mul + { 0xfff0f0f0, 0xfb00f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateMUL, "mul<c> <Rd>,<Rn>,<Rm>" }, + + // subs pc, lr and related instructions + { 0xffffff00, 0xf3de8f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPcLrEtc, "SUBS<c> PC, LR, #<imm8>" }, + + //---------------------------------------------------------------------- + // RFE instructions *** IMPORTANT *** THESE MUST BE LISTED **BEFORE** THE LDM.. Instructions in this table; + // otherwise the wrong instructions will be selected. + //---------------------------------------------------------------------- + + { 0xffd0ffff, 0xe810c000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateRFE, "rfedb<c> <Rn>{!}" }, + { 0xffd0ffff, 0xe990c000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateRFE, "rfe{ia}<c> <Rn>{!}" }, + + //---------------------------------------------------------------------- + // Load instructions + //---------------------------------------------------------------------- + { 0xfffff800, 0x0000c800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>" }, + { 0xffd02000, 0xe8900000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDM, "ldm<c>.w <Rn>{!} <registers>" }, + { 0xffd00000, 0xe9100000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>" }, + { 0xfffff800, 0x00006800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [<Rn>{,#imm}]"}, + { 0xfffff800, 0x00009800, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [SP{,#imm}]"}, + { 0xfff00000, 0xf8d00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c>.w <Rt>, [<Rn>{,#imm12}]"}, + { 0xfff00800, 0xf8500800, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [<Rn>{,#+/-<imm8>}]{!}"}, + // Thumb2 PC-relative load into register + { 0xff7f0000, 0xf85f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRRtPCRelative, "ldr<c>.w <Rt>, [PC, +/-#imm}]"}, + { 0xfffffe00, 0x00005800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRRegister, "ldr<c> <Rt>, [<Rn>, <Rm>]" }, + { 0xfff00fc0, 0xf8500000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRRegister, "ldr<c>.w <Rt>, [<Rn>,<Rm>{,LSL #<imm2>}]" }, + { 0xfffff800, 0x00007800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRBImmediate, "ldrb<c> <Rt>,[<Rn>{,#<imm5>}]" }, + { 0xfff00000, 0xf8900000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRBImmediate, "ldrb<c>.w <Rt>,[<Rn>{,#<imm12>}]" }, + { 0xfff00800, 0xf8100800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRBImmediate, "ldrb<c> <Rt>,[<Rn>, #+/-<imm8>]{!}" }, + { 0xff7f0000, 0xf81f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRBLiteral, "ldrb<c> <Rt>,[...]" }, + { 0xfffffe00, 0x00005c00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRBRegister, "ldrb<c> <Rt>,[<Rn>,<Rm>]" }, + { 0xfff00fc0, 0xf8100000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRBRegister, "ldrb<c>.w <Rt>,[<Rn>,<Rm>{,LSL #imm2>}]" }, + { 0xfffff800, 0x00008800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRHImmediate, "ldrh<c> <Rt>, [<Rn>{,#<imm>}]" }, + { 0xfff00000, 0xf8b00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRHImmediate, "ldrh<c>.w <Rt>,[<Rn>{,#<imm12>}]" }, + { 0xfff00800, 0xf8300800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRHImmediate, "ldrh<c> <Rt>,[<Rn>,#+/-<imm8>]{!}" }, + { 0xff7f0000, 0xf83f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRHLiteral, "ldrh<c> <Rt>, <label>" }, + { 0xfffffe00, 0x00005a00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRHRegister, "ldrh<c> <Rt>, [<Rn>,<Rm>]" }, + { 0xfff00fc0, 0xf8300000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRHRegister, "ldrh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]" }, + { 0xfff00000, 0xf9900000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSBImmediate, "ldrsb<c> <Rt>,[<Rn>,#<imm12>]" }, + { 0xfff00800, 0xf9100800, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSBImmediate, "ldrsb<c> <Rt>,[<Rn>,#+/-<imm8>]" }, + { 0xff7f0000, 0xf91f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSBLiteral, "ldrsb<c> <Rt>, <label>" }, + { 0xfffffe00, 0x00005600, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRSBRegister, "ldrsb<c> <Rt>,[<Rn>,<Rm>]" }, + { 0xfff00fc0, 0xf9100000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSBRegister, "ldrsb<c>.w <Rt>,[<Rn>,<Rm>{,LSL #imm2>}]" }, + { 0xfff00000, 0xf9b00000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSHImmediate, "ldrsh<c> <Rt>,[<Rn>,#<imm12>]" }, + { 0xfff00800, 0xf9300800, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSHImmediate, "ldrsh<c> <Rt>,[<Rn>,#+/-<imm8>]" }, + { 0xff7f0000, 0xf93f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSHLiteral, "ldrsh<c> <Rt>,<label>" }, + { 0xfffffe00, 0x00005e00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateLDRSHRegister, "ldrsh<c> <Rt>,[<Rn>,<Rm>]" }, + { 0xfff00fc0, 0xf9300000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRSHRegister, "ldrsh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]" }, + { 0xfe500000, 0xe8500000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateLDRDImmediate, "ldrd<c> <Rt>, <Rt2>, [<Rn>,#+/-<imm>]!"}, + { 0xfe100f00, 0xec100b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32, &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"}, + { 0xfe100f00, 0xec100a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32, &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>" }, + { 0xffe00f00, 0xed100b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32, &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Dd>, [<Rn>{,#+/-<imm>}]"}, + { 0xff300f00, 0xed100a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32, &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Sd>, {<Rn>{,#+/-<imm>}]"}, + { 0xffb00000, 0xf9200000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVLD1Multiple, "vld1<c>.<size> <list>, [<Rn>{@<align>}],<Rm>"}, + { 0xffb00300, 0xf9a00000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVLD1Single, "vld1<c>.<size> <list>, [<Rn>{@<align>}],<Rm>"}, + { 0xffb00f00, 0xf9a00c00, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVLD1SingleAll, "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + + //---------------------------------------------------------------------- + // Store instructions + //---------------------------------------------------------------------- + { 0xfffff800, 0x0000c000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>" }, + { 0xffd00000, 0xe8800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTM, "stm<c>.w <Rn>{!} <registers>" }, + { 0xffd00000, 0xe9000000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTMDB, "stmdb<c> <Rn>{!} <registers>" }, + { 0xfffff800, 0x00006000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [<Rn>{,#<imm>}]" }, + { 0xfffff800, 0x00009000, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16, &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [SP,#<imm>]" }, + { 0xfff00000, 0xf8c00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRThumb, "str<c>.w <Rt>, [<Rn>,#<imm12>]" }, + { 0xfff00800, 0xf8400800, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [<Rn>,#+/-<imm8>]" }, + { 0xfffffe00, 0x00005000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSTRRegister, "str<c> <Rt> ,{<Rn>, <Rm>]" }, + { 0xfff00fc0, 0xf8400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRRegister, "str<c>.w <Rt>, [<Rn>, <Rm> {lsl #imm2>}]" }, + { 0xfffff800, 0x00007000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSTRBThumb, "strb<c> <Rt>, [<Rn>, #<imm5>]" }, + { 0xfff00000, 0xf8800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRBThumb, "strb<c>.w <Rt>, [<Rn>, #<imm12>]" }, + { 0xfff00800, 0xf8000800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRBThumb, "strb<c> <Rt> ,[<Rn>, #+/-<imm8>]{!}" }, + { 0xfffffe00, 0x00005200, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSTRHRegister, "strh<c> <Rt>,[<Rn>,<Rm>]" }, + { 0xfff00fc0, 0xf8200000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRHRegister, "strh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]" }, + { 0xfff00000, 0xe8400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTREX, "strex<c> <Rd>, <Rt>, [<Rn{,#<imm>}]" }, + { 0xfe500000, 0xe8400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRDImm, "strd<c> <Rt>, <Rt2>, [<Rn>, #+/-<imm>]!"}, + { 0xfe100f00, 0xec000b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32, &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!}, <list>"}, + { 0xfea00f00, 0xec000a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32, &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!}, <list>"}, + { 0xff300f00, 0xed000b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32, &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Dd>, [<Rn>{,#+/-<imm>}]"}, + { 0xff300f00, 0xed000a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32, &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Sd>, [<Rn>{,#+/-<imm>}]"}, + { 0xffb00000, 0xf9000000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVST1Multiple, "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + { 0xffb00300, 0xf9800000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, &EmulateInstructionARM::EmulateVST1Single, "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + + //---------------------------------------------------------------------- + // Other instructions + //---------------------------------------------------------------------- + { 0xffffffc0, 0x0000b240, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSXTB, "sxtb<c> <Rd>,<Rm>" }, + { 0xfffff080, 0xfa4ff080, ARMV6_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSXTB, "sxtb<c>.w <Rd>,<Rm>{,<rotation>}" }, + { 0xffffffc0, 0x0000b200, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateSXTH, "sxth<c> <Rd>,<Rm>" }, + { 0xfffff080, 0xfa0ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateSXTH, "sxth<c>.w <Rd>,<Rm>{,<rotation>}" }, + { 0xffffffc0, 0x0000b2c0, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateUXTB, "uxtb<c> <Rd>,<Rm>" }, + { 0xfffff080, 0xfa5ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateUXTB, "uxtb<c>.w <Rd>,<Rm>{,<rotation>}" }, + { 0xffffffc0, 0x0000b280, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16, &EmulateInstructionARM::EmulateUXTH, "uxth<c> <Rd>,<Rm>" }, + { 0xfffff080, 0xfa1ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, &EmulateInstructionARM::EmulateUXTH, "uxth<c>.w <Rd>,<Rm>{,<rotation>}" }, + }; + + const size_t k_num_thumb_opcodes = sizeof(g_thumb_opcodes)/sizeof(ARMOpcode); + for (size_t i=0; i<k_num_thumb_opcodes; ++i) + { + if ((g_thumb_opcodes[i].mask & opcode) == g_thumb_opcodes[i].value && + (g_thumb_opcodes[i].variants & arm_isa) != 0) + return &g_thumb_opcodes[i]; + } + return NULL; +} + +bool +EmulateInstructionARM::SetArchitecture (const ArchSpec &arch) +{ + m_arch = arch; + m_arm_isa = 0; + const char *arch_cstr = arch.GetArchitectureName (); + if (arch_cstr) + { + if (0 == ::strcasecmp(arch_cstr, "armv4t")) m_arm_isa = ARMv4T; + else if (0 == ::strcasecmp(arch_cstr, "armv5tej")) m_arm_isa = ARMv5TEJ; + else if (0 == ::strcasecmp(arch_cstr, "armv5te")) m_arm_isa = ARMv5TE; + else if (0 == ::strcasecmp(arch_cstr, "armv5t")) m_arm_isa = ARMv5T; + else if (0 == ::strcasecmp(arch_cstr, "armv6k")) m_arm_isa = ARMv6K; + else if (0 == ::strcasecmp(arch_cstr, "armv6t2")) m_arm_isa = ARMv6T2; + else if (0 == ::strcasecmp(arch_cstr, "armv7s")) m_arm_isa = ARMv7S; + else if (0 == ::strcasecmp(arch_cstr, "arm")) m_arm_isa = ARMvAll; + else if (0 == ::strcasecmp(arch_cstr, "thumb")) m_arm_isa = ARMvAll; + else if (0 == ::strncasecmp(arch_cstr,"armv4", 5)) m_arm_isa = ARMv4; + else if (0 == ::strncasecmp(arch_cstr,"armv6", 5)) m_arm_isa = ARMv6; + else if (0 == ::strncasecmp(arch_cstr,"armv7", 5)) m_arm_isa = ARMv7; + else if (0 == ::strncasecmp(arch_cstr,"armv8", 5)) m_arm_isa = ARMv8; + } + return m_arm_isa != 0; +} + +bool +EmulateInstructionARM::SetInstruction (const Opcode &insn_opcode, const Address &inst_addr, Target *target) +{ + if (EmulateInstruction::SetInstruction (insn_opcode, inst_addr, target)) + { + if (m_arch.GetTriple().getArch() == llvm::Triple::thumb) + m_opcode_mode = eModeThumb; + else + { + AddressClass addr_class = inst_addr.GetAddressClass(); + + if ((addr_class == eAddressClassCode) || (addr_class == eAddressClassUnknown)) + m_opcode_mode = eModeARM; + else if (addr_class == eAddressClassCodeAlternateISA) + m_opcode_mode = eModeThumb; + else + return false; + } + if (m_opcode_mode == eModeThumb) + m_opcode_cpsr = CPSR_MODE_USR | MASK_CPSR_T; + else + m_opcode_cpsr = CPSR_MODE_USR; + return true; + } + return false; +} + +bool +EmulateInstructionARM::ReadInstruction () +{ + bool success = false; + m_opcode_cpsr = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, 0, &success); + if (success) + { + addr_t pc = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_ADDRESS, &success); + if (success) + { + Context read_inst_context; + read_inst_context.type = eContextReadOpcode; + read_inst_context.SetNoArgs (); + + if (m_opcode_cpsr & MASK_CPSR_T) + { + m_opcode_mode = eModeThumb; + uint32_t thumb_opcode = MemARead(read_inst_context, pc, 2, 0, &success); + + if (success) + { + if ((thumb_opcode & 0xe000) != 0xe000 || ((thumb_opcode & 0x1800u) == 0)) + { + m_opcode.SetOpcode16 (thumb_opcode); + } + else + { + m_opcode.SetOpcode32 ((thumb_opcode << 16) | MemARead(read_inst_context, pc + 2, 2, 0, &success)); + } + } + } + else + { + m_opcode_mode = eModeARM; + m_opcode.SetOpcode32 (MemARead(read_inst_context, pc, 4, 0, &success)); + } + } + } + if (!success) + { + m_opcode_mode = eModeInvalid; + m_addr = LLDB_INVALID_ADDRESS; + } + return success; +} + +uint32_t +EmulateInstructionARM::ArchVersion () +{ + return m_arm_isa; +} + +bool +EmulateInstructionARM::ConditionPassed (const uint32_t opcode, bool *is_conditional) +{ + // If we are ignoring conditions, then always return true. + // this allows us to iterate over disassembly code and still + // emulate an instruction even if we don't have all the right + // bits set in the CPSR register... + if (m_ignore_conditions) + return true; + + if (is_conditional) + *is_conditional = true; + + const uint32_t cond = CurrentCond (opcode); + + if (cond == UINT32_MAX) + return false; + + bool result = false; + switch (UnsignedBits(cond, 3, 1)) + { + case 0: + if (m_opcode_cpsr == 0) + result = true; + else + result = (m_opcode_cpsr & MASK_CPSR_Z) != 0; + break; + case 1: + if (m_opcode_cpsr == 0) + result = true; + else + result = (m_opcode_cpsr & MASK_CPSR_C) != 0; + break; + case 2: + if (m_opcode_cpsr == 0) + result = true; + else + result = (m_opcode_cpsr & MASK_CPSR_N) != 0; + break; + case 3: + if (m_opcode_cpsr == 0) + result = true; + else + result = (m_opcode_cpsr & MASK_CPSR_V) != 0; + break; + case 4: + if (m_opcode_cpsr == 0) + result = true; + else + result = ((m_opcode_cpsr & MASK_CPSR_C) != 0) && ((m_opcode_cpsr & MASK_CPSR_Z) == 0); + break; + case 5: + if (m_opcode_cpsr == 0) + result = true; + else + { + bool n = (m_opcode_cpsr & MASK_CPSR_N); + bool v = (m_opcode_cpsr & MASK_CPSR_V); + result = n == v; + } + break; + case 6: + if (m_opcode_cpsr == 0) + result = true; + else + { + bool n = (m_opcode_cpsr & MASK_CPSR_N); + bool v = (m_opcode_cpsr & MASK_CPSR_V); + result = n == v && ((m_opcode_cpsr & MASK_CPSR_Z) == 0); + } + break; + case 7: + // Always execute (cond == 0b1110, or the special 0b1111 which gives + // opcodes different meanings, but always means execution happpens. + if (is_conditional) + *is_conditional = false; + result = true; + break; + } + + if (cond & 1) + result = !result; + return result; +} + +uint32_t +EmulateInstructionARM::CurrentCond (const uint32_t opcode) +{ + switch (m_opcode_mode) + { + case eModeInvalid: + break; + + case eModeARM: + return UnsignedBits(opcode, 31, 28); + + case eModeThumb: + // For T1 and T3 encodings of the Branch instruction, it returns the 4-bit + // 'cond' field of the encoding. + { + const uint32_t byte_size = m_opcode.GetByteSize(); + if (byte_size == 2) + { + if (Bits32(opcode, 15, 12) == 0x0d && Bits32(opcode, 11, 7) != 0x0f) + return Bits32(opcode, 11, 7); + } + else if (byte_size == 4) + { + if (Bits32(opcode, 31, 27) == 0x1e && + Bits32(opcode, 15, 14) == 0x02 && + Bits32(opcode, 12, 12) == 0x00 && + Bits32(opcode, 25, 22) <= 0x0d) + { + return Bits32(opcode, 25, 22); + } + } + else + // We have an invalid thumb instruction, let's bail out. + break; + + return m_it_session.GetCond(); + } + } + return UINT32_MAX; // Return invalid value +} + +bool +EmulateInstructionARM::InITBlock() +{ + return CurrentInstrSet() == eModeThumb && m_it_session.InITBlock(); +} + +bool +EmulateInstructionARM::LastInITBlock() +{ + return CurrentInstrSet() == eModeThumb && m_it_session.LastInITBlock(); +} + +bool +EmulateInstructionARM::BadMode (uint32_t mode) +{ + + switch (mode) + { + case 16: return false; // '10000' + case 17: return false; // '10001' + case 18: return false; // '10010' + case 19: return false; // '10011' + case 22: return false; // '10110' + case 23: return false; // '10111' + case 27: return false; // '11011' + case 31: return false; // '11111' + default: return true; + } + return true; +} + +bool +EmulateInstructionARM::CurrentModeIsPrivileged () +{ + uint32_t mode = Bits32 (m_opcode_cpsr, 4, 0); + + if (BadMode (mode)) + return false; + + if (mode == 16) + return false; + + return true; +} + +void +EmulateInstructionARM::CPSRWriteByInstr (uint32_t value, uint32_t bytemask, bool affect_execstate) +{ + bool privileged = CurrentModeIsPrivileged(); + + uint32_t tmp_cpsr = Bits32 (m_opcode_cpsr, 23, 20) << 20; + + if (BitIsSet (bytemask, 3)) + { + tmp_cpsr = tmp_cpsr | (Bits32 (value, 31, 27) << 27); + if (affect_execstate) + tmp_cpsr = tmp_cpsr | (Bits32 (value, 26, 24) << 24); + } + + if (BitIsSet (bytemask, 2)) + { + tmp_cpsr = tmp_cpsr | (Bits32 (value, 19, 16) << 16); + } + + if (BitIsSet (bytemask, 1)) + { + if (affect_execstate) + tmp_cpsr = tmp_cpsr | (Bits32 (value, 15, 10) << 10); + tmp_cpsr = tmp_cpsr | (Bit32 (value, 9) << 9); + if (privileged) + tmp_cpsr = tmp_cpsr | (Bit32 (value, 8) << 8); + } + + if (BitIsSet (bytemask, 0)) + { + if (privileged) + tmp_cpsr = tmp_cpsr | (Bits32 (value, 7, 6) << 6); + if (affect_execstate) + tmp_cpsr = tmp_cpsr | (Bit32 (value, 5) << 5); + if (privileged) + tmp_cpsr = tmp_cpsr | Bits32 (value, 4, 0); + } + + m_opcode_cpsr = tmp_cpsr; +} + + +bool +EmulateInstructionARM::BranchWritePC (const Context &context, uint32_t addr) +{ + addr_t target; + + // Check the current instruction set. + if (CurrentInstrSet() == eModeARM) + target = addr & 0xfffffffc; + else + target = addr & 0xfffffffe; + + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, target)) + return false; + + return true; +} + +// As a side effect, BXWritePC sets context.arg2 to eModeARM or eModeThumb by inspecting addr. +bool +EmulateInstructionARM::BXWritePC (Context &context, uint32_t addr) +{ + addr_t target; + // If the CPSR is changed due to switching between ARM and Thumb ISETSTATE, + // we want to record it and issue a WriteRegister callback so the clients + // can track the mode changes accordingly. + bool cpsr_changed = false; + + if (BitIsSet(addr, 0)) + { + if (CurrentInstrSet() != eModeThumb) + { + SelectInstrSet(eModeThumb); + cpsr_changed = true; + } + target = addr & 0xfffffffe; + context.SetISA (eModeThumb); + } + else if (BitIsClear(addr, 1)) + { + if (CurrentInstrSet() != eModeARM) + { + SelectInstrSet(eModeARM); + cpsr_changed = true; + } + target = addr & 0xfffffffc; + context.SetISA (eModeARM); + } + else + return false; // address<1:0> == '10' => UNPREDICTABLE + + if (cpsr_changed) + { + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) + return false; + } + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, target)) + return false; + + return true; +} + +// Dispatches to either BXWritePC or BranchWritePC based on architecture versions. +bool +EmulateInstructionARM::LoadWritePC (Context &context, uint32_t addr) +{ + if (ArchVersion() >= ARMv5T) + return BXWritePC(context, addr); + else + return BranchWritePC((const Context)context, addr); +} + +// Dispatches to either BXWritePC or BranchWritePC based on architecture versions and current instruction set. +bool +EmulateInstructionARM::ALUWritePC (Context &context, uint32_t addr) +{ + if (ArchVersion() >= ARMv7 && CurrentInstrSet() == eModeARM) + return BXWritePC(context, addr); + else + return BranchWritePC((const Context)context, addr); +} + +EmulateInstructionARM::Mode +EmulateInstructionARM::CurrentInstrSet () +{ + return m_opcode_mode; +} + +// Set the 'T' bit of our CPSR. The m_opcode_mode gets updated when the next +// ReadInstruction() is performed. This function has a side effect of updating +// the m_new_inst_cpsr member variable if necessary. +bool +EmulateInstructionARM::SelectInstrSet (Mode arm_or_thumb) +{ + m_new_inst_cpsr = m_opcode_cpsr; + switch (arm_or_thumb) + { + default: + return false; + case eModeARM: + // Clear the T bit. + m_new_inst_cpsr &= ~MASK_CPSR_T; + break; + case eModeThumb: + // Set the T bit. + m_new_inst_cpsr |= MASK_CPSR_T; + break; + } + return true; +} + +// This function returns TRUE if the processor currently provides support for +// unaligned memory accesses, or FALSE otherwise. This is always TRUE in ARMv7, +// controllable by the SCTLR.U bit in ARMv6, and always FALSE before ARMv6. +bool +EmulateInstructionARM::UnalignedSupport() +{ + return (ArchVersion() >= ARMv7); +} + +// The main addition and subtraction instructions can produce status information +// about both unsigned carry and signed overflow conditions. This status +// information can be used to synthesize multi-word additions and subtractions. +EmulateInstructionARM::AddWithCarryResult +EmulateInstructionARM::AddWithCarry (uint32_t x, uint32_t y, uint8_t carry_in) +{ + uint32_t result; + uint8_t carry_out; + uint8_t overflow; + + uint64_t unsigned_sum = x + y + carry_in; + int64_t signed_sum = (int32_t)x + (int32_t)y + (int32_t)carry_in; + + result = UnsignedBits(unsigned_sum, 31, 0); +// carry_out = (result == unsigned_sum ? 0 : 1); + overflow = ((int32_t)result == signed_sum ? 0 : 1); + + if (carry_in) + carry_out = ((int32_t) x >= (int32_t) (~y)) ? 1 : 0; + else + carry_out = ((int32_t) x > (int32_t) y) ? 1 : 0; + + AddWithCarryResult res = { result, carry_out, overflow }; + return res; +} + +uint32_t +EmulateInstructionARM::ReadCoreReg(uint32_t num, bool *success) +{ + uint32_t reg_kind, reg_num; + switch (num) + { + case SP_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_SP; + break; + case LR_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_RA; + break; + case PC_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_PC; + break; + default: + if (num < SP_REG) + { + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_r0 + num; + } + else + { + //assert(0 && "Invalid register number"); + *success = false; + return UINT32_MAX; + } + break; + } + + // Read our register. + uint32_t val = ReadRegisterUnsigned (reg_kind, reg_num, 0, success); + + // When executing an ARM instruction , PC reads as the address of the current + // instruction plus 8. + // When executing a Thumb instruction , PC reads as the address of the current + // instruction plus 4. + if (num == 15) + { + if (CurrentInstrSet() == eModeARM) + val += 8; + else + val += 4; + } + + return val; +} + +// Write the result to the ARM core register Rd, and optionally update the +// condition flags based on the result. +// +// This helper method tries to encapsulate the following pseudocode from the +// ARM Architecture Reference Manual: +// +// if d == 15 then // Can only occur for encoding A1 +// ALUWritePC(result); // setflags is always FALSE here +// else +// R[d] = result; +// if setflags then +// APSR.N = result<31>; +// APSR.Z = IsZeroBit(result); +// APSR.C = carry; +// // APSR.V unchanged +// +// In the above case, the API client does not pass in the overflow arg, which +// defaults to ~0u. +bool +EmulateInstructionARM::WriteCoreRegOptionalFlags (Context &context, + const uint32_t result, + const uint32_t Rd, + bool setflags, + const uint32_t carry, + const uint32_t overflow) +{ + if (Rd == 15) + { + if (!ALUWritePC (context, result)) + return false; + } + else + { + uint32_t reg_kind, reg_num; + switch (Rd) + { + case SP_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_SP; + break; + case LR_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_RA; + break; + default: + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_r0 + Rd; + } + if (!WriteRegisterUnsigned (context, reg_kind, reg_num, result)) + return false; + if (setflags) + return WriteFlags (context, result, carry, overflow); + } + return true; +} + +// This helper method tries to encapsulate the following pseudocode from the +// ARM Architecture Reference Manual: +// +// APSR.N = result<31>; +// APSR.Z = IsZeroBit(result); +// APSR.C = carry; +// APSR.V = overflow +// +// Default arguments can be specified for carry and overflow parameters, which means +// not to update the respective flags. +bool +EmulateInstructionARM::WriteFlags (Context &context, + const uint32_t result, + const uint32_t carry, + const uint32_t overflow) +{ + m_new_inst_cpsr = m_opcode_cpsr; + SetBit32(m_new_inst_cpsr, CPSR_N_POS, Bit32(result, CPSR_N_POS)); + SetBit32(m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0); + if (carry != ~0u) + SetBit32(m_new_inst_cpsr, CPSR_C_POS, carry); + if (overflow != ~0u) + SetBit32(m_new_inst_cpsr, CPSR_V_POS, overflow); + if (m_new_inst_cpsr != m_opcode_cpsr) + { + if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) + return false; + } + return true; +} + +bool +EmulateInstructionARM::EvaluateInstruction (uint32_t evaluate_options) +{ + // Advance the ITSTATE bits to their values for the next instruction. + if (m_opcode_mode == eModeThumb && m_it_session.InITBlock()) + m_it_session.ITAdvance(); + + ARMOpcode *opcode_data = NULL; + + if (m_opcode_mode == eModeThumb) + opcode_data = GetThumbOpcodeForInstruction (m_opcode.GetOpcode32(), m_arm_isa); + else if (m_opcode_mode == eModeARM) + opcode_data = GetARMOpcodeForInstruction (m_opcode.GetOpcode32(), m_arm_isa); + + if (opcode_data == NULL) + return false; + + const bool auto_advance_pc = evaluate_options & eEmulateInstructionOptionAutoAdvancePC; + m_ignore_conditions = evaluate_options & eEmulateInstructionOptionIgnoreConditions; + + bool success = false; + if (m_opcode_cpsr == 0 || m_ignore_conditions == false) + { + m_opcode_cpsr = ReadRegisterUnsigned (eRegisterKindDWARF, + dwarf_cpsr, + 0, + &success); + } + + // Only return false if we are unable to read the CPSR if we care about conditions + if (success == false && m_ignore_conditions == false) + return false; + + uint32_t orig_pc_value = 0; + if (auto_advance_pc) + { + orig_pc_value = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc, 0, &success); + if (!success) + return false; + } + + // Call the Emulate... function. + success = (this->*opcode_data->callback) (m_opcode.GetOpcode32(), opcode_data->encoding); + if (!success) + return false; + + if (auto_advance_pc) + { + uint32_t after_pc_value = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc, 0, &success); + if (!success) + return false; + + if (auto_advance_pc && (after_pc_value == orig_pc_value)) + { + if (opcode_data->size == eSize32) + after_pc_value += 4; + else if (opcode_data->size == eSize16) + after_pc_value += 2; + + EmulateInstruction::Context context; + context.type = eContextAdvancePC; + context.SetNoArgs(); + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc, after_pc_value)) + return false; + + } + } + return true; +} + +bool +EmulateInstructionARM::TestEmulation (Stream *out_stream, ArchSpec &arch, OptionValueDictionary *test_data) +{ + if (!test_data) + { + out_stream->Printf ("TestEmulation: Missing test data.\n"); + return false; + } + + static ConstString opcode_key ("opcode"); + static ConstString before_key ("before_state"); + static ConstString after_key ("after_state"); + + OptionValueSP value_sp = test_data->GetValueForKey (opcode_key); + + uint32_t test_opcode; + if ((value_sp.get() == NULL) || (value_sp->GetType() != OptionValue::eTypeUInt64)) + { + out_stream->Printf ("TestEmulation: Error reading opcode from test file.\n"); + return false; + } + test_opcode = value_sp->GetUInt64Value (); + + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + m_opcode_mode = eModeARM; + m_opcode.SetOpcode32 (test_opcode); + } + else if (arch.GetTriple().getArch() == llvm::Triple::thumb) + { + m_opcode_mode = eModeThumb; + if (test_opcode < 0x10000) + m_opcode.SetOpcode16 (test_opcode); + else + m_opcode.SetOpcode32 (test_opcode); + + } + else + { + out_stream->Printf ("TestEmulation: Invalid arch.\n"); + return false; + } + + EmulationStateARM before_state; + EmulationStateARM after_state; + + value_sp = test_data->GetValueForKey (before_key); + if ((value_sp.get() == NULL) || (value_sp->GetType() != OptionValue::eTypeDictionary)) + { + out_stream->Printf ("TestEmulation: Failed to find 'before' state.\n"); + return false; + } + + OptionValueDictionary *state_dictionary = value_sp->GetAsDictionary (); + if (!before_state.LoadStateFromDictionary (state_dictionary)) + { + out_stream->Printf ("TestEmulation: Failed loading 'before' state.\n"); + return false; + } + + value_sp = test_data->GetValueForKey (after_key); + if ((value_sp.get() == NULL) || (value_sp->GetType() != OptionValue::eTypeDictionary)) + { + out_stream->Printf ("TestEmulation: Failed to find 'after' state.\n"); + return false; + } + + state_dictionary = value_sp->GetAsDictionary (); + if (!after_state.LoadStateFromDictionary (state_dictionary)) + { + out_stream->Printf ("TestEmulation: Failed loading 'after' state.\n"); + return false; + } + + SetBaton ((void *) &before_state); + SetCallbacks (&EmulationStateARM::ReadPseudoMemory, + &EmulationStateARM::WritePseudoMemory, + &EmulationStateARM::ReadPseudoRegister, + &EmulationStateARM::WritePseudoRegister); + + bool success = EvaluateInstruction (eEmulateInstructionOptionAutoAdvancePC); + if (!success) + { + out_stream->Printf ("TestEmulation: EvaluateInstruction() failed.\n"); + return false; + } + + success = before_state.CompareState (after_state); + if (!success) + out_stream->Printf ("TestEmulation: 'before' and 'after' states do not match.\n"); + + return success; +} +// +// +//const char * +//EmulateInstructionARM::GetRegisterName (uint32_t reg_kind, uint32_t reg_num) +//{ +// if (reg_kind == eRegisterKindGeneric) +// { +// switch (reg_num) +// { +// case LLDB_REGNUM_GENERIC_PC: return "pc"; +// case LLDB_REGNUM_GENERIC_SP: return "sp"; +// case LLDB_REGNUM_GENERIC_FP: return "fp"; +// case LLDB_REGNUM_GENERIC_RA: return "lr"; +// case LLDB_REGNUM_GENERIC_FLAGS: return "cpsr"; +// default: return NULL; +// } +// } +// else if (reg_kind == eRegisterKindDWARF) +// { +// return GetARMDWARFRegisterName (reg_num); +// } +// return NULL; +//} +// +bool +EmulateInstructionARM::CreateFunctionEntryUnwind (UnwindPlan &unwind_plan) +{ + unwind_plan.Clear(); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our previous Call Frame Address is the stack pointer + row->SetCFARegister (dwarf_sp); + + // Our previous PC is in the LR + row->SetRegisterLocationToRegister(dwarf_pc, dwarf_lr, true); + unwind_plan.AppendRow (row); + + // All other registers are the same. + + unwind_plan.SetSourceName ("EmulateInstructionARM"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); + return true; +} diff --git a/source/Plugins/Instruction/ARM/EmulateInstructionARM.h b/source/Plugins/Instruction/ARM/EmulateInstructionARM.h new file mode 100644 index 000000000000..b926dc0deb4a --- /dev/null +++ b/source/Plugins/Instruction/ARM/EmulateInstructionARM.h @@ -0,0 +1,990 @@ +//===-- lldb_EmulateInstructionARM.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_EmulateInstructionARM_h_ +#define lldb_EmulateInstructionARM_h_ + +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "Plugins/Process/Utility/ARMDefines.h" + +namespace lldb_private { + +// ITSession - Keep track of the IT Block progression. +class ITSession +{ +public: + ITSession() : ITCounter(0), ITState(0) {} + ~ITSession() {} + + // InitIT - Initializes ITCounter/ITState. + bool InitIT(uint32_t bits7_0); + + // ITAdvance - Updates ITCounter/ITState as IT Block progresses. + void ITAdvance(); + + // InITBlock - Returns true if we're inside an IT Block. + bool InITBlock(); + + // LastInITBlock - Returns true if we're the last instruction inside an IT Block. + bool LastInITBlock(); + + // GetCond - Gets condition bits for the current thumb instruction. + uint32_t GetCond(); + +private: + uint32_t ITCounter; // Possible values: 0, 1, 2, 3, 4. + uint32_t ITState; // A2.5.2 Consists of IT[7:5] and IT[4:0] initially. +}; + +class EmulateInstructionARM : public EmulateInstruction +{ +public: + typedef enum + { + eEncodingA1, + eEncodingA2, + eEncodingA3, + eEncodingA4, + eEncodingA5, + eEncodingT1, + eEncodingT2, + eEncodingT3, + eEncodingT4, + eEncodingT5 + } ARMEncoding; + + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (); + + static const char * + GetPluginDescriptionStatic (); + + static lldb_private::EmulateInstruction * + CreateInstance (const lldb_private::ArchSpec &arch, + InstructionType inst_type); + + static bool + SupportsEmulatingIntructionsOfTypeStatic (InstructionType inst_type) + { + switch (inst_type) + { + case eInstructionTypeAny: + case eInstructionTypePrologueEpilogue: + case eInstructionTypePCModifying: + return true; + + case eInstructionTypeAll: + return false; + } + return false; + } + + virtual lldb_private::ConstString + GetPluginName() + { + return GetPluginNameStatic(); + } + + virtual uint32_t + GetPluginVersion() + { + return 1; + } + + bool + SetTargetTriple (const ArchSpec &arch); + + enum Mode + { + eModeInvalid = -1, + eModeARM, + eModeThumb + }; + + EmulateInstructionARM (const ArchSpec &arch) : + EmulateInstruction (arch), + m_arm_isa (0), + m_opcode_mode (eModeInvalid), + m_opcode_cpsr (0), + m_it_session (), + m_ignore_conditions (false) + { + SetArchitecture (arch); + } + +// EmulateInstructionARM (const ArchSpec &arch, +// bool ignore_conditions, +// void *baton, +// ReadMemory read_mem_callback, +// WriteMemory write_mem_callback, +// ReadRegister read_reg_callback, +// WriteRegister write_reg_callback) : +// EmulateInstruction (arch, +// ignore_conditions, +// baton, +// read_mem_callback, +// write_mem_callback, +// read_reg_callback, +// write_reg_callback), +// m_arm_isa (0), +// m_opcode_mode (eModeInvalid), +// m_opcode_cpsr (0), +// m_it_session () +// { +// } + + virtual bool + SupportsEmulatingIntructionsOfType (InstructionType inst_type) + { + return SupportsEmulatingIntructionsOfTypeStatic (inst_type); + } + + virtual bool + SetArchitecture (const ArchSpec &arch); + + virtual bool + ReadInstruction (); + + virtual bool + SetInstruction (const Opcode &insn_opcode, const Address &inst_addr, Target *target); + + virtual bool + EvaluateInstruction (uint32_t evaluate_options); + + virtual bool + TestEmulation (Stream *out_stream, ArchSpec &arch, OptionValueDictionary *test_data); + + virtual bool + GetRegisterInfo (uint32_t reg_kind, uint32_t reg_num, RegisterInfo ®_info); + + + virtual bool + CreateFunctionEntryUnwind (UnwindPlan &unwind_plan); + + uint32_t + ArchVersion(); + + bool + ConditionPassed (const uint32_t opcode, + bool *is_conditional = NULL); // Filled in with true if the opcode is a conditional opcode + // Filled in with false if the opcode is always executed + + uint32_t + CurrentCond (const uint32_t opcode); + + // InITBlock - Returns true if we're in Thumb mode and inside an IT Block. + bool InITBlock(); + + // LastInITBlock - Returns true if we're in Thumb mode and the last instruction inside an IT Block. + bool LastInITBlock(); + + bool + BadMode (uint32_t mode); + + bool + CurrentModeIsPrivileged (); + + void + CPSRWriteByInstr (uint32_t value, uint32_t bytemask, bool affect_execstate); + + bool + BranchWritePC(const Context &context, uint32_t addr); + + bool + BXWritePC(Context &context, uint32_t addr); + + bool + LoadWritePC(Context &context, uint32_t addr); + + bool + ALUWritePC(Context &context, uint32_t addr); + + Mode + CurrentInstrSet(); + + bool + SelectInstrSet(Mode arm_or_thumb); + + bool + WriteBits32Unknown (int n); + + bool + WriteBits32UnknownToMemory (lldb::addr_t address); + + bool + UnalignedSupport(); + + typedef struct + { + uint32_t result; + uint8_t carry_out; + uint8_t overflow; + } AddWithCarryResult; + + AddWithCarryResult + AddWithCarry(uint32_t x, uint32_t y, uint8_t carry_in); + + // Helper method to read the content of an ARM core register. + uint32_t + ReadCoreReg (uint32_t regnum, bool *success); + + // See A8.6.96 MOV (immediate) Operation. + // Default arguments are specified for carry and overflow parameters, which means + // not to update the respective flags even if setflags is true. + bool + WriteCoreRegOptionalFlags (Context &context, + const uint32_t result, + const uint32_t Rd, + bool setflags, + const uint32_t carry = ~0u, + const uint32_t overflow = ~0u); + + bool + WriteCoreReg (Context &context, + const uint32_t result, + const uint32_t Rd) + { + // Don't set the flags. + return WriteCoreRegOptionalFlags(context, result, Rd, false); + } + + // See A8.6.35 CMP (immediate) Operation. + // Default arguments are specified for carry and overflow parameters, which means + // not to update the respective flags. + bool + WriteFlags (Context &context, + const uint32_t result, + const uint32_t carry = ~0u, + const uint32_t overflow = ~0u); + + inline uint64_t + MemARead (EmulateInstruction::Context &context, + lldb::addr_t address, + uint32_t size, + uint64_t fail_value, + bool *success_ptr) + { + // This is a stub function corresponding to "MemA[]" in the ARM manual pseudocode, for + // aligned reads from memory. Since we are not trying to write a full hardware simulator, and since + // we are running in User mode (rather than Kernel mode) and therefore won't have access to many of the + // system registers we would need in order to fully implement this function, we will just call + // ReadMemoryUnsigned from here. In the future, if we decide we do need to do more faithful emulation of + // the hardware, we can update this function appropriately. + + return ReadMemoryUnsigned (context, address, size, fail_value, success_ptr); + } + + inline bool + MemAWrite (EmulateInstruction::Context &context, + lldb::addr_t address, + uint64_t data_val, + uint32_t size) + + { + // This is a stub function corresponding to "MemA[]" in the ARM manual pseudocode, for + // aligned writes to memory. Since we are not trying to write a full hardware simulator, and since + // we are running in User mode (rather than Kernel mode) and therefore won't have access to many of the + // system registers we would need in order to fully implement this function, we will just call + // WriteMemoryUnsigned from here. In the future, if we decide we do need to do more faithful emulation of + // the hardware, we can update this function appropriately. + + return WriteMemoryUnsigned (context, address, data_val, size); + } + + + inline uint64_t + MemURead (EmulateInstruction::Context &context, + lldb::addr_t address, + uint32_t size, + uint64_t fail_value, + bool *success_ptr) + { + // This is a stub function corresponding to "MemU[]" in the ARM manual pseudocode, for + // unaligned reads from memory. Since we are not trying to write a full hardware simulator, and since + // we are running in User mode (rather than Kernel mode) and therefore won't have access to many of the + // system registers we would need in order to fully implement this function, we will just call + // ReadMemoryUnsigned from here. In the future, if we decide we do need to do more faithful emulation of + // the hardware, we can update this function appropriately. + + return ReadMemoryUnsigned (context, address, size, fail_value, success_ptr); + } + + inline bool + MemUWrite (EmulateInstruction::Context &context, + lldb::addr_t address, + uint64_t data_val, + uint32_t size) + + { + // This is a stub function corresponding to "MemU[]" in the ARM manual pseudocode, for + // unaligned writes to memory. Since we are not trying to write a full hardware simulator, and since + // we are running in User mode (rather than Kernel mode) and therefore won't have access to many of the + // system registers we would need in order to fully implement this function, we will just call + // WriteMemoryUnsigned from here. In the future, if we decide we do need to do more faithful emulation of + // the hardware, we can update this function appropriately. + + return WriteMemoryUnsigned (context, address, data_val, size); + } + +protected: + + // Typedef for the callback function used during the emulation. + // Pass along (ARMEncoding)encoding as the callback data. + typedef enum + { + eSize16, + eSize32 + } ARMInstrSize; + + typedef struct + { + uint32_t mask; + uint32_t value; + uint32_t variants; + EmulateInstructionARM::ARMEncoding encoding; + uint32_t vfp_variants; + ARMInstrSize size; + bool (EmulateInstructionARM::*callback) (const uint32_t opcode, const EmulateInstructionARM::ARMEncoding encoding); + const char *name; + } ARMOpcode; + + uint32_t + GetFramePointerRegisterNumber () const; + + uint32_t + GetFramePointerDWARFRegisterNumber () const; + + static ARMOpcode* + GetARMOpcodeForInstruction (const uint32_t opcode, uint32_t isa_mask); + + static ARMOpcode* + GetThumbOpcodeForInstruction (const uint32_t opcode, uint32_t isa_mask); + + // A8.6.123 PUSH + bool + EmulatePUSH (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.122 POP + bool + EmulatePOP (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.8 ADD (SP plus immediate) + bool + EmulateADDRdSPImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.97 MOV (register) -- Rd == r7|ip and Rm == sp + bool + EmulateMOVRdSP (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.97 MOV (register) -- move from r8-r15 to r0-r7 + bool + EmulateMOVLowHigh (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.59 LDR (literal) + bool + EmulateLDRRtPCRelative (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.8 ADD (SP plus immediate) + bool + EmulateADDSPImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.9 ADD (SP plus register) + bool + EmulateADDSPRm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.23 BL, BLX (immediate) + bool + EmulateBLXImmediate (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.24 BLX (register) + bool + EmulateBLXRm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.25 BX + bool + EmulateBXRm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.26 BXJ + bool + EmulateBXJRm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.212 SUB (immediate, ARM) -- Rd == r7 and Rm == ip + bool + EmulateSUBR7IPImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.215 SUB (SP minus immediate) -- Rd == ip + bool + EmulateSUBIPSPImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.215 SUB (SP minus immediate) + bool + EmulateSUBSPImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.216 SUB (SP minus register) + bool + EmulateSUBSPReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.194 STR (immediate, ARM) -- Rn == sp + bool + EmulateSTRRtSP (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.355 VPUSH + bool + EmulateVPUSH (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.354 VPOP + bool + EmulateVPOP (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.218 SVC (previously SWI) + bool + EmulateSVC (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.50 IT + bool + EmulateIT (const uint32_t opcode, const ARMEncoding encoding); + + // NOP + bool + EmulateNop (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.16 B + bool + EmulateB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.27 CBNZ, CBZ + bool + EmulateCB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.226 TBB, TBH + bool + EmulateTB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.4 ADD (immediate, Thumb) + bool + EmulateADDImmThumb (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.5 ADD (immediate, ARM) + bool + EmulateADDImmARM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.6 ADD (register) + bool + EmulateADDReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.7 ADD (register-shifted register) + bool + EmulateADDRegShift (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.97 MOV (register) + bool + EmulateMOVRdRm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.96 MOV (immediate) + bool + EmulateMOVRdImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.35 CMP (immediate) + bool + EmulateCMPImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.36 CMP (register) + bool + EmulateCMPReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.14 ASR (immediate) + bool + EmulateASRImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.15 ASR (register) + bool + EmulateASRReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.88 LSL (immediate) + bool + EmulateLSLImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.89 LSL (register) + bool + EmulateLSLReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.90 LSR (immediate) + bool + EmulateLSRImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.91 LSR (register) + bool + EmulateLSRReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.139 ROR (immediate) + bool + EmulateRORImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.140 ROR (register) + bool + EmulateRORReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.141 RRX + bool + EmulateRRX (const uint32_t opcode, const ARMEncoding encoding); + + // Helper method for ASR, LSL, LSR, ROR (immediate), and RRX + bool + EmulateShiftImm (const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type); + + // Helper method for ASR, LSL, LSR, and ROR (register) + bool + EmulateShiftReg (const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type); + + // LOAD FUNCTIONS + + // A8.6.53 LDM/LDMIA/LDMFD + bool + EmulateLDM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.54 LDMDA/LDMFA + bool + EmulateLDMDA (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.55 LDMDB/LDMEA + bool + EmulateLDMDB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.56 LDMIB/LDMED + bool + EmulateLDMIB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.57 LDR (immediate, Thumb) -- Encoding T1 + bool + EmulateLDRRtRnImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.58 LDR (immediate, ARM) - Encoding A1 + bool + EmulateLDRImmediateARM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.59 LDR (literal) + bool + EmulateLDRLiteral (const uint32_t, const ARMEncoding encoding); + + // A8.6.60 LDR (register) - Encoding T1, T2, A1 + bool + EmulateLDRRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.61 LDRB (immediate, Thumb) - Encoding T1, T2, T3 + bool + EmulateLDRBImmediate (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.62 LDRB (immediate, ARM) + bool + EmulateLDRBImmediateARM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.63 LDRB (literal) - Encoding T1, A1 + bool + EmulateLDRBLiteral (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.64 LDRB (register) - Encoding T1, T2, A1 + bool + EmulateLDRBRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.65 LDRBT + bool + EmulateLDRBT (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.66 LDRD (immediate) + bool + EmulateLDRDImmediate (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.67 + bool + EmulateLDRDLiteral (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.68 LDRD (register) + bool + EmulateLDRDRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.69 LDREX + bool + EmulateLDREX (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.70 LDREXB + bool + EmulateLDREXB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.71 LDREXD + bool + EmulateLDREXD (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.72 LDREXH + bool + EmulateLDREXH (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.73 LDRH (immediate, Thumb) - Encoding T1, T2, T3 + bool + EmulateLDRHImmediate (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.74 LDRS (immediate, ARM) + bool + EmulateLDRHImmediateARM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.75 LDRH (literal) - Encoding T1, A1 + bool + EmulateLDRHLiteral (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.76 LDRH (register) - Encoding T1, T2, A1 + bool + EmulateLDRHRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.77 LDRHT + bool + EmulateLDRHT (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.78 LDRSB (immediate) - Encoding T1, T2, A1 + bool + EmulateLDRSBImmediate (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.79 LDRSB (literal) - Encoding T1, A1 + bool + EmulateLDRSBLiteral (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.80 LDRSB (register) - Encoding T1, T2, A1 + bool + EmulateLDRSBRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.81 LDRSBT + bool + EmulateLDRSBT (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.82 LDRSH (immediate) - Encoding T1, T2, A1 + bool + EmulateLDRSHImmediate (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.83 LDRSH (literal) - Encoding T1, A1 + bool + EmulateLDRSHLiteral (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.84 LDRSH (register) - Encoding T1, T2, A1 + bool + EmulateLDRSHRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.85 LDRSHT + bool + EmulateLDRSHT (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.86 + bool + EmulateLDRT (const uint32_t opcode, const ARMEncoding encoding); + + + // STORE FUNCTIONS + + // A8.6.189 STM/STMIA/STMEA + bool + EmulateSTM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.190 STMDA/STMED + bool + EmulateSTMDA (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.191 STMDB/STMFD + bool + EmulateSTMDB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.192 STMIB/STMFA + bool + EmulateSTMIB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.193 STR (immediate, Thumb) + bool + EmulateSTRThumb(const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.194 STR (immediate, ARM) + bool + EmulateSTRImmARM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.195 STR (register) + bool + EmulateSTRRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.196 STRB (immediate, Thumb) + bool + EmulateSTRBThumb (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.197 STRB (immediate, ARM) + bool + EmulateSTRBImmARM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.198 STRB (register) + bool + EmulateSTRBReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.199 STRBT + bool + EmulateSTRBT (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.200 STRD (immediate) + bool + EmulateSTRDImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.201 STRD (register) + bool + EmulateSTRDReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.202 STREX + bool + EmulateSTREX (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.203 STREXB + bool + EmulateSTREXB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.204 STREXD + bool + EmulateSTREXD (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.205 STREXH + bool + EmulateSTREXH (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.206 STRH (immediate, Thumb) + bool + EmulateSTRHImmThumb (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.207 STRH (immediate, ARM) + bool + EmulateSTRHImmARM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.208 STRH (register) + bool + EmulateSTRHRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.209 STRHT + bool + EmulateSTRHT (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.210 STRT + bool + EmulateSTRT (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.1 ADC (immediate) + bool + EmulateADCImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.2 ADC (Register) + bool + EmulateADCReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.10 ADR + bool + EmulateADR (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.11 AND (immediate) + bool + EmulateANDImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.12 AND (register) + bool + EmulateANDReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.19 BIC (immediate) + bool + EmulateBICImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.20 BIC (register) + bool + EmulateBICReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.26 BXJ + bool + EmulateBXJ (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.32 CMN (immediate) + bool + EmulateCMNImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.33 CMN (register) + bool + EmulateCMNReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.44 EOR (immediate) + bool + EmulateEORImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.45 EOR (register) + bool + EmulateEORReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.105 MUL + bool + EmulateMUL (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.106 MVN (immediate) + bool + EmulateMVNImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.107 MVN (register) + bool + EmulateMVNReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.113 ORR (immediate) + bool + EmulateORRImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.114 ORR (register) + bool + EmulateORRReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.117 PLD (immediate, literal) - Encoding T1, T2, T3, A1 + bool + EmulatePLDImmediate (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.119 PLI (immediate,literal) - Encoding T3, A1 + bool + EmulatePLIImmediate (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.120 PLI (register) - Encoding T1, A1 + bool + EmulatePLIRegister (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.141 RSB (immediate) + bool + EmulateRSBImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.142 RSB (register) + bool + EmulateRSBReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.144 RSC (immediate) + bool + EmulateRSCImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.145 RSC (register) + bool + EmulateRSCReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.150 SBC (immediate) + bool + EmulateSBCImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.151 SBC (register) + bool + EmulateSBCReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.211 SUB (immediate, Thumb) + bool + EmulateSUBImmThumb (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.212 SUB (immediate, ARM) + bool + EmulateSUBImmARM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.213 SUB (register) + bool + EmulateSUBReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.214 SUB (register-shifted register) + bool + EmulateSUBRegShift (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.222 SXTB - Encoding T1 + bool + EmulateSXTB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.224 SXTH - EncodingT1 + bool + EmulateSXTH (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.227 TEQ (immediate) - Encoding A1 + bool + EmulateTEQImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.228 TEQ (register) - Encoding A1 + bool + EmulateTEQReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.230 TST (immediate) - Encoding A1 + bool + EmulateTSTImm (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.231 TST (register) - Encoding T1, A1 + bool + EmulateTSTReg (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.262 UXTB - Encoding T1 + bool + EmulateUXTB (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.264 UXTH - Encoding T1 + bool + EmulateUXTH (const uint32_t opcode, const ARMEncoding encoding); + + // B6.1.8 RFE + bool + EmulateRFE (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.319 VLDM + bool + EmulateVLDM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.399 VSTM + bool + EmulateVSTM (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.307 VLD1 (multiple single elements) + bool + EmulateVLD1Multiple (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.308 VLD1 (single element to one lane) + bool + EmulateVLD1Single (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.309 VLD1 (single element to all lanes) + bool + EmulateVLD1SingleAll (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.391 VST1 (multiple single elements) + bool + EmulateVST1Multiple (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.392 VST1 (single element from one lane) + bool + EmulateVST1Single (const uint32_t opcode, const ARMEncoding encoding); + + // A8.6.317 VLDR + bool + EmulateVLDR (const uint32_t opcode, const ARMEncoding encoding); + + + // A8.6.400 VSTR + bool + EmulateVSTR (const uint32_t opcode, const ARMEncoding encoding); + + // B6.2.13 SUBS PC, LR and related instructions + bool + EmulateSUBSPcLrEtc (const uint32_t opcode, const ARMEncoding encoding); + + uint32_t m_arm_isa; + Mode m_opcode_mode; + uint32_t m_opcode_cpsr; + uint32_t m_new_inst_cpsr; // This can get updated by the opcode. + ITSession m_it_session; + bool m_ignore_conditions; +}; + +} // namespace lldb_private + +#endif // lldb_EmulateInstructionARM_h_ diff --git a/source/Plugins/Instruction/ARM/EmulationStateARM.cpp b/source/Plugins/Instruction/ARM/EmulationStateARM.cpp new file mode 100644 index 000000000000..bad4118971e0 --- /dev/null +++ b/source/Plugins/Instruction/ARM/EmulationStateARM.cpp @@ -0,0 +1,406 @@ +//===-- EmulationStateARM.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "EmulationStateARM.h" + +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/RegisterContext.h" + +#include "Utility/ARM_DWARF_Registers.h" + +using namespace lldb; +using namespace lldb_private; + +EmulationStateARM::EmulationStateARM () : + m_gpr (), + m_vfp_regs (), + m_memory () +{ + ClearPseudoRegisters(); +} + +EmulationStateARM::~EmulationStateARM () +{ +} + +bool +EmulationStateARM::LoadPseudoRegistersFromFrame (StackFrame &frame) +{ + RegisterContext *reg_ctx = frame.GetRegisterContext().get(); + bool success = true; + uint32_t reg_num; + + for (int i = dwarf_r0; i < dwarf_r0 + 17; ++i) + { + reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindDWARF, i); + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num); + RegisterValue reg_value; + if (reg_ctx->ReadRegister (reg_info, reg_value)) + { + m_gpr[i - dwarf_r0] = reg_value.GetAsUInt32(); + } + else + success = false; + } + + for (int i = dwarf_d0; i < dwarf_d0 + 32; ++i) + { + reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindDWARF, i); + RegisterValue reg_value; + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num); + + if (reg_ctx->ReadRegister (reg_info, reg_value)) + { + uint32_t idx = i - dwarf_d0; + if (i < 16) + m_vfp_regs.sd_regs[idx].d_reg = reg_value.GetAsUInt64(); + else + m_vfp_regs.d_regs[idx - 16] = reg_value.GetAsUInt64(); + } + else + success = false; + } + + return success; +} + +bool +EmulationStateARM::StorePseudoRegisterValue (uint32_t reg_num, uint64_t value) +{ + if ((dwarf_r0 <= reg_num) && (reg_num <= dwarf_cpsr)) + m_gpr[reg_num - dwarf_r0] = (uint32_t) value; + else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31)) + { + uint32_t idx = reg_num - dwarf_s0; + m_vfp_regs.sd_regs[idx / 2].s_reg[idx % 2] = (uint32_t) value; + } + else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31)) + { + if ((reg_num - dwarf_d0) < 16) + { + m_vfp_regs.sd_regs[reg_num - dwarf_d0].d_reg = value; + } + else + m_vfp_regs.d_regs[reg_num - dwarf_d16] = value; + } + else + return false; + + return true; +} + +uint64_t +EmulationStateARM::ReadPseudoRegisterValue (uint32_t reg_num, bool &success) +{ + uint64_t value = 0; + success = true; + + if ((dwarf_r0 <= reg_num) && (reg_num <= dwarf_cpsr)) + value = m_gpr[reg_num - dwarf_r0]; + else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31)) + { + uint32_t idx = reg_num - dwarf_s0; + value = m_vfp_regs.sd_regs[idx / 2].s_reg[idx % 2]; + } + else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31)) + { + if ((reg_num - dwarf_d0) < 16) + value = m_vfp_regs.sd_regs[reg_num - dwarf_d0].d_reg; + else + value = m_vfp_regs.d_regs[reg_num - dwarf_d16]; + } + else + success = false; + + return value; +} + +void +EmulationStateARM::ClearPseudoRegisters () +{ + for (int i = 0; i < 17; ++i) + m_gpr[i] = 0; + + for (int i = 0; i < 16; ++i) + m_vfp_regs.sd_regs[i].d_reg = 0; + + for (int i = 0; i < 16; ++i) + m_vfp_regs.d_regs[i] = 0; +} + +void +EmulationStateARM::ClearPseudoMemory () +{ + m_memory.clear(); +} + +bool +EmulationStateARM::StoreToPseudoAddress (lldb::addr_t p_address, uint64_t value, uint32_t size) +{ + if (size > 8) + return false; + + if (size <= 4) + m_memory[p_address] = value; + else if (size == 8) + { + m_memory[p_address] = (value << 32) >> 32; + m_memory[p_address + 4] = value << 32; + } + return true; +} + +uint32_t +EmulationStateARM::ReadFromPseudoAddress (lldb::addr_t p_address, uint32_t size, bool &success) +{ + std::map<lldb::addr_t,uint32_t>::iterator pos; + uint32_t ret_val = 0; + + success = true; + pos = m_memory.find(p_address); + if (pos != m_memory.end()) + ret_val = pos->second; + else + success = false; + + return ret_val; +} + +size_t +EmulationStateARM::ReadPseudoMemory (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + void *dst, + size_t length) +{ + if (!baton) + return 0; + + bool success = true; + EmulationStateARM *pseudo_state = (EmulationStateARM *) baton; + if (length <= 4) + { + uint32_t value = pseudo_state->ReadFromPseudoAddress (addr, length, success); + if (!success) + return 0; + + *((uint32_t *) dst) = value; + } + else if (length == 8) + { + uint32_t value1 = pseudo_state->ReadFromPseudoAddress (addr, 4, success); + if (!success) + return 0; + + uint32_t value2 = pseudo_state->ReadFromPseudoAddress (addr + 4, 4, success); + if (!success) + return 0; + + uint64_t value64 = value2; + value64 = (value64 << 32) | value1; + *((uint64_t *) dst) = value64; + } + else + success = false; + + if (success) + return length; + + return 0; +} + +size_t +EmulationStateARM::WritePseudoMemory (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t length) +{ + if (!baton) + return 0; + + bool success; + EmulationStateARM *pseudo_state = (EmulationStateARM *) baton; + uint64_t value = *((uint64_t *) dst); + success = pseudo_state->StoreToPseudoAddress (addr, value, length); + if (success) + return length; + + return 0; +} + +bool +EmulationStateARM::ReadPseudoRegister (EmulateInstruction *instruction, + void *baton, + const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value) +{ + if (!baton || !reg_info) + return false; + + bool success = true; + EmulationStateARM *pseudo_state = (EmulationStateARM *) baton; + const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF]; + assert (dwarf_reg_num != LLDB_INVALID_REGNUM); + uint64_t reg_uval = pseudo_state->ReadPseudoRegisterValue (dwarf_reg_num, success); + + if (success) + success = reg_value.SetUInt(reg_uval, reg_info->byte_size); + return success; + +} + +bool +EmulationStateARM::WritePseudoRegister (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value) +{ + if (!baton || !reg_info) + return false; + + EmulationStateARM *pseudo_state = (EmulationStateARM *) baton; + const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF]; + assert (dwarf_reg_num != LLDB_INVALID_REGNUM); + return pseudo_state->StorePseudoRegisterValue (dwarf_reg_num, reg_value.GetAsUInt64()); +} + +bool +EmulationStateARM::CompareState (EmulationStateARM &other_state) +{ + bool match = true; + + for (int i = 0; match && i < 17; ++i) + { + if (m_gpr[i] != other_state.m_gpr[i]) + match = false; + } + + for (int i = 0; match && i < 16; ++i) + { + if (m_vfp_regs.sd_regs[i].s_reg[0] != other_state.m_vfp_regs.sd_regs[i].s_reg[0]) + match = false; + + if (m_vfp_regs.sd_regs[i].s_reg[1] != other_state.m_vfp_regs.sd_regs[i].s_reg[1]) + match = false; + } + + for (int i = 0; match && i < 32; ++i) + { + if (i < 16) + { + if (m_vfp_regs.sd_regs[i].d_reg != other_state.m_vfp_regs.sd_regs[i].d_reg) + match = false; + } + else + { + if (m_vfp_regs.d_regs[i - 16] != other_state.m_vfp_regs.d_regs[i - 16]) + match = false; + } + } + + return match; +} + +bool +EmulationStateARM::LoadStateFromDictionary (OptionValueDictionary *test_data) +{ + static ConstString memory_key ("memory"); + static ConstString registers_key ("registers"); + + if (!test_data) + return false; + + OptionValueSP value_sp = test_data->GetValueForKey (memory_key); + + // Load memory, if present. + + if (value_sp.get() != NULL) + { + static ConstString address_key ("address"); + static ConstString data_key ("data"); + uint64_t start_address = 0; + + OptionValueDictionary *mem_dict = value_sp->GetAsDictionary(); + value_sp = mem_dict->GetValueForKey (address_key); + if (value_sp.get() == NULL) + return false; + else + start_address = value_sp->GetUInt64Value (); + + value_sp = mem_dict->GetValueForKey (data_key); + OptionValueArray *mem_array = value_sp->GetAsArray(); + if (!mem_array) + return false; + + uint32_t num_elts = mem_array->GetSize(); + uint32_t address = (uint32_t) start_address; + + for (uint32_t i = 0; i < num_elts; ++i) + { + value_sp = mem_array->GetValueAtIndex (i); + if (value_sp.get() == NULL) + return false; + uint64_t value = value_sp->GetUInt64Value(); + StoreToPseudoAddress (address, value, 4); + address = address + 4; + } + } + + value_sp = test_data->GetValueForKey (registers_key); + if (value_sp.get() == NULL) + return false; + + + // Load General Registers + + OptionValueDictionary *reg_dict = value_sp->GetAsDictionary (); + + StreamString sstr; + for (int i = 0; i < 16; ++i) + { + sstr.Clear(); + sstr.Printf ("r%d", i); + ConstString reg_name (sstr.GetData()); + value_sp = reg_dict->GetValueForKey (reg_name); + if (value_sp.get() == NULL) + return false; + uint64_t reg_value = value_sp->GetUInt64Value(); + StorePseudoRegisterValue (dwarf_r0 + i, reg_value); + } + + static ConstString cpsr_name ("cpsr"); + value_sp = reg_dict->GetValueForKey (cpsr_name); + if (value_sp.get() == NULL) + return false; + StorePseudoRegisterValue (dwarf_cpsr, value_sp->GetUInt64Value()); + + // Load s/d Registers + for (int i = 0; i < 32; ++i) + { + sstr.Clear(); + sstr.Printf ("s%d", i); + ConstString reg_name (sstr.GetData()); + value_sp = reg_dict->GetValueForKey (reg_name); + if (value_sp.get() == NULL) + return false; + uint64_t reg_value = value_sp->GetUInt64Value(); + StorePseudoRegisterValue (dwarf_s0 + i, reg_value); + } + + return true; +} + diff --git a/source/Plugins/Instruction/ARM/EmulationStateARM.h b/source/Plugins/Instruction/ARM/EmulationStateARM.h new file mode 100644 index 000000000000..8d84abc75614 --- /dev/null +++ b/source/Plugins/Instruction/ARM/EmulationStateARM.h @@ -0,0 +1,100 @@ +//===-- lldb_EmulationStateARM.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_EmulationStateARM_h_ +#define lldb_EmulationStateARM_h_ + +#include <map> + +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Opcode.h" + +class EmulationStateARM { +public: + + EmulationStateARM (); + + virtual + ~EmulationStateARM (); + + bool + StorePseudoRegisterValue (uint32_t reg_num, uint64_t value); + + uint64_t + ReadPseudoRegisterValue (uint32_t reg_num, bool &success); + + bool + StoreToPseudoAddress (lldb::addr_t p_address, uint64_t value, uint32_t size); + + uint32_t + ReadFromPseudoAddress (lldb::addr_t p_address, uint32_t size, bool &success); + + void + ClearPseudoRegisters (); + + void + ClearPseudoMemory (); + + bool + LoadPseudoRegistersFromFrame (lldb_private::StackFrame &frame); + + bool + LoadStateFromDictionary (lldb_private::OptionValueDictionary *test_data); + + bool + CompareState (EmulationStateARM &other_state); + + static size_t + ReadPseudoMemory (lldb_private::EmulateInstruction *instruction, + void *baton, + const lldb_private::EmulateInstruction::Context &context, + lldb::addr_t addr, + void *dst, + size_t length); + + static size_t + WritePseudoMemory (lldb_private::EmulateInstruction *instruction, + void *baton, + const lldb_private::EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + static bool + ReadPseudoRegister (lldb_private::EmulateInstruction *instruction, + void *baton, + const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value); + + static bool + WritePseudoRegister (lldb_private::EmulateInstruction *instruction, + void *baton, + const lldb_private::EmulateInstruction::Context &context, + const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value); +private: + uint32_t m_gpr[17]; + struct sd_regs + { + union + { + uint32_t s_reg[2]; + uint64_t d_reg; + } sd_regs[16]; // sregs 0 - 31 & dregs 0 - 15 + + uint64_t d_regs[16]; // dregs 16-31 + + } m_vfp_regs; + + std::map<lldb::addr_t, uint32_t> m_memory; // Eventually will want to change uint32_t to a data buffer heap type. + + DISALLOW_COPY_AND_ASSIGN (EmulationStateARM); +}; + +#endif // lldb_EmulationStateARM_h_ diff --git a/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp new file mode 100644 index 000000000000..247d7b0e7fe4 --- /dev/null +++ b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp @@ -0,0 +1,461 @@ +//===-- ItaniumABILanguageRuntime.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ItaniumABILanguageRuntime.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include <vector> + +using namespace lldb; +using namespace lldb_private; + +static const char *vtable_demangled_prefix = "vtable for "; + +bool +ItaniumABILanguageRuntime::CouldHaveDynamicValue (ValueObject &in_value) +{ + const bool check_cxx = true; + const bool check_objc = false; + return in_value.GetClangType().IsPossibleDynamicType (NULL, check_cxx, check_objc); +} + +bool +ItaniumABILanguageRuntime::GetDynamicTypeAndAddress (ValueObject &in_value, + lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, + Address &dynamic_address) +{ + // For Itanium, if the type has a vtable pointer in the object, it will be at offset 0 + // in the object. That will point to the "address point" within the vtable (not the beginning of the + // vtable.) We can then look up the symbol containing this "address point" and that symbol's name + // demangled will contain the full class name. + // The second pointer above the "address point" is the "offset_to_top". We'll use that to get the + // start of the value object which holds the dynamic type. + // + + class_type_or_name.Clear(); + + // Only a pointer or reference type can have a different dynamic and static type: + if (CouldHaveDynamicValue (in_value)) + { + // First job, pull out the address at 0 offset from the object. + AddressType address_type; + lldb::addr_t original_ptr = in_value.GetPointerValue(&address_type); + if (original_ptr == LLDB_INVALID_ADDRESS) + return false; + + ExecutionContext exe_ctx (in_value.GetExecutionContextRef()); + + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + + char memory_buffer[16]; + DataExtractor data(memory_buffer, sizeof(memory_buffer), + process->GetByteOrder(), + process->GetAddressByteSize()); + size_t address_byte_size = process->GetAddressByteSize(); + Error error; + size_t bytes_read = process->ReadMemory (original_ptr, + memory_buffer, + address_byte_size, + error); + if (!error.Success() || (bytes_read != address_byte_size)) + { + return false; + } + + lldb::offset_t offset = 0; + lldb::addr_t vtable_address_point = data.GetAddress (&offset); + + if (offset == 0) + return false; + + // Now find the symbol that contains this address: + + SymbolContext sc; + Address address_point_address; + if (target && !target->GetSectionLoadList().IsEmpty()) + { + if (target->GetSectionLoadList().ResolveLoadAddress (vtable_address_point, address_point_address)) + { + target->GetImages().ResolveSymbolContextForAddress (address_point_address, eSymbolContextSymbol, sc); + Symbol *symbol = sc.symbol; + if (symbol != NULL) + { + const char *name = symbol->GetMangled().GetDemangledName().AsCString(); + if (strstr(name, vtable_demangled_prefix) == name) + { + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has vtable symbol '%s'\n", + original_ptr, + in_value.GetTypeName().GetCString(), + name); + // We are a C++ class, that's good. Get the class name and look it up: + const char *class_name = name + strlen(vtable_demangled_prefix); + class_type_or_name.SetName (class_name); + const bool exact_match = true; + TypeList class_types; + + uint32_t num_matches = 0; + // First look in the module that the vtable symbol came from + // and look for a single exact match. + if (sc.module_sp) + { + num_matches = sc.module_sp->FindTypes (sc, + ConstString(class_name), + exact_match, + 1, + class_types); + } + + // If we didn't find a symbol, then move on to the entire + // module list in the target and get as many unique matches + // as possible + if (num_matches == 0) + { + num_matches = target->GetImages().FindTypes (sc, + ConstString(class_name), + exact_match, + UINT32_MAX, + class_types); + } + + lldb::TypeSP type_sp; + if (num_matches == 0) + { + if (log) + log->Printf("0x%16.16" PRIx64 ": is not dynamic\n", original_ptr); + return false; + } + if (num_matches == 1) + { + type_sp = class_types.GetTypeAtIndex(0); + if (log) + log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has dynamic type: uid={0x%" PRIx64 "}, type-name='%s'\n", + original_ptr, + in_value.GetTypeName().AsCString(), + type_sp->GetID(), + type_sp->GetName().GetCString()); + + class_type_or_name.SetTypeSP(class_types.GetTypeAtIndex(0)); + } + else if (num_matches > 1) + { + size_t i; + if (log) + { + for (i = 0; i < num_matches; i++) + { + type_sp = class_types.GetTypeAtIndex(i); + if (type_sp) + { + if (log) + log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has multiple matching dynamic types: uid={0x%" PRIx64 "}, type-name='%s'\n", + original_ptr, + in_value.GetTypeName().AsCString(), + type_sp->GetID(), + type_sp->GetName().GetCString()); + } + } + } + + for (i = 0; i < num_matches; i++) + { + type_sp = class_types.GetTypeAtIndex(i); + if (type_sp) + { + if (type_sp->GetClangFullType().IsCXXClassType()) + { + if (log) + log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has multiple matching dynamic types, picking this one: uid={0x%" PRIx64 "}, type-name='%s'\n", + original_ptr, + in_value.GetTypeName().AsCString(), + type_sp->GetID(), + type_sp->GetName().GetCString()); + class_type_or_name.SetTypeSP(type_sp); + break; + } + } + } + + if (i == num_matches) + { + if (log) + log->Printf ("0x%16.16" PRIx64 ": static-type = '%s' has multiple matching dynamic types, didn't find a C++ match\n", + original_ptr, + in_value.GetTypeName().AsCString()); + return false; + } + } + + // There can only be one type with a given name, + // so we've just found duplicate definitions, and this + // one will do as well as any other. + // We don't consider something to have a dynamic type if + // it is the same as the static type. So compare against + // the value we were handed. + if (type_sp) + { + if (ClangASTContext::AreTypesSame (in_value.GetClangType(), + type_sp->GetClangFullType())) + { + // The dynamic type we found was the same type, + // so we don't have a dynamic type here... + return false; + } + + // The offset_to_top is two pointers above the address. + Address offset_to_top_address = address_point_address; + int64_t slide = -2 * ((int64_t) target->GetArchitecture().GetAddressByteSize()); + offset_to_top_address.Slide (slide); + + Error error; + lldb::addr_t offset_to_top_location = offset_to_top_address.GetLoadAddress(target); + + size_t bytes_read = process->ReadMemory (offset_to_top_location, + memory_buffer, + address_byte_size, + error); + + if (!error.Success() || (bytes_read != address_byte_size)) + { + return false; + } + + offset = 0; + int64_t offset_to_top = data.GetMaxS64(&offset, process->GetAddressByteSize()); + + // So the dynamic type is a value that starts at offset_to_top + // above the original address. + lldb::addr_t dynamic_addr = original_ptr + offset_to_top; + if (!target->GetSectionLoadList().ResolveLoadAddress (dynamic_addr, dynamic_address)) + { + dynamic_address.SetRawAddress(dynamic_addr); + } + return true; + } + } + } + } + } + } + + return class_type_or_name.IsEmpty() == false; +} + +bool +ItaniumABILanguageRuntime::IsVTableName (const char *name) +{ + if (name == NULL) + return false; + + // Can we maybe ask Clang about this? + if (strstr (name, "_vptr$") == name) + return true; + else + return false; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +LanguageRuntime * +ItaniumABILanguageRuntime::CreateInstance (Process *process, lldb::LanguageType language) +{ + // FIXME: We have to check the process and make sure we actually know that this process supports + // the Itanium ABI. + if (language == eLanguageTypeC_plus_plus) + return new ItaniumABILanguageRuntime (process); + else + return NULL; +} + +void +ItaniumABILanguageRuntime::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "Itanium ABI for the C++ language", + CreateInstance); +} + +void +ItaniumABILanguageRuntime::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ItaniumABILanguageRuntime::GetPluginNameStatic() +{ + static ConstString g_name("itanium"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ItaniumABILanguageRuntime::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ItaniumABILanguageRuntime::GetPluginVersion() +{ + return 1; +} + +BreakpointResolverSP +ItaniumABILanguageRuntime::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp) +{ + return CreateExceptionResolver (bkpt, catch_bp, throw_bp, false); +} + +BreakpointResolverSP +ItaniumABILanguageRuntime::CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp, bool for_expressions) +{ + // One complication here is that most users DON'T want to stop at __cxa_allocate_expression, but until we can do + // anything better with predicting unwinding the expression parser does. So we have two forms of the exception + // breakpoints, one for expressions that leaves out __cxa_allocate_exception, and one that includes it. + // The SetExceptionBreakpoints does the latter, the CreateExceptionBreakpoint in the runtime the former. + static const char *g_catch_name = "__cxa_begin_catch"; + static const char *g_throw_name1 = "__cxa_throw"; + static const char *g_throw_name2 = "__cxa_rethrow"; + static const char *g_exception_throw_name = "__cxa_allocate_exception"; + std::vector<const char *> exception_names; + exception_names.reserve(4); + if (catch_bp) + exception_names.push_back(g_catch_name); + + if (throw_bp) + { + exception_names.push_back(g_throw_name1); + exception_names.push_back(g_throw_name2); + } + + if (for_expressions) + exception_names.push_back(g_exception_throw_name); + + BreakpointResolverSP resolver_sp (new BreakpointResolverName (bkpt, + exception_names.data(), + exception_names.size(), + eFunctionNameTypeBase, + eLazyBoolNo)); + + return resolver_sp; +} + + + +lldb::SearchFilterSP +ItaniumABILanguageRuntime::CreateExceptionSearchFilter () +{ + Target &target = m_process->GetTarget(); + + if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) + { + // Limit the number of modules that are searched for these breakpoints for + // Apple binaries. + FileSpecList filter_modules; + filter_modules.Append(FileSpec("libc++abi.dylib", false)); + filter_modules.Append(FileSpec("libSystem.B.dylib", false)); + return target.GetSearchFilterForModuleList(&filter_modules); + } + else + { + return LanguageRuntime::CreateExceptionSearchFilter(); + } +} + +lldb::BreakpointSP +ItaniumABILanguageRuntime::CreateExceptionBreakpoint (bool catch_bp, + bool throw_bp, + bool for_expressions, + bool is_internal) +{ + Target &target = m_process->GetTarget(); + FileSpecList filter_modules; + BreakpointResolverSP exception_resolver_sp = CreateExceptionResolver (NULL, catch_bp, throw_bp, for_expressions); + SearchFilterSP filter_sp (CreateExceptionSearchFilter ()); + return target.CreateBreakpoint (filter_sp, exception_resolver_sp, is_internal); +} + +void +ItaniumABILanguageRuntime::SetExceptionBreakpoints () +{ + if (!m_process) + return; + + const bool catch_bp = false; + const bool throw_bp = true; + const bool is_internal = true; + const bool for_expressions = true; + + // For the exception breakpoints set by the Expression parser, we'll be a little more aggressive and + // stop at exception allocation as well. + + if (m_cxx_exception_bp_sp) + { + m_cxx_exception_bp_sp->SetEnabled (true); + } + else + { + m_cxx_exception_bp_sp = CreateExceptionBreakpoint (catch_bp, throw_bp, for_expressions, is_internal); + if (m_cxx_exception_bp_sp) + m_cxx_exception_bp_sp->SetBreakpointKind("c++ exception"); + } + +} + +void +ItaniumABILanguageRuntime::ClearExceptionBreakpoints () +{ + if (!m_process) + return; + + if (m_cxx_exception_bp_sp) + { + m_cxx_exception_bp_sp->SetEnabled (false); + } +} + +bool +ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop (lldb::StopInfoSP stop_reason) +{ + if (!m_process) + return false; + + if (!stop_reason || + stop_reason->GetStopReason() != eStopReasonBreakpoint) + return false; + + uint64_t break_site_id = stop_reason->GetValue(); + return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint(break_site_id, + m_cxx_exception_bp_sp->GetID()); + +} diff --git a/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h new file mode 100644 index 000000000000..6b2c437de252 --- /dev/null +++ b/source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h @@ -0,0 +1,101 @@ +//===-- ItaniumABILanguageRuntime.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ItaniumABILanguageRuntime_h_ +#define liblldb_ItaniumABILanguageRuntime_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/CPPLanguageRuntime.h" +#include "lldb/Core/Value.h" + +namespace lldb_private { + + class ItaniumABILanguageRuntime : + public lldb_private::CPPLanguageRuntime + { + public: + ~ItaniumABILanguageRuntime() { } + + virtual bool + IsVTableName (const char *name); + + virtual bool + GetDynamicTypeAndAddress (ValueObject &in_value, + lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, + Address &address); + + virtual bool + CouldHaveDynamicValue (ValueObject &in_value); + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::LanguageRuntime * + CreateInstance (Process *process, lldb::LanguageType language); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + SetExceptionBreakpoints (); + + virtual void + ClearExceptionBreakpoints (); + + virtual bool + ExceptionBreakpointsExplainStop (lldb::StopInfoSP stop_reason); + + virtual lldb::BreakpointResolverSP + CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp); + + virtual lldb::SearchFilterSP + CreateExceptionSearchFilter (); + + protected: + + lldb::BreakpointResolverSP + CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp, bool for_expressions); + + lldb::BreakpointSP + CreateExceptionBreakpoint(bool catch_bp, + bool throw_bp, + bool for_expressions, + bool is_internal); + + private: + ItaniumABILanguageRuntime(Process *process) : lldb_private::CPPLanguageRuntime(process) { } // Call CreateInstance instead. + + lldb::BreakpointSP m_cxx_exception_bp_sp; + }; + +} // namespace lldb_private + +#endif // liblldb_ItaniumABILanguageRuntime_h_ diff --git a/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp new file mode 100644 index 000000000000..1ec5f3d733a0 --- /dev/null +++ b/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -0,0 +1,585 @@ +//===-- ObjectContainerBSDArchive.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectContainerBSDArchive.h" + +#include <ar.h> + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + + + +ObjectContainerBSDArchive::Object::Object() : + ar_name(), + ar_date(0), + ar_uid(0), + ar_gid(0), + ar_mode(0), + ar_size(0), + ar_file_offset(0), + ar_file_size(0) +{ +} + +void +ObjectContainerBSDArchive::Object::Clear() +{ + ar_name.Clear(); + ar_date = 0; + ar_uid = 0; + ar_gid = 0; + ar_mode = 0; + ar_size = 0; + ar_file_offset = 0; + ar_file_size = 0; +} + +lldb::offset_t +ObjectContainerBSDArchive::Object::Extract (const DataExtractor& data, lldb::offset_t offset) +{ + size_t ar_name_len = 0; + std::string str; + char *err; + str.assign ((const char *)data.GetData(&offset, 16), 16); + if (str.find("#1/") == 0) + { + // If the name is longer than 16 bytes, or contains an embedded space + // then it will use this format where the length of the name is + // here and the name characters are after this header. + ar_name_len = strtoul(str.c_str() + 3, &err, 10); + } + else + { + // Strip off any spaces (if the object file name contains spaces it + // will use the extended format above). + str.erase (str.find(' ')); + ar_name.SetCString(str.c_str()); + } + + str.assign ((const char *)data.GetData(&offset, 12), 12); + ar_date = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 6), 6); + ar_uid = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 6), 6); + ar_gid = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 8), 8); + ar_mode = strtoul(str.c_str(), &err, 8); + + str.assign ((const char *)data.GetData(&offset, 10), 10); + ar_size = strtoul(str.c_str(), &err, 10); + + str.assign ((const char *)data.GetData(&offset, 2), 2); + if (str == ARFMAG) + { + if (ar_name_len > 0) + { + str.assign ((const char *)data.GetData(&offset, ar_name_len), ar_name_len); + ar_name.SetCString (str.c_str()); + } + ar_file_offset = offset; + ar_file_size = ar_size - ar_name_len; + return offset; + } + return LLDB_INVALID_OFFSET; +} + +ObjectContainerBSDArchive::Archive::Archive +( + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &time, + lldb::offset_t file_offset, + lldb_private::DataExtractor &data +) : + m_arch (arch), + m_time (time), + m_file_offset (file_offset), + m_objects(), + m_data (data) +{ +} + +ObjectContainerBSDArchive::Archive::~Archive () +{ +} + +size_t +ObjectContainerBSDArchive::Archive::ParseObjects () +{ + DataExtractor &data = m_data; + std::string str; + lldb::offset_t offset = 0; + str.assign((const char *)data.GetData(&offset, SARMAG), SARMAG); + if (str == ARMAG) + { + Object obj; + do + { + offset = obj.Extract (data, offset); + if (offset == LLDB_INVALID_OFFSET) + break; + size_t obj_idx = m_objects.size(); + m_objects.push_back(obj); + // Insert all of the C strings out of order for now... + m_object_name_to_index_map.Append (obj.ar_name.GetCString(), obj_idx); + offset += obj.ar_file_size; + obj.Clear(); + } while (data.ValidOffset(offset)); + + // Now sort all of the object name pointers + m_object_name_to_index_map.Sort (); + } + return m_objects.size(); +} + +ObjectContainerBSDArchive::Object * +ObjectContainerBSDArchive::Archive::FindObject (const ConstString &object_name, const TimeValue &object_mod_time) +{ + const ObjectNameToIndexMap::Entry *match = m_object_name_to_index_map.FindFirstValueForName (object_name.GetCString()); + if (match) + { + if (object_mod_time.IsValid()) + { + const uint64_t object_date = object_mod_time.GetAsSecondsSinceJan1_1970(); + if (m_objects[match->value].ar_date == object_date) + return &m_objects[match->value]; + const ObjectNameToIndexMap::Entry *next_match = m_object_name_to_index_map.FindNextValueForName (match); + while (next_match) + { + if (m_objects[next_match->value].ar_date == object_date) + return &m_objects[next_match->value]; + next_match = m_object_name_to_index_map.FindNextValueForName (next_match); + } + } + else + { + return &m_objects[match->value]; + } + } + return NULL; +} + + +ObjectContainerBSDArchive::Archive::shared_ptr +ObjectContainerBSDArchive::Archive::FindCachedArchive (const FileSpec &file, const ArchSpec &arch, const TimeValue &time, lldb::offset_t file_offset) +{ + Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); + shared_ptr archive_sp; + Archive::Map &archive_map = Archive::GetArchiveCache (); + Archive::Map::iterator pos = archive_map.find (file); + // Don't cache a value for "archive_map.end()" below since we might + // delete an archive entry... + while (pos != archive_map.end() && pos->first == file) + { + bool match = true; + if (arch.IsValid() && pos->second->GetArchitecture().IsCompatibleMatch(arch) == false) + match = false; + else if (file_offset != LLDB_INVALID_OFFSET && pos->second->GetFileOffset() != file_offset) + match = false; + if (match) + { + if (pos->second->GetModificationTime() == time) + { + return pos->second; + } + else + { + // We have a file at the same path with the same architecture + // whose modification time doesn't match. It doesn't make sense + // for us to continue to use this BSD archive since we cache only + // the object info which consists of file time info and also the + // file offset and file size of any contianed objects. Since + // this information is now out of date, we won't get the correct + // information if we go and extract the file data, so we should + // remove the old and outdated entry. + archive_map.erase (pos); + pos = archive_map.find (file); + continue; // Continue to next iteration so we don't increment pos below... + } + } + ++pos; + } + return archive_sp; +} + +ObjectContainerBSDArchive::Archive::shared_ptr +ObjectContainerBSDArchive::Archive::ParseAndCacheArchiveForFile +( + const FileSpec &file, + const ArchSpec &arch, + const TimeValue &time, + lldb::offset_t file_offset, + DataExtractor &data +) +{ + shared_ptr archive_sp(new Archive (arch, time, file_offset, data)); + if (archive_sp) + { + const size_t num_objects = archive_sp->ParseObjects (); + if (num_objects > 0) + { + Mutex::Locker locker(Archive::GetArchiveCacheMutex ()); + Archive::GetArchiveCache().insert(std::make_pair(file, archive_sp)); + } + else + { + archive_sp.reset(); + } + } + return archive_sp; +} + +ObjectContainerBSDArchive::Archive::Map & +ObjectContainerBSDArchive::Archive::GetArchiveCache () +{ + static Archive::Map g_archive_map; + return g_archive_map; +} + +Mutex & +ObjectContainerBSDArchive::Archive::GetArchiveCacheMutex () +{ + static Mutex g_archive_map_mutex (Mutex::eMutexTypeRecursive); + return g_archive_map_mutex; +} + + +void +ObjectContainerBSDArchive::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + GetModuleSpecifications); +} + +void +ObjectContainerBSDArchive::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +ObjectContainerBSDArchive::GetPluginNameStatic() +{ + static ConstString g_name("bsd-archive"); + return g_name; +} + +const char * +ObjectContainerBSDArchive::GetPluginDescriptionStatic() +{ + return "BSD Archive object container reader."; +} + + +ObjectContainer * +ObjectContainerBSDArchive::CreateInstance +( + const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length) +{ + ConstString object_name (module_sp->GetObjectName()); + if (object_name) + { + if (data_sp) + { + // We have data, which means this is the first 512 bytes of the file + // Check to see if the magic bytes match and if they do, read the entire + // table of contents for the archive and cache it + DataExtractor data; + data.SetData (data_sp, data_offset, length); + if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) + { + Timer scoped_timer (__PRETTY_FUNCTION__, + "ObjectContainerBSDArchive::CreateInstance (module = %s, file = %p, file_offset = 0x%8.8" PRIx64 ", file_size = 0x%8.8" PRIx64 ")", + module_sp->GetFileSpec().GetPath().c_str(), + file, (uint64_t) file_offset, (uint64_t) length); + + // Map the entire .a file to be sure that we don't lose any data if the file + // gets updated by a new build while this .a file is being used for debugging + DataBufferSP archive_data_sp (file->MemoryMapFileContents(file_offset, length)); + lldb::offset_t archive_data_offset = 0; + + Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, + module_sp->GetArchitecture(), + module_sp->GetModificationTime(), + file_offset)); + std::unique_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, + archive_data_sp, + archive_data_offset, + file, + file_offset, + length)); + + if (container_ap.get()) + { + if (archive_sp) + { + // We already have this archive in our cache, use it + container_ap->SetArchive (archive_sp); + return container_ap.release(); + } + else if (container_ap->ParseHeader()) + return container_ap.release(); + } + } + } + else + { + // No data, just check for a cached archive + Archive::shared_ptr archive_sp (Archive::FindCachedArchive (*file, + module_sp->GetArchitecture(), + module_sp->GetModificationTime(), + file_offset)); + if (archive_sp) + { + std::unique_ptr<ObjectContainerBSDArchive> container_ap(new ObjectContainerBSDArchive (module_sp, data_sp, data_offset, file, file_offset, length)); + + if (container_ap.get()) + { + // We already have this archive in our cache, use it + container_ap->SetArchive (archive_sp); + return container_ap.release(); + } + } + } + } + return NULL; +} + + + +bool +ObjectContainerBSDArchive::MagicBytesMatch (const DataExtractor &data) +{ + uint32_t offset = 0; + const char* armag = (const char* )data.PeekData (offset, sizeof(ar_hdr)); + if (armag && ::strncmp(armag, ARMAG, SARMAG) == 0) + { + armag += offsetof(struct ar_hdr, ar_fmag) + SARMAG; + if (strncmp(armag, ARFMAG, 2) == 0) + return true; + } + return false; +} + +ObjectContainerBSDArchive::ObjectContainerBSDArchive +( + const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t size +) : + ObjectContainer (module_sp, file, file_offset, size, data_sp, data_offset), + m_archive_sp () +{ +} +void +ObjectContainerBSDArchive::SetArchive (Archive::shared_ptr &archive_sp) +{ + m_archive_sp = archive_sp; +} + + + +ObjectContainerBSDArchive::~ObjectContainerBSDArchive() +{ +} + +bool +ObjectContainerBSDArchive::ParseHeader () +{ + if (m_archive_sp.get() == NULL) + { + if (m_data.GetByteSize() > 0) + { + ModuleSP module_sp (GetModule()); + if (module_sp) + { + m_archive_sp = Archive::ParseAndCacheArchiveForFile (m_file, + module_sp->GetArchitecture(), + module_sp->GetModificationTime(), + m_offset, + m_data); + } + // Clear the m_data that contains the entire archive + // data and let our m_archive_sp hold onto the data. + m_data.Clear(); + } + } + return m_archive_sp.get() != NULL; +} + +void +ObjectContainerBSDArchive::Dump (Stream *s) const +{ + s->Printf("%p: ", this); + s->Indent(); + const size_t num_archs = GetNumArchitectures(); + const size_t num_objects = GetNumObjects(); + s->Printf("ObjectContainerBSDArchive, num_archs = %lu, num_objects = %lu", num_archs, num_objects); + uint32_t i; + ArchSpec arch; + s->IndentMore(); + for (i=0; i<num_archs; i++) + { + s->Indent(); + GetArchitectureAtIndex(i, arch); + s->Printf("arch[%u] = %s\n", i, arch.GetArchitectureName()); + } + for (i=0; i<num_objects; i++) + { + s->Indent(); + s->Printf("object[%u] = %s\n", i, GetObjectNameAtIndex (i)); + } + s->IndentLess(); + s->EOL(); +} + +ObjectFileSP +ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file) +{ + ModuleSP module_sp (GetModule()); + if (module_sp) + { + if (module_sp->GetObjectName() && m_archive_sp) + { + Object *object = m_archive_sp->FindObject (module_sp->GetObjectName(), + module_sp->GetObjectModificationTime()); + if (object) + { + lldb::offset_t data_offset = object->ar_file_offset; + return ObjectFile::FindPlugin (module_sp, + file, + m_offset + object->ar_file_offset, + object->ar_file_size, + m_archive_sp->GetData().GetSharedDataBuffer(), + data_offset); + } + } + } + return ObjectFileSP(); +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ObjectContainerBSDArchive::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectContainerBSDArchive::GetPluginVersion() +{ + return 1; +} + + +size_t +ObjectContainerBSDArchive::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t file_size, + lldb_private::ModuleSpecList &specs) +{ + + // We have data, which means this is the first 512 bytes of the file + // Check to see if the magic bytes match and if they do, read the entire + // table of contents for the archive and cache it + DataExtractor data; + data.SetData (data_sp, data_offset, data_sp->GetByteSize()); + if (file && data_sp && ObjectContainerBSDArchive::MagicBytesMatch(data)) + { + const size_t initial_count = specs.GetSize(); + TimeValue file_mod_time = file.GetModificationTime(); + Archive::shared_ptr archive_sp (Archive::FindCachedArchive (file, ArchSpec(), file_mod_time, file_offset)); + bool set_archive_arch = false; + if (!archive_sp) + { + set_archive_arch = true; + DataBufferSP data_sp (file.MemoryMapFileContents(file_offset, file_size)); + data.SetData (data_sp, 0, data_sp->GetByteSize()); + archive_sp = Archive::ParseAndCacheArchiveForFile(file, ArchSpec(), file_mod_time, file_offset, data); + } + + if (archive_sp) + { + const size_t num_objects = archive_sp->GetNumObjects(); + for (size_t idx = 0; idx < num_objects; ++idx) + { + const Object *object = archive_sp->GetObjectAtIndex (idx); + if (object) + { + const lldb::offset_t object_file_offset = file_offset + object->ar_file_offset; + if (object->ar_file_offset < file_size && file_size > object_file_offset) + { + if (ObjectFile::GetModuleSpecifications(file, + object_file_offset, + file_size - object_file_offset, + specs)) + { + ModuleSpec &spec = specs.GetModuleSpecRefAtIndex (specs.GetSize() - 1); + TimeValue object_mod_time; + object_mod_time.OffsetWithSeconds(object->ar_date); + spec.GetObjectName () = object->ar_name; + spec.SetObjectOffset(object_file_offset); + spec.GetObjectModificationTime () = object_mod_time; + } + } + } + } + } + const size_t end_count = specs.GetSize(); + size_t num_specs_added = end_count - initial_count; + if (set_archive_arch && num_specs_added > 0) + { + // The archive was created but we didn't have an architecture + // so we need to set it + for (size_t i=initial_count; i<end_count; ++ i) + { + ModuleSpec module_spec; + if (specs.GetModuleSpecAtIndex(i, module_spec)) + { + if (module_spec.GetArchitecture().IsValid()) + { + archive_sp->SetArchitecture (module_spec.GetArchitecture()); + break; + } + } + } + } + return num_specs_added; + } + return 0; +} diff --git a/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h b/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h new file mode 100644 index 000000000000..8093c580ff97 --- /dev/null +++ b/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h @@ -0,0 +1,229 @@ +//===-- ObjectContainerBSDArchive.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectContainerBSDArchive_h_ +#define liblldb_ObjectContainerBSDArchive_h_ + +#include "lldb/Symbol/ObjectContainer.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Host/TimeValue.h" + +class ObjectContainerBSDArchive : + public lldb_private::ObjectContainer +{ +public: + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectContainer * + CreateInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, + lldb::offset_t length); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool + MagicBytesMatch (const lldb_private::DataExtractor &data); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectContainerBSDArchive (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec *file, + lldb::offset_t offset, + lldb::offset_t length); + + virtual + ~ObjectContainerBSDArchive(); + + virtual bool + ParseHeader (); + + virtual size_t + GetNumObjects () const + { + if (m_archive_sp) + return m_archive_sp->GetNumObjects(); + return 0; + } + virtual void + Dump (lldb_private::Stream *s) const; + + virtual lldb::ObjectFileSP + GetObjectFile (const lldb_private::FileSpec *file); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + + struct Object + { + Object(); + + void + Clear(); + + lldb::offset_t + Extract (const lldb_private::DataExtractor& data, lldb::offset_t offset); + + lldb_private::ConstString ar_name; // name + uint32_t ar_date; // modification time + uint16_t ar_uid; // user id + uint16_t ar_gid; // group id + uint16_t ar_mode; // octal file permissions + uint32_t ar_size; // size in bytes + lldb::offset_t ar_file_offset; // file offset in bytes from the beginning of the file of the object data + lldb::offset_t ar_file_size; // length of the object data + + typedef std::vector<Object> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + }; + + class Archive + { + public: + typedef std::shared_ptr<Archive> shared_ptr; + typedef std::multimap<lldb_private::FileSpec, shared_ptr> Map; + + static Map & + GetArchiveCache (); + + static lldb_private::Mutex & + GetArchiveCacheMutex (); + + static Archive::shared_ptr + FindCachedArchive (const lldb_private::FileSpec &file, + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time, + lldb::offset_t file_offset); + + static Archive::shared_ptr + ParseAndCacheArchiveForFile (const lldb_private::FileSpec &file, + const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time, + lldb::offset_t file_offset, + lldb_private::DataExtractor &data); + + Archive (const lldb_private::ArchSpec &arch, + const lldb_private::TimeValue &mod_time, + lldb::offset_t file_offset, + lldb_private::DataExtractor &data); + + ~Archive (); + + size_t + GetNumObjects () const + { + return m_objects.size(); + } + + const Object * + GetObjectAtIndex (size_t idx) + { + if (idx < m_objects.size()) + return &m_objects[idx]; + return NULL; + } + + size_t + ParseObjects (); + + Object * + FindObject (const lldb_private::ConstString &object_name, + const lldb_private::TimeValue &object_mod_time); + + lldb::offset_t + GetFileOffset () const + { + return m_file_offset; + } + + const lldb_private::TimeValue & + GetModificationTime() + { + return m_time; + } + + const lldb_private::ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + void + SetArchitecture (const lldb_private::ArchSpec &arch) + { + m_arch = arch; + } + + bool + HasNoExternalReferences() const; + + lldb_private::DataExtractor & + GetData () + { + return m_data; + } + + protected: + typedef lldb_private::UniqueCStringMap<uint32_t> ObjectNameToIndexMap; + //---------------------------------------------------------------------- + // Member Variables + //---------------------------------------------------------------------- + lldb_private::ArchSpec m_arch; + lldb_private::TimeValue m_time; + lldb::offset_t m_file_offset; + Object::collection m_objects; + ObjectNameToIndexMap m_object_name_to_index_map; + lldb_private::DataExtractor m_data; ///< The data for this object container so we don't lose data if the .a files gets modified + }; + + void + SetArchive (Archive::shared_ptr &archive_sp); + + Archive::shared_ptr m_archive_sp; +}; + +#endif // liblldb_ObjectContainerBSDArchive_h_ diff --git a/source/Plugins/ObjectFile/ELF/ELFHeader.cpp b/source/Plugins/ObjectFile/ELF/ELFHeader.cpp new file mode 100644 index 000000000000..a63a01d7ed7a --- /dev/null +++ b/source/Plugins/ObjectFile/ELF/ELFHeader.cpp @@ -0,0 +1,465 @@ +//===-- ELFHeader.cpp ----------------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <cstring> + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" + +#include "ELFHeader.h" + +using namespace elf; +using namespace lldb; +using namespace llvm::ELF; + +//------------------------------------------------------------------------------ +// Static utility functions. +// +// GetMaxU64 and GetMaxS64 wrap the similarly named methods from DataExtractor +// with error handling code and provide for parsing a sequence of values. +static bool +GetMaxU64(const lldb_private::DataExtractor &data, + lldb::offset_t *offset, + uint64_t *value, + uint32_t byte_size) +{ + const lldb::offset_t saved_offset = *offset; + *value = data.GetMaxU64(offset, byte_size); + return *offset != saved_offset; +} + +static bool +GetMaxU64(const lldb_private::DataExtractor &data, + lldb::offset_t *offset, + uint64_t *value, + uint32_t byte_size, + uint32_t count) +{ + lldb::offset_t saved_offset = *offset; + + for (uint32_t i = 0; i < count; ++i, ++value) + { + if (GetMaxU64(data, offset, value, byte_size) == false) + { + *offset = saved_offset; + return false; + } + } + return true; +} + +static bool +GetMaxS64(const lldb_private::DataExtractor &data, + lldb::offset_t *offset, + int64_t *value, + uint32_t byte_size) +{ + const lldb::offset_t saved_offset = *offset; + *value = data.GetMaxS64(offset, byte_size); + return *offset != saved_offset; +} + +static bool +GetMaxS64(const lldb_private::DataExtractor &data, + lldb::offset_t *offset, + int64_t *value, + uint32_t byte_size, + uint32_t count) +{ + lldb::offset_t saved_offset = *offset; + + for (uint32_t i = 0; i < count; ++i, ++value) + { + if (GetMaxS64(data, offset, value, byte_size) == false) + { + *offset = saved_offset; + return false; + } + } + return true; +} + +//------------------------------------------------------------------------------ +// ELFHeader + +ELFHeader::ELFHeader() +{ + memset(this, 0, sizeof(ELFHeader)); +} + +ByteOrder +ELFHeader::GetByteOrder() const +{ + if (e_ident[EI_DATA] == ELFDATA2MSB) + return eByteOrderBig; + if (e_ident[EI_DATA] == ELFDATA2LSB) + return eByteOrderLittle; + return eByteOrderInvalid; +} + +bool +ELFHeader::Parse(lldb_private::DataExtractor &data, lldb::offset_t *offset) +{ + // Read e_ident. This provides byte order and address size info. + if (data.GetU8(offset, &e_ident, EI_NIDENT) == NULL) + return false; + + const unsigned byte_size = Is32Bit() ? 4 : 8; + data.SetByteOrder(GetByteOrder()); + data.SetAddressByteSize(byte_size); + + // Read e_type and e_machine. + if (data.GetU16(offset, &e_type, 2) == NULL) + return false; + + // Read e_version. + if (data.GetU32(offset, &e_version, 1) == NULL) + return false; + + // Read e_entry, e_phoff and e_shoff. + if (GetMaxU64(data, offset, &e_entry, byte_size, 3) == false) + return false; + + // Read e_flags. + if (data.GetU32(offset, &e_flags, 1) == NULL) + return false; + + // Read e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum and + // e_shstrndx. + if (data.GetU16(offset, &e_ehsize, 6) == NULL) + return false; + + return true; +} + +bool +ELFHeader::MagicBytesMatch(const uint8_t *magic) +{ + return memcmp(magic, ElfMagic, strlen(ElfMagic)) == 0; +} + +unsigned +ELFHeader::AddressSizeInBytes(const uint8_t *magic) +{ + unsigned address_size = 0; + + switch (magic[EI_CLASS]) + { + case ELFCLASS32: + address_size = 4; + break; + + case ELFCLASS64: + address_size = 8; + break; + } + return address_size; +} + +unsigned +ELFHeader::GetRelocationJumpSlotType() const +{ + unsigned slot = 0; + + switch (e_machine) + { + default: + assert(false && "architecture not supported"); + break; + case EM_386: + case EM_486: + slot = R_386_JUMP_SLOT; + break; + case EM_X86_64: + slot = R_X86_64_JUMP_SLOT; + break; + case EM_ARM: + slot = R_ARM_JUMP_SLOT; + break; + } + + return slot; +} + +//------------------------------------------------------------------------------ +// ELFSectionHeader + +ELFSectionHeader::ELFSectionHeader() +{ + memset(this, 0, sizeof(ELFSectionHeader)); +} + +bool +ELFSectionHeader::Parse(const lldb_private::DataExtractor &data, + lldb::offset_t *offset) +{ + const unsigned byte_size = data.GetAddressByteSize(); + + // Read sh_name and sh_type. + if (data.GetU32(offset, &sh_name, 2) == NULL) + return false; + + // Read sh_flags. + if (GetMaxU64(data, offset, &sh_flags, byte_size) == false) + return false; + + // Read sh_addr, sh_off and sh_size. + if (GetMaxU64(data, offset, &sh_addr, byte_size, 3) == false) + return false; + + // Read sh_link and sh_info. + if (data.GetU32(offset, &sh_link, 2) == NULL) + return false; + + // Read sh_addralign and sh_entsize. + if (GetMaxU64(data, offset, &sh_addralign, byte_size, 2) == false) + return false; + + return true; +} + +//------------------------------------------------------------------------------ +// ELFSymbol + +ELFSymbol::ELFSymbol() +{ + memset(this, 0, sizeof(ELFSymbol)); +} + +#define ENUM_TO_CSTR(e) case e: return #e + +const char * +ELFSymbol::bindingToCString(unsigned char binding) +{ + switch (binding) + { + ENUM_TO_CSTR(STB_LOCAL); + ENUM_TO_CSTR(STB_GLOBAL); + ENUM_TO_CSTR(STB_WEAK); + ENUM_TO_CSTR(STB_LOOS); + ENUM_TO_CSTR(STB_HIOS); + ENUM_TO_CSTR(STB_LOPROC); + ENUM_TO_CSTR(STB_HIPROC); + } + return ""; +} + +const char * +ELFSymbol::typeToCString(unsigned char type) +{ + switch (type) + { + ENUM_TO_CSTR(STT_NOTYPE); + ENUM_TO_CSTR(STT_OBJECT); + ENUM_TO_CSTR(STT_FUNC); + ENUM_TO_CSTR(STT_SECTION); + ENUM_TO_CSTR(STT_FILE); + ENUM_TO_CSTR(STT_COMMON); + ENUM_TO_CSTR(STT_TLS); + ENUM_TO_CSTR(STT_LOOS); + ENUM_TO_CSTR(STT_HIOS); + ENUM_TO_CSTR(STT_GNU_IFUNC); + ENUM_TO_CSTR(STT_LOPROC); + ENUM_TO_CSTR(STT_HIPROC); + } + return ""; +} + +const char * +ELFSymbol::sectionIndexToCString (elf_half shndx, + const lldb_private::SectionList *section_list) +{ + switch (shndx) + { + ENUM_TO_CSTR(SHN_UNDEF); + ENUM_TO_CSTR(SHN_LOPROC); + ENUM_TO_CSTR(SHN_HIPROC); + ENUM_TO_CSTR(SHN_LOOS); + ENUM_TO_CSTR(SHN_HIOS); + ENUM_TO_CSTR(SHN_ABS); + ENUM_TO_CSTR(SHN_COMMON); + ENUM_TO_CSTR(SHN_XINDEX); + default: + { + const lldb_private::Section *section = section_list->GetSectionAtIndex(shndx).get(); + if (section) + return section->GetName().AsCString(""); + } + break; + } + return ""; +} + +void +ELFSymbol::Dump (lldb_private::Stream *s, + uint32_t idx, + const lldb_private::DataExtractor *strtab_data, + const lldb_private::SectionList *section_list) +{ + s->Printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 " 0x%8.8x 0x%2.2x (%-10s %-13s) 0x%2.2x 0x%4.4x (%-10s) %s\n", + idx, + st_value, + st_size, + st_name, + st_info, + bindingToCString (getBinding()), + typeToCString (getType()), + st_other, + st_shndx, + sectionIndexToCString (st_shndx, section_list), + strtab_data ? strtab_data->PeekCStr(st_name) : ""); +} + +bool +ELFSymbol::Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset) +{ + const unsigned byte_size = data.GetAddressByteSize(); + const bool parsing_32 = byte_size == 4; + + // Read st_name. + if (data.GetU32(offset, &st_name, 1) == NULL) + return false; + + if (parsing_32) + { + // Read st_value and st_size. + if (GetMaxU64(data, offset, &st_value, byte_size, 2) == false) + return false; + + // Read st_info and st_other. + if (data.GetU8(offset, &st_info, 2) == NULL) + return false; + + // Read st_shndx. + if (data.GetU16(offset, &st_shndx, 1) == NULL) + return false; + } + else + { + // Read st_info and st_other. + if (data.GetU8(offset, &st_info, 2) == NULL) + return false; + + // Read st_shndx. + if (data.GetU16(offset, &st_shndx, 1) == NULL) + return false; + + // Read st_value and st_size. + if (data.GetU64(offset, &st_value, 2) == NULL) + return false; + } + return true; +} + +//------------------------------------------------------------------------------ +// ELFProgramHeader + +ELFProgramHeader::ELFProgramHeader() +{ + memset(this, 0, sizeof(ELFProgramHeader)); +} + +bool +ELFProgramHeader::Parse(const lldb_private::DataExtractor &data, + lldb::offset_t *offset) +{ + const uint32_t byte_size = data.GetAddressByteSize(); + const bool parsing_32 = byte_size == 4; + + // Read p_type; + if (data.GetU32(offset, &p_type, 1) == NULL) + return false; + + if (parsing_32) { + // Read p_offset, p_vaddr, p_paddr, p_filesz and p_memsz. + if (GetMaxU64(data, offset, &p_offset, byte_size, 5) == false) + return false; + + // Read p_flags. + if (data.GetU32(offset, &p_flags, 1) == NULL) + return false; + + // Read p_align. + if (GetMaxU64(data, offset, &p_align, byte_size) == false) + return false; + } + else { + // Read p_flags. + if (data.GetU32(offset, &p_flags, 1) == NULL) + return false; + + // Read p_offset, p_vaddr, p_paddr, p_filesz, p_memsz and p_align. + if (GetMaxU64(data, offset, &p_offset, byte_size, 6) == false) + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +// ELFDynamic + +ELFDynamic::ELFDynamic() +{ + memset(this, 0, sizeof(ELFDynamic)); +} + +bool +ELFDynamic::Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset) +{ + const unsigned byte_size = data.GetAddressByteSize(); + return GetMaxS64(data, offset, &d_tag, byte_size, 2); +} + +//------------------------------------------------------------------------------ +// ELFRel + +ELFRel::ELFRel() +{ + memset(this, 0, sizeof(ELFRel)); +} + +bool +ELFRel::Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset) +{ + const unsigned byte_size = data.GetAddressByteSize(); + + // Read r_offset and r_info. + if (GetMaxU64(data, offset, &r_offset, byte_size, 2) == false) + return false; + + return true; +} + +//------------------------------------------------------------------------------ +// ELFRela + +ELFRela::ELFRela() +{ + memset(this, 0, sizeof(ELFRela)); +} + +bool +ELFRela::Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset) +{ + const unsigned byte_size = data.GetAddressByteSize(); + + // Read r_offset and r_info. + if (GetMaxU64(data, offset, &r_offset, byte_size, 2) == false) + return false; + + // Read r_addend; + if (GetMaxS64(data, offset, &r_addend, byte_size) == false) + return false; + + return true; +} + + diff --git a/source/Plugins/ObjectFile/ELF/ELFHeader.h b/source/Plugins/ObjectFile/ELF/ELFHeader.h new file mode 100644 index 000000000000..aa2c16b6168c --- /dev/null +++ b/source/Plugins/ObjectFile/ELF/ELFHeader.h @@ -0,0 +1,433 @@ +//===-- ELFHeader.h ------------------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// @file +/// @brief Generic structures and typedefs for ELF files. +/// +/// This file provides definitions for the various entities comprising an ELF +/// file. The structures are generic in the sense that they do not correspond +/// to the exact binary layout of an ELF, but can be used to hold the +/// information present in both 32 and 64 bit variants of the format. Each +/// entity provides a \c Parse method which is capable of transparently reading +/// both 32 and 64 bit instances of the object. +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ELFHeader_h_ +#define liblldb_ELFHeader_h_ + +#include "llvm/Support/ELF.h" + +#include "lldb/lldb-enumerations.h" + +namespace lldb_private +{ +class DataExtractor; +} // End namespace lldb_private. + +namespace elf +{ + +//------------------------------------------------------------------------------ +/// @name ELF type definitions. +/// +/// Types used to represent the various components of ELF structures. All types +/// are signed or unsigned integral types wide enough to hold values from both +/// 32 and 64 bit ELF variants. +//@{ +typedef uint64_t elf_addr; +typedef uint64_t elf_off; +typedef uint16_t elf_half; +typedef uint32_t elf_word; +typedef int32_t elf_sword; +typedef uint64_t elf_size; +typedef uint64_t elf_xword; +typedef int64_t elf_sxword; +//@} + +//------------------------------------------------------------------------------ +/// @class ELFHeader +/// @brief Generic representation of an ELF file header. +/// +/// This object is used to identify the general attributes on an ELF file and to +/// locate additional sections within the file. +struct ELFHeader +{ + unsigned char e_ident[llvm::ELF::EI_NIDENT]; ///< ELF file identification. + elf_addr e_entry; ///< Virtual address program entry point. + elf_off e_phoff; ///< File offset of program header table. + elf_off e_shoff; ///< File offset of section header table. + elf_word e_flags; ///< Processor specific flags. + elf_word e_version; ///< Version of object file (always 1). + elf_half e_type; ///< Object file type. + elf_half e_machine; ///< Target architecture. + elf_half e_ehsize; ///< Byte size of the ELF header. + elf_half e_phentsize; ///< Size of a program header table entry. + elf_half e_phnum; ///< Number of program header entries. + elf_half e_shentsize; ///< Size of a section header table entry. + elf_half e_shnum; ///< Number of section header entries. + elf_half e_shstrndx; ///< String table section index. + + ELFHeader(); + + //-------------------------------------------------------------------------- + /// Returns true if this is a 32 bit ELF file header. + /// + /// @return + /// True if this is a 32 bit ELF file header. + bool Is32Bit() const { + return e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS32; + } + + //-------------------------------------------------------------------------- + /// Returns true if this is a 64 bit ELF file header. + /// + /// @return + /// True if this is a 64 bit ELF file header. + bool Is64Bit() const { + return e_ident[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS64; + } + + //-------------------------------------------------------------------------- + /// The byte order of this ELF file header. + /// + /// @return + /// The byte order of this ELF file as described by the header. + lldb::ByteOrder + GetByteOrder() const; + + //-------------------------------------------------------------------------- + /// The jump slot relocation type of this ELF. + unsigned + GetRelocationJumpSlotType() const; + + //-------------------------------------------------------------------------- + /// Parse an ELFHeader entry starting at position \p offset and + /// update the data extractor with the address size and byte order + /// attributes as defined by the header. + /// + /// @param[in,out] data + /// The DataExtractor to read from. Updated with the address size and + /// byte order attributes appropriate to this header. + /// + /// @param[in,out] offset + /// Pointer to an offset in the data. On return the offset will be + /// advanced by the number of bytes read. + /// + /// @return + /// True if the ELFHeader was successfully read and false + /// otherwise. + bool + Parse(lldb_private::DataExtractor &data, lldb::offset_t *offset); + + //-------------------------------------------------------------------------- + /// Examines at most EI_NIDENT bytes starting from the given pointer and + /// determines if the magic ELF identification exists. + /// + /// @return + /// True if the given sequence of bytes identifies an ELF file. + static bool + MagicBytesMatch(const uint8_t *magic); + + //-------------------------------------------------------------------------- + /// Examines at most EI_NIDENT bytes starting from the given address and + /// determines the address size of the underlying ELF file. This function + /// should only be called on an pointer for which MagicBytesMatch returns + /// true. + /// + /// @return + /// The number of bytes forming an address in the ELF file (either 4 or + /// 8), else zero if the address size could not be determined. + static unsigned + AddressSizeInBytes(const uint8_t *magic); +}; + +//------------------------------------------------------------------------------ +/// @class ELFSectionHeader +/// @brief Generic representation of an ELF section header. +struct ELFSectionHeader +{ + elf_word sh_name; ///< Section name string index. + elf_word sh_type; ///< Section type. + elf_xword sh_flags; ///< Section attributes. + elf_addr sh_addr; ///< Virtual address of the section in memory. + elf_off sh_offset; ///< Start of section from beginning of file. + elf_xword sh_size; ///< Number of bytes occupied in the file. + elf_word sh_link; ///< Index of associated section. + elf_word sh_info; ///< Extra section info (overloaded). + elf_xword sh_addralign; ///< Power of two alignment constraint. + elf_xword sh_entsize; ///< Byte size of each section entry. + + ELFSectionHeader(); + + //-------------------------------------------------------------------------- + /// Parse an ELFSectionHeader entry from the given DataExtracter starting at + /// position \p offset. + /// + /// @param[in] data + /// The DataExtractor to read from. The address size of the extractor + /// determines if a 32 or 64 bit object should be read. + /// + /// @param[in,out] offset + /// Pointer to an offset in the data. On return the offset will be + /// advanced by the number of bytes read. + /// + /// @return + /// True if the ELFSectionHeader was successfully read and false + /// otherwise. + bool + Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); +}; + +//------------------------------------------------------------------------------ +/// @class ELFProgramHeader +/// @brief Generic representation of an ELF program header. +struct ELFProgramHeader +{ + elf_word p_type; ///< Type of program segment. + elf_word p_flags; ///< Segment attributes. + elf_off p_offset; ///< Start of segment from beginning of file. + elf_addr p_vaddr; ///< Virtual address of segment in memory. + elf_addr p_paddr; ///< Physical address (for non-VM systems). + elf_xword p_filesz; ///< Byte size of the segment in file. + elf_xword p_memsz; ///< Byte size of the segment in memory. + elf_xword p_align; ///< Segment alignment constraint. + + ELFProgramHeader(); + + /// Parse an ELFProgramHeader entry from the given DataExtractor starting at + /// position \p offset. The address size of the DataExtractor determines if + /// a 32 or 64 bit object is to be parsed. + /// + /// @param[in] data + /// The DataExtractor to read from. The address size of the extractor + /// determines if a 32 or 64 bit object should be read. + /// + /// @param[in,out] offset + /// Pointer to an offset in the data. On return the offset will be + /// advanced by the number of bytes read. + /// + /// @return + /// True if the ELFProgramHeader was successfully read and false + /// otherwise. + bool + Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); +}; + +//------------------------------------------------------------------------------ +/// @class ELFSymbol +/// @brief Represents a symbol within an ELF symbol table. +struct ELFSymbol +{ + elf_addr st_value; ///< Absolute or relocatable address. + elf_xword st_size; ///< Size of the symbol or zero. + elf_word st_name; ///< Symbol name string index. + unsigned char st_info; ///< Symbol type and binding attributes. + unsigned char st_other; ///< Reserved for future use. + elf_half st_shndx; ///< Section to which this symbol applies. + + ELFSymbol(); + + /// Returns the binding attribute of the st_info member. + unsigned char getBinding() const { return st_info >> 4; } + + /// Returns the type attribute of the st_info member. + unsigned char getType() const { return st_info & 0x0F; } + + /// Sets the binding and type of the st_info member. + void setBindingAndType(unsigned char binding, unsigned char type) { + st_info = (binding << 4) + (type & 0x0F); + } + + static const char * + bindingToCString(unsigned char binding); + + static const char * + typeToCString(unsigned char type); + + static const char * + sectionIndexToCString(elf_half shndx, + const lldb_private::SectionList *section_list); + + /// Parse an ELFSymbol entry from the given DataExtractor starting at + /// position \p offset. The address size of the DataExtractor determines if + /// a 32 or 64 bit object is to be parsed. + /// + /// @param[in] data + /// The DataExtractor to read from. The address size of the extractor + /// determines if a 32 or 64 bit object should be read. + /// + /// @param[in,out] offset + /// Pointer to an offset in the data. On return the offset will be + /// advanced by the number of bytes read. + /// + /// @return + /// True if the ELFSymbol was successfully read and false otherwise. + bool + Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); + + void + Dump (lldb_private::Stream *s, + uint32_t idx, + const lldb_private::DataExtractor *strtab_data, + const lldb_private::SectionList *section_list); +}; + +//------------------------------------------------------------------------------ +/// @class ELFDynamic +/// @brief Represents an entry in an ELF dynamic table. +struct ELFDynamic +{ + elf_sxword d_tag; ///< Type of dynamic table entry. + union + { + elf_xword d_val; ///< Integer value of the table entry. + elf_addr d_ptr; ///< Pointer value of the table entry. + }; + + ELFDynamic(); + + /// Parse an ELFDynamic entry from the given DataExtractor starting at + /// position \p offset. The address size of the DataExtractor determines if + /// a 32 or 64 bit object is to be parsed. + /// + /// @param[in] data + /// The DataExtractor to read from. The address size of the extractor + /// determines if a 32 or 64 bit object should be read. + /// + /// @param[in,out] offset + /// Pointer to an offset in the data. On return the offset will be + /// advanced by the number of bytes read. + /// + /// @return + /// True if the ELFDynamic entry was successfully read and false + /// otherwise. + bool + Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); +}; + +//------------------------------------------------------------------------------ +/// @class ELFRel +/// @brief Represents a relocation entry with an implicit addend. +struct ELFRel +{ + elf_addr r_offset; ///< Address of reference. + elf_xword r_info; ///< symbol index and type of relocation. + + ELFRel(); + + /// Parse an ELFRel entry from the given DataExtractor starting at position + /// \p offset. The address size of the DataExtractor determines if a 32 or + /// 64 bit object is to be parsed. + /// + /// @param[in] data + /// The DataExtractor to read from. The address size of the extractor + /// determines if a 32 or 64 bit object should be read. + /// + /// @param[in,out] offset + /// Pointer to an offset in the data. On return the offset will be + /// advanced by the number of bytes read. + /// + /// @return + /// True if the ELFRel entry was successfully read and false otherwise. + bool + Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); + + /// Returns the type when the given entry represents a 32-bit relocation. + static unsigned + RelocType32(const ELFRel &rel) + { + return rel.r_info & 0x0ff; + } + + /// Returns the type when the given entry represents a 64-bit relocation. + static unsigned + RelocType64(const ELFRel &rel) + { + return rel.r_info & 0xffffffff; + } + + /// Returns the symbol index when the given entry represents a 32-bit + /// reloction. + static unsigned + RelocSymbol32(const ELFRel &rel) + { + return rel.r_info >> 8; + } + + /// Returns the symbol index when the given entry represents a 64-bit + /// reloction. + static unsigned + RelocSymbol64(const ELFRel &rel) + { + return rel.r_info >> 32; + } +}; + +//------------------------------------------------------------------------------ +/// @class ELFRela +/// @brief Represents a relocation entry with an explicit addend. +struct ELFRela +{ + elf_addr r_offset; ///< Address of reference. + elf_xword r_info; ///< Symbol index and type of relocation. + elf_sxword r_addend; ///< Constant part of expression. + + ELFRela(); + + /// Parse an ELFRela entry from the given DataExtractor starting at position + /// \p offset. The address size of the DataExtractor determines if a 32 or + /// 64 bit object is to be parsed. + /// + /// @param[in] data + /// The DataExtractor to read from. The address size of the extractor + /// determines if a 32 or 64 bit object should be read. + /// + /// @param[in,out] offset + /// Pointer to an offset in the data. On return the offset will be + /// advanced by the number of bytes read. + /// + /// @return + /// True if the ELFRela entry was successfully read and false otherwise. + bool + Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); + + /// Returns the type when the given entry represents a 32-bit relocation. + static unsigned + RelocType32(const ELFRela &rela) + { + return rela.r_info & 0x0ff; + } + + /// Returns the type when the given entry represents a 64-bit relocation. + static unsigned + RelocType64(const ELFRela &rela) + { + return rela.r_info & 0xffffffff; + } + + /// Returns the symbol index when the given entry represents a 32-bit + /// reloction. + static unsigned + RelocSymbol32(const ELFRela &rela) + { + return rela.r_info >> 8; + } + + /// Returns the symbol index when the given entry represents a 64-bit + /// reloction. + static unsigned + RelocSymbol64(const ELFRela &rela) + { + return rela.r_info >> 32; + } +}; + +} // End namespace elf. + +#endif // #ifndef liblldb_ELFHeader_h_ diff --git a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp new file mode 100644 index 000000000000..2e9f6903280c --- /dev/null +++ b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -0,0 +1,1893 @@ +//===-- ObjectFileELF.cpp ------------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileELF.h" + +#include <cassert> +#include <algorithm> + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Host/Host.h" + +#include "llvm/ADT/PointerUnion.h" + +#define CASE_AND_STREAM(s, def, width) \ + case def: s->Printf("%-*s", width, #def); break; + +using namespace lldb; +using namespace lldb_private; +using namespace elf; +using namespace llvm::ELF; + +namespace { +//===----------------------------------------------------------------------===// +/// @class ELFRelocation +/// @brief Generic wrapper for ELFRel and ELFRela. +/// +/// This helper class allows us to parse both ELFRel and ELFRela relocation +/// entries in a generic manner. +class ELFRelocation +{ +public: + + /// Constructs an ELFRelocation entry with a personality as given by @p + /// type. + /// + /// @param type Either DT_REL or DT_RELA. Any other value is invalid. + ELFRelocation(unsigned type); + + ~ELFRelocation(); + + bool + Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); + + static unsigned + RelocType32(const ELFRelocation &rel); + + static unsigned + RelocType64(const ELFRelocation &rel); + + static unsigned + RelocSymbol32(const ELFRelocation &rel); + + static unsigned + RelocSymbol64(const ELFRelocation &rel); + +private: + typedef llvm::PointerUnion<ELFRel*, ELFRela*> RelocUnion; + + RelocUnion reloc; +}; + +ELFRelocation::ELFRelocation(unsigned type) +{ + if (type == DT_REL) + reloc = new ELFRel(); + else if (type == DT_RELA) + reloc = new ELFRela(); + else { + assert(false && "unexpected relocation type"); + reloc = static_cast<ELFRel*>(NULL); + } +} + +ELFRelocation::~ELFRelocation() +{ + if (reloc.is<ELFRel*>()) + delete reloc.get<ELFRel*>(); + else + delete reloc.get<ELFRela*>(); +} + +bool +ELFRelocation::Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset) +{ + if (reloc.is<ELFRel*>()) + return reloc.get<ELFRel*>()->Parse(data, offset); + else + return reloc.get<ELFRela*>()->Parse(data, offset); +} + +unsigned +ELFRelocation::RelocType32(const ELFRelocation &rel) +{ + if (rel.reloc.is<ELFRel*>()) + return ELFRel::RelocType32(*rel.reloc.get<ELFRel*>()); + else + return ELFRela::RelocType32(*rel.reloc.get<ELFRela*>()); +} + +unsigned +ELFRelocation::RelocType64(const ELFRelocation &rel) +{ + if (rel.reloc.is<ELFRel*>()) + return ELFRel::RelocType64(*rel.reloc.get<ELFRel*>()); + else + return ELFRela::RelocType64(*rel.reloc.get<ELFRela*>()); +} + +unsigned +ELFRelocation::RelocSymbol32(const ELFRelocation &rel) +{ + if (rel.reloc.is<ELFRel*>()) + return ELFRel::RelocSymbol32(*rel.reloc.get<ELFRel*>()); + else + return ELFRela::RelocSymbol32(*rel.reloc.get<ELFRela*>()); +} + +unsigned +ELFRelocation::RelocSymbol64(const ELFRelocation &rel) +{ + if (rel.reloc.is<ELFRel*>()) + return ELFRel::RelocSymbol64(*rel.reloc.get<ELFRel*>()); + else + return ELFRela::RelocSymbol64(*rel.reloc.get<ELFRela*>()); +} + +} // end anonymous namespace + +//------------------------------------------------------------------ +// Static methods. +//------------------------------------------------------------------ +void +ObjectFileELF::Initialize() +{ + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + CreateMemoryInstance, + GetModuleSpecifications); +} + +void +ObjectFileELF::Terminate() +{ + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString +ObjectFileELF::GetPluginNameStatic() +{ + static ConstString g_name("elf"); + return g_name; +} + +const char * +ObjectFileELF::GetPluginDescriptionStatic() +{ + return "ELF object file reader."; +} + +ObjectFile * +ObjectFileELF::CreateInstance (const lldb::ModuleSP &module_sp, + DataBufferSP &data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) +{ + if (!data_sp) + { + data_sp = file->MemoryMapFileContents(file_offset, length); + data_offset = 0; + } + + if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) + { + const uint8_t *magic = data_sp->GetBytes() + data_offset; + if (ELFHeader::MagicBytesMatch(magic)) + { + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) { + data_sp = file->MemoryMapFileContents(file_offset, length); + data_offset = 0; + magic = data_sp->GetBytes(); + } + unsigned address_size = ELFHeader::AddressSizeInBytes(magic); + if (address_size == 4 || address_size == 8) + { + std::unique_ptr<ObjectFileELF> objfile_ap(new ObjectFileELF(module_sp, data_sp, data_offset, file, file_offset, length)); + ArchSpec spec; + if (objfile_ap->GetArchitecture(spec) && + objfile_ap->SetModulesArchitecture(spec)) + return objfile_ap.release(); + } + } + } + return NULL; +} + + +ObjectFile* +ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr) +{ + return NULL; +} + +bool +ObjectFileELF::MagicBytesMatch (DataBufferSP& data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) +{ + if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) + { + const uint8_t *magic = data_sp->GetBytes() + data_offset; + return ELFHeader::MagicBytesMatch(magic); + } + return false; +} + +/* + * crc function from http://svnweb.freebsd.org/base/head/sys/libkern/crc32.c + * + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ +static uint32_t +calc_gnu_debuglink_crc32(const void *buf, size_t size) +{ + static const uint32_t g_crc32_tab[] = + { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + const uint8_t *p = (const uint8_t *)buf; + uint32_t crc; + + crc = ~0U; + while (size--) + crc = g_crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + return crc ^ ~0U; +} + +size_t +ObjectFileELF::GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs) +{ + const size_t initial_count = specs.GetSize(); + + if (ObjectFileELF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) + { + DataExtractor data; + data.SetData(data_sp); + elf::ELFHeader header; + if (header.Parse(data, &data_offset)) + { + if (data_sp) + { + ModuleSpec spec; + spec.GetFileSpec() = file; + spec.GetArchitecture().SetArchitecture(eArchTypeELF, + header.e_machine, + LLDB_INVALID_CPUTYPE); + if (spec.GetArchitecture().IsValid()) + { + // We could parse the ABI tag information (in .note, .notes, or .note.ABI-tag) to get the + // machine information. However, this info isn't guaranteed to exist or be correct. Details: + // http://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/noteabitag.html + // Instead of passing potentially incorrect information down the pipeline, grab + // the host information and use it. + spec.GetArchitecture().GetTriple().setOSName (Host::GetOSString().GetCString()); + spec.GetArchitecture().GetTriple().setVendorName(Host::GetVendorString().GetCString()); + + // Try to get the UUID from the section list. Usually that's at the end, so + // map the file in if we don't have it already. + size_t section_header_end = header.e_shoff + header.e_shnum * header.e_shentsize; + if (section_header_end > data_sp->GetByteSize()) + { + data_sp = file.MemoryMapFileContents (file_offset, section_header_end); + data.SetData(data_sp); + } + + uint32_t gnu_debuglink_crc = 0; + std::string gnu_debuglink_file; + SectionHeaderColl section_headers; + lldb_private::UUID &uuid = spec.GetUUID(); + GetSectionHeaderInfo(section_headers, data, header, uuid, gnu_debuglink_file, gnu_debuglink_crc); + + if (!uuid.IsValid()) + { + if (!gnu_debuglink_crc) + { + // Need to map entire file into memory to calculate the crc. + data_sp = file.MemoryMapFileContents (file_offset, SIZE_MAX); + data.SetData(data_sp); + gnu_debuglink_crc = calc_gnu_debuglink_crc32 (data.GetDataStart(), data.GetByteSize()); + } + if (gnu_debuglink_crc) + { + // Use 4 bytes of crc from the .gnu_debuglink section. + uint32_t uuidt[4] = { gnu_debuglink_crc, 0, 0, 0 }; + uuid.SetBytes (uuidt, sizeof(uuidt)); + } + } + + specs.Append(spec); + } + } + } + } + + return specs.GetSize() - initial_count; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ObjectFileELF::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFileELF::GetPluginVersion() +{ + return m_plugin_version; +} +//------------------------------------------------------------------ +// ObjectFile protocol +//------------------------------------------------------------------ + +ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, + DataBufferSP& data_sp, + lldb::offset_t data_offset, + const FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length) : + ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), + m_header(), + m_program_headers(), + m_section_headers(), + m_filespec_ap() +{ + if (file) + m_file = *file; + ::memset(&m_header, 0, sizeof(m_header)); + m_gnu_debuglink_crc = 0; + m_gnu_debuglink_file.clear(); +} + +ObjectFileELF::~ObjectFileELF() +{ +} + +bool +ObjectFileELF::IsExecutable() const +{ + return m_header.e_entry != 0; +} + +ByteOrder +ObjectFileELF::GetByteOrder() const +{ + if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) + return eByteOrderBig; + if (m_header.e_ident[EI_DATA] == ELFDATA2LSB) + return eByteOrderLittle; + return eByteOrderInvalid; +} + +uint32_t +ObjectFileELF::GetAddressByteSize() const +{ + return m_data.GetAddressByteSize(); +} + +size_t +ObjectFileELF::SectionIndex(const SectionHeaderCollIter &I) +{ + return std::distance(m_section_headers.begin(), I) + 1u; +} + +size_t +ObjectFileELF::SectionIndex(const SectionHeaderCollConstIter &I) const +{ + return std::distance(m_section_headers.begin(), I) + 1u; +} + +bool +ObjectFileELF::ParseHeader() +{ + lldb::offset_t offset = 0; + return m_header.Parse(m_data, &offset); +} + +bool +ObjectFileELF::GetUUID(lldb_private::UUID* uuid) +{ + // Need to parse the section list to get the UUIDs, so make sure that's been done. + if (!ParseSectionHeaders()) + return false; + + if (m_uuid.IsValid()) + { + // We have the full build id uuid. + *uuid = m_uuid; + return true; + } + else + { + if (!m_gnu_debuglink_crc) + m_gnu_debuglink_crc = calc_gnu_debuglink_crc32 (m_data.GetDataStart(), m_data.GetByteSize()); + if (m_gnu_debuglink_crc) + { + // Use 4 bytes of crc from the .gnu_debuglink section. + uint32_t uuidt[4] = { m_gnu_debuglink_crc, 0, 0, 0 }; + uuid->SetBytes (uuidt, sizeof(uuidt)); + return true; + } + } + + return false; +} + +lldb_private::FileSpecList +ObjectFileELF::GetDebugSymbolFilePaths() +{ + FileSpecList file_spec_list; + + if (!m_gnu_debuglink_file.empty()) + { + FileSpec file_spec (m_gnu_debuglink_file.c_str(), false); + file_spec_list.Append (file_spec); + } + return file_spec_list; +} + +uint32_t +ObjectFileELF::GetDependentModules(FileSpecList &files) +{ + size_t num_modules = ParseDependentModules(); + uint32_t num_specs = 0; + + for (unsigned i = 0; i < num_modules; ++i) + { + if (files.AppendIfUnique(m_filespec_ap->GetFileSpecAtIndex(i))) + num_specs++; + } + + return num_specs; +} + +Address +ObjectFileELF::GetImageInfoAddress() +{ + if (!ParseDynamicSymbols()) + return Address(); + + SectionList *section_list = GetSectionList(); + if (!section_list) + return Address(); + + // Find the SHT_DYNAMIC (.dynamic) section. + SectionSP dynsym_section_sp (section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true)); + if (!dynsym_section_sp) + return Address(); + assert (dynsym_section_sp->GetObjectFile() == this); + + user_id_t dynsym_id = dynsym_section_sp->GetID(); + const ELFSectionHeaderInfo *dynsym_hdr = GetSectionHeaderByIndex(dynsym_id); + if (!dynsym_hdr) + return Address(); + + for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) + { + ELFDynamic &symbol = m_dynamic_symbols[i]; + + if (symbol.d_tag == DT_DEBUG) + { + // Compute the offset as the number of previous entries plus the + // size of d_tag. + addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize(); + return Address(dynsym_section_sp, offset); + } + } + + return Address(); +} + +lldb_private::Address +ObjectFileELF::GetEntryPointAddress () +{ + if (m_entry_point_address.IsValid()) + return m_entry_point_address; + + if (!ParseHeader() || !IsExecutable()) + return m_entry_point_address; + + SectionList *section_list = GetSectionList(); + addr_t offset = m_header.e_entry; + + if (!section_list) + m_entry_point_address.SetOffset(offset); + else + m_entry_point_address.ResolveAddressUsingFileSections(offset, section_list); + return m_entry_point_address; +} + +//---------------------------------------------------------------------- +// ParseDependentModules +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseDependentModules() +{ + if (m_filespec_ap.get()) + return m_filespec_ap->GetSize(); + + m_filespec_ap.reset(new FileSpecList()); + + if (!ParseSectionHeaders()) + return 0; + + SectionList *section_list = GetSectionList(); + if (!section_list) + return 0; + + // Find the SHT_DYNAMIC section. + Section *dynsym = section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true).get(); + if (!dynsym) + return 0; + assert (dynsym->GetObjectFile() == this); + + const ELFSectionHeaderInfo *header = GetSectionHeaderByIndex (dynsym->GetID()); + if (!header) + return 0; + // sh_link: section header index of string table used by entries in the section. + Section *dynstr = section_list->FindSectionByID (header->sh_link + 1).get(); + if (!dynstr) + return 0; + + DataExtractor dynsym_data; + DataExtractor dynstr_data; + if (ReadSectionData(dynsym, dynsym_data) && + ReadSectionData(dynstr, dynstr_data)) + { + ELFDynamic symbol; + const lldb::offset_t section_size = dynsym_data.GetByteSize(); + lldb::offset_t offset = 0; + + // The only type of entries we are concerned with are tagged DT_NEEDED, + // yielding the name of a required library. + while (offset < section_size) + { + if (!symbol.Parse(dynsym_data, &offset)) + break; + + if (symbol.d_tag != DT_NEEDED) + continue; + + uint32_t str_index = static_cast<uint32_t>(symbol.d_val); + const char *lib_name = dynstr_data.PeekCStr(str_index); + m_filespec_ap->Append(FileSpec(lib_name, true)); + } + } + + return m_filespec_ap->GetSize(); +} + +//---------------------------------------------------------------------- +// ParseProgramHeaders +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseProgramHeaders() +{ + // We have already parsed the program headers + if (!m_program_headers.empty()) + return m_program_headers.size(); + + // If there are no program headers to read we are done. + if (m_header.e_phnum == 0) + return 0; + + m_program_headers.resize(m_header.e_phnum); + if (m_program_headers.size() != m_header.e_phnum) + return 0; + + const size_t ph_size = m_header.e_phnum * m_header.e_phentsize; + const elf_off ph_offset = m_header.e_phoff; + DataExtractor data; + if (GetData (ph_offset, ph_size, data) != ph_size) + return 0; + + uint32_t idx; + lldb::offset_t offset; + for (idx = 0, offset = 0; idx < m_header.e_phnum; ++idx) + { + if (m_program_headers[idx].Parse(data, &offset) == false) + break; + } + + if (idx < m_program_headers.size()) + m_program_headers.resize(idx); + + return m_program_headers.size(); +} + +static bool +ParseNoteGNUBuildID(DataExtractor &data, lldb_private::UUID &uuid) +{ + // Try to parse the note section (ie .note.gnu.build-id|.notes|.note|...) and get the build id. + // BuildID documentation: https://fedoraproject.org/wiki/Releases/FeatureBuildId + struct + { + uint32_t name_len; // Length of note name + uint32_t desc_len; // Length of note descriptor + uint32_t type; // Type of note (1 is ABI_TAG, 3 is BUILD_ID) + } notehdr; + lldb::offset_t offset = 0; + static const uint32_t g_gnu_build_id = 3; // NT_GNU_BUILD_ID from elf.h + + while (true) + { + if (data.GetU32 (&offset, ¬ehdr, 3) == NULL) + return false; + + notehdr.name_len = llvm::RoundUpToAlignment (notehdr.name_len, 4); + notehdr.desc_len = llvm::RoundUpToAlignment (notehdr.desc_len, 4); + + lldb::offset_t offset_next_note = offset + notehdr.name_len + notehdr.desc_len; + + // 16 bytes is UUID|MD5, 20 bytes is SHA1 + if ((notehdr.type == g_gnu_build_id) && (notehdr.name_len == 4) && + (notehdr.desc_len == 16 || notehdr.desc_len == 20)) + { + char name[4]; + if (data.GetU8 (&offset, name, 4) == NULL) + return false; + if (!strcmp(name, "GNU")) + { + uint8_t uuidbuf[20]; + if (data.GetU8 (&offset, &uuidbuf, notehdr.desc_len) == NULL) + return false; + uuid.SetBytes (uuidbuf, notehdr.desc_len); + return true; + } + } + offset = offset_next_note; + } + return false; +} + +//---------------------------------------------------------------------- +// GetSectionHeaderInfo +//---------------------------------------------------------------------- +size_t +ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl §ion_headers, + lldb_private::DataExtractor &object_data, + const elf::ELFHeader &header, + lldb_private::UUID &uuid, + std::string &gnu_debuglink_file, + uint32_t &gnu_debuglink_crc) +{ + // We have already parsed the section headers + if (!section_headers.empty()) + return section_headers.size(); + + // If there are no section headers we are done. + if (header.e_shnum == 0) + return 0; + + section_headers.resize(header.e_shnum); + if (section_headers.size() != header.e_shnum) + return 0; + + const size_t sh_size = header.e_shnum * header.e_shentsize; + const elf_off sh_offset = header.e_shoff; + DataExtractor sh_data; + if (sh_data.SetData (object_data, sh_offset, sh_size) != sh_size) + return 0; + + uint32_t idx; + lldb::offset_t offset; + for (idx = 0, offset = 0; idx < header.e_shnum; ++idx) + { + if (section_headers[idx].Parse(sh_data, &offset) == false) + break; + } + if (idx < section_headers.size()) + section_headers.resize(idx); + + const unsigned strtab_idx = header.e_shstrndx; + if (strtab_idx && strtab_idx < section_headers.size()) + { + const ELFSectionHeaderInfo &sheader = section_headers[strtab_idx]; + const size_t byte_size = sheader.sh_size; + const Elf64_Off offset = sheader.sh_offset; + lldb_private::DataExtractor shstr_data; + + if (shstr_data.SetData (object_data, offset, byte_size) == byte_size) + { + for (SectionHeaderCollIter I = section_headers.begin(); + I != section_headers.end(); ++I) + { + static ConstString g_sect_name_gnu_debuglink (".gnu_debuglink"); + const ELFSectionHeaderInfo &header = *I; + const uint64_t section_size = header.sh_type == SHT_NOBITS ? 0 : header.sh_size; + ConstString name(shstr_data.PeekCStr(I->sh_name)); + + I->section_name = name; + + if (name == g_sect_name_gnu_debuglink) + { + DataExtractor data; + if (section_size && (data.SetData (object_data, header.sh_offset, section_size) == section_size)) + { + lldb::offset_t gnu_debuglink_offset = 0; + gnu_debuglink_file = data.GetCStr (&gnu_debuglink_offset); + gnu_debuglink_offset = llvm::RoundUpToAlignment (gnu_debuglink_offset, 4); + data.GetU32 (&gnu_debuglink_offset, &gnu_debuglink_crc, 1); + } + } + + if (header.sh_type == SHT_NOTE && !uuid.IsValid()) + { + DataExtractor data; + if (section_size && (data.SetData (object_data, header.sh_offset, section_size) == section_size)) + { + ParseNoteGNUBuildID (data, uuid); + } + } + } + + return section_headers.size(); + } + } + + section_headers.clear(); + return 0; +} + +size_t +ObjectFileELF::GetProgramHeaderCount() +{ + return ParseProgramHeaders(); +} + +const elf::ELFProgramHeader * +ObjectFileELF::GetProgramHeaderByIndex(lldb::user_id_t id) +{ + if (!id || !ParseProgramHeaders()) + return NULL; + + if (--id < m_program_headers.size()) + return &m_program_headers[id]; + + return NULL; +} + +DataExtractor +ObjectFileELF::GetSegmentDataByIndex(lldb::user_id_t id) +{ + const elf::ELFProgramHeader *segment_header = GetProgramHeaderByIndex(id); + if (segment_header == NULL) + return DataExtractor(); + return DataExtractor(m_data, segment_header->p_offset, segment_header->p_filesz); +} + +//---------------------------------------------------------------------- +// ParseSectionHeaders +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseSectionHeaders() +{ + return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid, m_gnu_debuglink_file, m_gnu_debuglink_crc); +} + +const ObjectFileELF::ELFSectionHeaderInfo * +ObjectFileELF::GetSectionHeaderByIndex(lldb::user_id_t id) +{ + if (!id || !ParseSectionHeaders()) + return NULL; + + if (--id < m_section_headers.size()) + return &m_section_headers[id]; + + return NULL; +} + +void +ObjectFileELF::CreateSections(SectionList &unified_section_list) +{ + if (!m_sections_ap.get() && ParseSectionHeaders()) + { + m_sections_ap.reset(new SectionList()); + + for (SectionHeaderCollIter I = m_section_headers.begin(); + I != m_section_headers.end(); ++I) + { + const ELFSectionHeaderInfo &header = *I; + + ConstString& name = I->section_name; + const uint64_t file_size = header.sh_type == SHT_NOBITS ? 0 : header.sh_size; + const uint64_t vm_size = header.sh_flags & SHF_ALLOC ? header.sh_size : 0; + + static ConstString g_sect_name_text (".text"); + static ConstString g_sect_name_data (".data"); + static ConstString g_sect_name_bss (".bss"); + static ConstString g_sect_name_tdata (".tdata"); + static ConstString g_sect_name_tbss (".tbss"); + static ConstString g_sect_name_dwarf_debug_abbrev (".debug_abbrev"); + static ConstString g_sect_name_dwarf_debug_aranges (".debug_aranges"); + static ConstString g_sect_name_dwarf_debug_frame (".debug_frame"); + static ConstString g_sect_name_dwarf_debug_info (".debug_info"); + static ConstString g_sect_name_dwarf_debug_line (".debug_line"); + static ConstString g_sect_name_dwarf_debug_loc (".debug_loc"); + static ConstString g_sect_name_dwarf_debug_macinfo (".debug_macinfo"); + static ConstString g_sect_name_dwarf_debug_pubnames (".debug_pubnames"); + static ConstString g_sect_name_dwarf_debug_pubtypes (".debug_pubtypes"); + static ConstString g_sect_name_dwarf_debug_ranges (".debug_ranges"); + static ConstString g_sect_name_dwarf_debug_str (".debug_str"); + static ConstString g_sect_name_eh_frame (".eh_frame"); + + SectionType sect_type = eSectionTypeOther; + + bool is_thread_specific = false; + + if (name == g_sect_name_text) sect_type = eSectionTypeCode; + else if (name == g_sect_name_data) sect_type = eSectionTypeData; + else if (name == g_sect_name_bss) sect_type = eSectionTypeZeroFill; + else if (name == g_sect_name_tdata) + { + sect_type = eSectionTypeData; + is_thread_specific = true; + } + else if (name == g_sect_name_tbss) + { + sect_type = eSectionTypeZeroFill; + is_thread_specific = true; + } + // .debug_abbrev – Abbreviations used in the .debug_info section + // .debug_aranges – Lookup table for mapping addresses to compilation units + // .debug_frame – Call frame information + // .debug_info – The core DWARF information section + // .debug_line – Line number information + // .debug_loc – Location lists used in DW_AT_location attributes + // .debug_macinfo – Macro information + // .debug_pubnames – Lookup table for mapping object and function names to compilation units + // .debug_pubtypes – Lookup table for mapping type names to compilation units + // .debug_ranges – Address ranges used in DW_AT_ranges attributes + // .debug_str – String table used in .debug_info + // MISSING? .gnu_debugdata - "mini debuginfo / MiniDebugInfo" section, http://sourceware.org/gdb/onlinedocs/gdb/MiniDebugInfo.html + // MISSING? .debug-index - http://src.chromium.org/viewvc/chrome/trunk/src/build/gdb-add-index?pathrev=144644 + // MISSING? .debug_types - Type descriptions from DWARF 4? See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo + else if (name == g_sect_name_dwarf_debug_abbrev) sect_type = eSectionTypeDWARFDebugAbbrev; + else if (name == g_sect_name_dwarf_debug_aranges) sect_type = eSectionTypeDWARFDebugAranges; + else if (name == g_sect_name_dwarf_debug_frame) sect_type = eSectionTypeDWARFDebugFrame; + else if (name == g_sect_name_dwarf_debug_info) sect_type = eSectionTypeDWARFDebugInfo; + else if (name == g_sect_name_dwarf_debug_line) sect_type = eSectionTypeDWARFDebugLine; + else if (name == g_sect_name_dwarf_debug_loc) sect_type = eSectionTypeDWARFDebugLoc; + else if (name == g_sect_name_dwarf_debug_macinfo) sect_type = eSectionTypeDWARFDebugMacInfo; + else if (name == g_sect_name_dwarf_debug_pubnames) sect_type = eSectionTypeDWARFDebugPubNames; + else if (name == g_sect_name_dwarf_debug_pubtypes) sect_type = eSectionTypeDWARFDebugPubTypes; + else if (name == g_sect_name_dwarf_debug_ranges) sect_type = eSectionTypeDWARFDebugRanges; + else if (name == g_sect_name_dwarf_debug_str) sect_type = eSectionTypeDWARFDebugStr; + else if (name == g_sect_name_eh_frame) sect_type = eSectionTypeEHFrame; + + switch (header.sh_type) + { + case SHT_SYMTAB: + assert (sect_type == eSectionTypeOther); + sect_type = eSectionTypeELFSymbolTable; + break; + case SHT_DYNSYM: + assert (sect_type == eSectionTypeOther); + sect_type = eSectionTypeELFDynamicSymbols; + break; + case SHT_RELA: + case SHT_REL: + assert (sect_type == eSectionTypeOther); + sect_type = eSectionTypeELFRelocationEntries; + break; + case SHT_DYNAMIC: + assert (sect_type == eSectionTypeOther); + sect_type = eSectionTypeELFDynamicLinkInfo; + break; + } + + SectionSP section_sp (new Section(GetModule(), // Module to which this section belongs. + this, // ObjectFile to which this section belongs and should read section data from. + SectionIndex(I), // Section ID. + name, // Section name. + sect_type, // Section type. + header.sh_addr, // VM address. + vm_size, // VM size in bytes of this section. + header.sh_offset, // Offset of this section in the file. + file_size, // Size of the section as found in the file. + header.sh_flags)); // Flags for this section. + + if (is_thread_specific) + section_sp->SetIsThreadSpecific (is_thread_specific); + m_sections_ap->AddSection(section_sp); + } + } + + if (m_sections_ap.get()) + { + if (GetType() == eTypeDebugInfo) + { + static const SectionType g_sections[] = + { + eSectionTypeDWARFDebugAranges, + eSectionTypeDWARFDebugInfo, + eSectionTypeDWARFDebugAbbrev, + eSectionTypeDWARFDebugFrame, + eSectionTypeDWARFDebugLine, + eSectionTypeDWARFDebugStr, + eSectionTypeDWARFDebugLoc, + eSectionTypeDWARFDebugMacInfo, + eSectionTypeDWARFDebugPubNames, + eSectionTypeDWARFDebugPubTypes, + eSectionTypeDWARFDebugRanges, + eSectionTypeELFSymbolTable, + }; + SectionList *elf_section_list = m_sections_ap.get(); + for (size_t idx = 0; idx < sizeof(g_sections) / sizeof(g_sections[0]); ++idx) + { + SectionType section_type = g_sections[idx]; + SectionSP section_sp (elf_section_list->FindSectionByType (section_type, true)); + if (section_sp) + { + SectionSP module_section_sp (unified_section_list.FindSectionByType (section_type, true)); + if (module_section_sp) + unified_section_list.ReplaceSection (module_section_sp->GetID(), section_sp); + else + unified_section_list.AddSection (section_sp); + } + } + } + else + { + unified_section_list = *m_sections_ap; + } + } +} + +// private +unsigned +ObjectFileELF::ParseSymbols (Symtab *symtab, + user_id_t start_id, + SectionList *section_list, + const size_t num_symbols, + const DataExtractor &symtab_data, + const DataExtractor &strtab_data) +{ + ELFSymbol symbol; + lldb::offset_t offset = 0; + + static ConstString text_section_name(".text"); + static ConstString init_section_name(".init"); + static ConstString fini_section_name(".fini"); + static ConstString ctors_section_name(".ctors"); + static ConstString dtors_section_name(".dtors"); + + static ConstString data_section_name(".data"); + static ConstString rodata_section_name(".rodata"); + static ConstString rodata1_section_name(".rodata1"); + static ConstString data2_section_name(".data1"); + static ConstString bss_section_name(".bss"); + + //StreamFile strm(stdout, false); + unsigned i; + for (i = 0; i < num_symbols; ++i) + { + if (symbol.Parse(symtab_data, &offset) == false) + break; + + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); + + // No need to add symbols that have no names + if (symbol_name == NULL || symbol_name[0] == '\0') + continue; + + //symbol.Dump (&strm, i, &strtab_data, section_list); + + SectionSP symbol_section_sp; + SymbolType symbol_type = eSymbolTypeInvalid; + Elf64_Half symbol_idx = symbol.st_shndx; + + switch (symbol_idx) + { + case SHN_ABS: + symbol_type = eSymbolTypeAbsolute; + break; + case SHN_UNDEF: + symbol_type = eSymbolTypeUndefined; + break; + default: + symbol_section_sp = section_list->GetSectionAtIndex(symbol_idx); + break; + } + + // If a symbol is undefined do not process it further even if it has a STT type + if (symbol_type != eSymbolTypeUndefined) + { + switch (symbol.getType()) + { + default: + case STT_NOTYPE: + // The symbol's type is not specified. + break; + + case STT_OBJECT: + // The symbol is associated with a data object, such as a variable, + // an array, etc. + symbol_type = eSymbolTypeData; + break; + + case STT_FUNC: + // The symbol is associated with a function or other executable code. + symbol_type = eSymbolTypeCode; + break; + + case STT_SECTION: + // The symbol is associated with a section. Symbol table entries of + // this type exist primarily for relocation and normally have + // STB_LOCAL binding. + break; + + case STT_FILE: + // Conventionally, the symbol's name gives the name of the source + // file associated with the object file. A file symbol has STB_LOCAL + // binding, its section index is SHN_ABS, and it precedes the other + // STB_LOCAL symbols for the file, if it is present. + symbol_type = eSymbolTypeSourceFile; + break; + + case STT_GNU_IFUNC: + // The symbol is associated with an indirect function. The actual + // function will be resolved if it is referenced. + symbol_type = eSymbolTypeResolver; + break; + } + } + + if (symbol_type == eSymbolTypeInvalid) + { + if (symbol_section_sp) + { + const ConstString §_name = symbol_section_sp->GetName(); + if (sect_name == text_section_name || + sect_name == init_section_name || + sect_name == fini_section_name || + sect_name == ctors_section_name || + sect_name == dtors_section_name) + { + symbol_type = eSymbolTypeCode; + } + else if (sect_name == data_section_name || + sect_name == data2_section_name || + sect_name == rodata_section_name || + sect_name == rodata1_section_name || + sect_name == bss_section_name) + { + symbol_type = eSymbolTypeData; + } + } + } + + // If the symbol section we've found has no data (SHT_NOBITS), then check the module section + // list. This can happen if we're parsing the debug file and it has no .text section, for example. + if (symbol_section_sp && (symbol_section_sp->GetFileSize() == 0)) + { + ModuleSP module_sp(GetModule()); + if (module_sp) + { + SectionList *module_section_list = module_sp->GetSectionList(); + if (module_section_list && module_section_list != section_list) + { + const ConstString §_name = symbol_section_sp->GetName(); + lldb::SectionSP section_sp (module_section_list->FindSectionByName (sect_name)); + if (section_sp && section_sp->GetFileSize()) + { + symbol_section_sp = section_sp; + } + } + } + } + + uint64_t symbol_value = symbol.st_value; + if (symbol_section_sp) + symbol_value -= symbol_section_sp->GetFileAddress(); + bool is_global = symbol.getBinding() == STB_GLOBAL; + uint32_t flags = symbol.st_other << 8 | symbol.st_info; + bool is_mangled = symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false; + Symbol dc_symbol( + i + start_id, // ID is the original symbol table index. + symbol_name, // Symbol name. + is_mangled, // Is the symbol name mangled? + symbol_type, // Type of this symbol + is_global, // Is this globally visible? + false, // Is this symbol debug info? + false, // Is this symbol a trampoline? + false, // Is this symbol artificial? + symbol_section_sp, // Section in which this symbol is defined or null. + symbol_value, // Offset in section or symbol value. + symbol.st_size, // Size in bytes of this symbol. + true, // Size is valid + flags); // Symbol flags. + symtab->AddSymbol(dc_symbol); + } + + return i; +} + +unsigned +ObjectFileELF::ParseSymbolTable(Symtab *symbol_table, user_id_t start_id, lldb_private::Section *symtab) +{ + if (symtab->GetObjectFile() != this) + { + // If the symbol table section is owned by a different object file, have it do the + // parsing. + ObjectFileELF *obj_file_elf = static_cast<ObjectFileELF *>(symtab->GetObjectFile()); + return obj_file_elf->ParseSymbolTable (symbol_table, start_id, symtab); + } + + // Get section list for this object file. + SectionList *section_list = m_sections_ap.get(); + if (!section_list) + return 0; + + user_id_t symtab_id = symtab->GetID(); + const ELFSectionHeaderInfo *symtab_hdr = GetSectionHeaderByIndex(symtab_id); + assert(symtab_hdr->sh_type == SHT_SYMTAB || + symtab_hdr->sh_type == SHT_DYNSYM); + + // sh_link: section header index of associated string table. + // Section ID's are ones based. + user_id_t strtab_id = symtab_hdr->sh_link + 1; + Section *strtab = section_list->FindSectionByID(strtab_id).get(); + + unsigned num_symbols = 0; + if (symtab && strtab) + { + assert (symtab->GetObjectFile() == this); + assert (strtab->GetObjectFile() == this); + + DataExtractor symtab_data; + DataExtractor strtab_data; + if (ReadSectionData(symtab, symtab_data) && + ReadSectionData(strtab, strtab_data)) + { + size_t num_symbols = symtab_data.GetByteSize() / symtab_hdr->sh_entsize; + + num_symbols = ParseSymbols(symbol_table, start_id, + section_list, num_symbols, + symtab_data, strtab_data); + } + } + + return num_symbols; +} + +size_t +ObjectFileELF::ParseDynamicSymbols() +{ + if (m_dynamic_symbols.size()) + return m_dynamic_symbols.size(); + + SectionList *section_list = GetSectionList(); + if (!section_list) + return 0; + + // Find the SHT_DYNAMIC section. + Section *dynsym = section_list->FindSectionByType (eSectionTypeELFDynamicLinkInfo, true).get(); + if (!dynsym) + return 0; + assert (dynsym->GetObjectFile() == this); + + ELFDynamic symbol; + DataExtractor dynsym_data; + if (ReadSectionData(dynsym, dynsym_data)) + { + const lldb::offset_t section_size = dynsym_data.GetByteSize(); + lldb::offset_t cursor = 0; + + while (cursor < section_size) + { + if (!symbol.Parse(dynsym_data, &cursor)) + break; + + m_dynamic_symbols.push_back(symbol); + } + } + + return m_dynamic_symbols.size(); +} + +const ELFDynamic * +ObjectFileELF::FindDynamicSymbol(unsigned tag) +{ + if (!ParseDynamicSymbols()) + return NULL; + + DynamicSymbolCollIter I = m_dynamic_symbols.begin(); + DynamicSymbolCollIter E = m_dynamic_symbols.end(); + for ( ; I != E; ++I) + { + ELFDynamic *symbol = &*I; + + if (symbol->d_tag == tag) + return symbol; + } + + return NULL; +} + +unsigned +ObjectFileELF::PLTRelocationType() +{ + const ELFDynamic *symbol = FindDynamicSymbol(DT_PLTREL); + + if (symbol) + return symbol->d_val; + + return 0; +} + +static unsigned +ParsePLTRelocations(Symtab *symbol_table, + user_id_t start_id, + unsigned rel_type, + const ELFHeader *hdr, + const ELFSectionHeader *rel_hdr, + const ELFSectionHeader *plt_hdr, + const ELFSectionHeader *sym_hdr, + const lldb::SectionSP &plt_section_sp, + DataExtractor &rel_data, + DataExtractor &symtab_data, + DataExtractor &strtab_data) +{ + ELFRelocation rel(rel_type); + ELFSymbol symbol; + lldb::offset_t offset = 0; + const elf_xword plt_entsize = plt_hdr->sh_entsize; + const elf_xword num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize; + + typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel); + reloc_info_fn reloc_type; + reloc_info_fn reloc_symbol; + + if (hdr->Is32Bit()) + { + reloc_type = ELFRelocation::RelocType32; + reloc_symbol = ELFRelocation::RelocSymbol32; + } + else + { + reloc_type = ELFRelocation::RelocType64; + reloc_symbol = ELFRelocation::RelocSymbol64; + } + + unsigned slot_type = hdr->GetRelocationJumpSlotType(); + unsigned i; + for (i = 0; i < num_relocations; ++i) + { + if (rel.Parse(rel_data, &offset) == false) + break; + + if (reloc_type(rel) != slot_type) + continue; + + lldb::offset_t symbol_offset = reloc_symbol(rel) * sym_hdr->sh_entsize; + uint64_t plt_index = (i + 1) * plt_entsize; + + if (!symbol.Parse(symtab_data, &symbol_offset)) + break; + + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); + bool is_mangled = symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false; + + Symbol jump_symbol( + i + start_id, // Symbol table index + symbol_name, // symbol name. + is_mangled, // is the symbol name mangled? + eSymbolTypeTrampoline, // Type of this symbol + false, // Is this globally visible? + false, // Is this symbol debug info? + true, // Is this symbol a trampoline? + true, // Is this symbol artificial? + plt_section_sp, // Section in which this symbol is defined or null. + plt_index, // Offset in section or symbol value. + plt_entsize, // Size in bytes of this symbol. + true, // Size is valid + 0); // Symbol flags. + + symbol_table->AddSymbol(jump_symbol); + } + + return i; +} + +unsigned +ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, + user_id_t start_id, + const ELFSectionHeaderInfo *rel_hdr, + user_id_t rel_id) +{ + assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL); + + // The link field points to the associated symbol table. The info field + // points to the section holding the plt. + user_id_t symtab_id = rel_hdr->sh_link; + user_id_t plt_id = rel_hdr->sh_info; + + if (!symtab_id || !plt_id) + return 0; + + // Section ID's are ones based; + symtab_id++; + plt_id++; + + const ELFSectionHeaderInfo *plt_hdr = GetSectionHeaderByIndex(plt_id); + if (!plt_hdr) + return 0; + + const ELFSectionHeaderInfo *sym_hdr = GetSectionHeaderByIndex(symtab_id); + if (!sym_hdr) + return 0; + + SectionList *section_list = m_sections_ap.get(); + if (!section_list) + return 0; + + Section *rel_section = section_list->FindSectionByID(rel_id).get(); + if (!rel_section) + return 0; + + SectionSP plt_section_sp (section_list->FindSectionByID(plt_id)); + if (!plt_section_sp) + return 0; + + Section *symtab = section_list->FindSectionByID(symtab_id).get(); + if (!symtab) + return 0; + + // sh_link points to associated string table. + Section *strtab = section_list->FindSectionByID(sym_hdr->sh_link + 1).get(); + if (!strtab) + return 0; + + DataExtractor rel_data; + if (!ReadSectionData(rel_section, rel_data)) + return 0; + + DataExtractor symtab_data; + if (!ReadSectionData(symtab, symtab_data)) + return 0; + + DataExtractor strtab_data; + if (!ReadSectionData(strtab, strtab_data)) + return 0; + + unsigned rel_type = PLTRelocationType(); + if (!rel_type) + return 0; + + return ParsePLTRelocations (symbol_table, + start_id, + rel_type, + &m_header, + rel_hdr, + plt_hdr, + sym_hdr, + plt_section_sp, + rel_data, + symtab_data, + strtab_data); +} + +Symtab * +ObjectFileELF::GetSymtab() +{ + ModuleSP module_sp(GetModule()); + if (!module_sp) + return NULL; + + // We always want to use the main object file so we (hopefully) only have one cached copy + // of our symtab, dynamic sections, etc. + ObjectFile *module_obj_file = module_sp->GetObjectFile(); + if (module_obj_file && module_obj_file != this) + return module_obj_file->GetSymtab(); + + if (m_symtab_ap.get() == NULL) + { + SectionList *section_list = GetSectionList(); + if (!section_list) + return NULL; + + uint64_t symbol_id = 0; + lldb_private::Mutex::Locker locker(module_sp->GetMutex()); + + m_symtab_ap.reset(new Symtab(this)); + + // Sharable objects and dynamic executables usually have 2 distinct symbol + // tables, one named ".symtab", and the other ".dynsym". The dynsym is a smaller + // version of the symtab that only contains global symbols. The information found + // in the dynsym is therefore also found in the symtab, while the reverse is not + // necessarily true. + Section *symtab = section_list->FindSectionByType (eSectionTypeELFSymbolTable, true).get(); + if (!symtab) + { + // The symtab section is non-allocable and can be stripped, so if it doesn't exist + // then use the dynsym section which should always be there. + symtab = section_list->FindSectionByType (eSectionTypeELFDynamicSymbols, true).get(); + } + if (symtab) + symbol_id += ParseSymbolTable (m_symtab_ap.get(), symbol_id, symtab); + + // Synthesize trampoline symbols to help navigate the PLT. + const ELFDynamic *symbol = FindDynamicSymbol(DT_JMPREL); + if (symbol) + { + addr_t addr = symbol->d_ptr; + Section *reloc_section = section_list->FindSectionContainingFileAddress(addr).get(); + if (reloc_section) + { + user_id_t reloc_id = reloc_section->GetID(); + const ELFSectionHeaderInfo *reloc_header = GetSectionHeaderByIndex(reloc_id); + assert(reloc_header); + + ParseTrampolineSymbols (m_symtab_ap.get(), symbol_id, reloc_header, reloc_id); + } + } + } + return m_symtab_ap.get(); +} + +bool +ObjectFileELF::IsStripped () +{ + // TODO: determine this for ELF + return false; +} + +//===----------------------------------------------------------------------===// +// Dump +// +// Dump the specifics of the runtime file container (such as any headers +// segments, sections, etc). +//---------------------------------------------------------------------- +void +ObjectFileELF::Dump(Stream *s) +{ + DumpELFHeader(s, m_header); + s->EOL(); + DumpELFProgramHeaders(s); + s->EOL(); + DumpELFSectionHeaders(s); + s->EOL(); + SectionList *section_list = GetSectionList(); + if (section_list) + section_list->Dump(s, NULL, true, UINT32_MAX); + Symtab *symtab = GetSymtab(); + if (symtab) + symtab->Dump(s, NULL, eSortOrderNone); + s->EOL(); + DumpDependentModules(s); + s->EOL(); +} + +//---------------------------------------------------------------------- +// DumpELFHeader +// +// Dump the ELF header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader(Stream *s, const ELFHeader &header) +{ + s->PutCString("ELF Header\n"); + s->Printf("e_ident[EI_MAG0 ] = 0x%2.2x\n", header.e_ident[EI_MAG0]); + s->Printf("e_ident[EI_MAG1 ] = 0x%2.2x '%c'\n", + header.e_ident[EI_MAG1], header.e_ident[EI_MAG1]); + s->Printf("e_ident[EI_MAG2 ] = 0x%2.2x '%c'\n", + header.e_ident[EI_MAG2], header.e_ident[EI_MAG2]); + s->Printf("e_ident[EI_MAG3 ] = 0x%2.2x '%c'\n", + header.e_ident[EI_MAG3], header.e_ident[EI_MAG3]); + + s->Printf("e_ident[EI_CLASS ] = 0x%2.2x\n", header.e_ident[EI_CLASS]); + s->Printf("e_ident[EI_DATA ] = 0x%2.2x ", header.e_ident[EI_DATA]); + DumpELFHeader_e_ident_EI_DATA(s, header.e_ident[EI_DATA]); + s->Printf ("\ne_ident[EI_VERSION] = 0x%2.2x\n", header.e_ident[EI_VERSION]); + s->Printf ("e_ident[EI_PAD ] = 0x%2.2x\n", header.e_ident[EI_PAD]); + + s->Printf("e_type = 0x%4.4x ", header.e_type); + DumpELFHeader_e_type(s, header.e_type); + s->Printf("\ne_machine = 0x%4.4x\n", header.e_machine); + s->Printf("e_version = 0x%8.8x\n", header.e_version); + s->Printf("e_entry = 0x%8.8" PRIx64 "\n", header.e_entry); + s->Printf("e_phoff = 0x%8.8" PRIx64 "\n", header.e_phoff); + s->Printf("e_shoff = 0x%8.8" PRIx64 "\n", header.e_shoff); + s->Printf("e_flags = 0x%8.8x\n", header.e_flags); + s->Printf("e_ehsize = 0x%4.4x\n", header.e_ehsize); + s->Printf("e_phentsize = 0x%4.4x\n", header.e_phentsize); + s->Printf("e_phnum = 0x%4.4x\n", header.e_phnum); + s->Printf("e_shentsize = 0x%4.4x\n", header.e_shentsize); + s->Printf("e_shnum = 0x%4.4x\n", header.e_shnum); + s->Printf("e_shstrndx = 0x%4.4x\n", header.e_shstrndx); +} + +//---------------------------------------------------------------------- +// DumpELFHeader_e_type +// +// Dump an token value for the ELF header member e_type +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader_e_type(Stream *s, elf_half e_type) +{ + switch (e_type) + { + case ET_NONE: *s << "ET_NONE"; break; + case ET_REL: *s << "ET_REL"; break; + case ET_EXEC: *s << "ET_EXEC"; break; + case ET_DYN: *s << "ET_DYN"; break; + case ET_CORE: *s << "ET_CORE"; break; + default: + break; + } +} + +//---------------------------------------------------------------------- +// DumpELFHeader_e_ident_EI_DATA +// +// Dump an token value for the ELF header member e_ident[EI_DATA] +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader_e_ident_EI_DATA(Stream *s, unsigned char ei_data) +{ + switch (ei_data) + { + case ELFDATANONE: *s << "ELFDATANONE"; break; + case ELFDATA2LSB: *s << "ELFDATA2LSB - Little Endian"; break; + case ELFDATA2MSB: *s << "ELFDATA2MSB - Big Endian"; break; + default: + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFProgramHeader +// +// Dump a single ELF program header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader(Stream *s, const ELFProgramHeader &ph) +{ + DumpELFProgramHeader_p_type(s, ph.p_type); + s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, ph.p_offset, ph.p_vaddr, ph.p_paddr); + s->Printf(" %8.8" PRIx64 " %8.8" PRIx64 " %8.8x (", ph.p_filesz, ph.p_memsz, ph.p_flags); + + DumpELFProgramHeader_p_flags(s, ph.p_flags); + s->Printf(") %8.8" PRIx64, ph.p_align); +} + +//---------------------------------------------------------------------- +// DumpELFProgramHeader_p_type +// +// Dump an token value for the ELF program header member p_type which +// describes the type of the program header +// ---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, elf_word p_type) +{ + const int kStrWidth = 15; + switch (p_type) + { + CASE_AND_STREAM(s, PT_NULL , kStrWidth); + CASE_AND_STREAM(s, PT_LOAD , kStrWidth); + CASE_AND_STREAM(s, PT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, PT_INTERP , kStrWidth); + CASE_AND_STREAM(s, PT_NOTE , kStrWidth); + CASE_AND_STREAM(s, PT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, PT_PHDR , kStrWidth); + CASE_AND_STREAM(s, PT_TLS , kStrWidth); + CASE_AND_STREAM(s, PT_GNU_EH_FRAME, kStrWidth); + default: + s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, ""); + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFProgramHeader_p_flags +// +// Dump an token value for the ELF program header member p_flags +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader_p_flags(Stream *s, elf_word p_flags) +{ + *s << ((p_flags & PF_X) ? "PF_X" : " ") + << (((p_flags & PF_X) && (p_flags & PF_W)) ? '+' : ' ') + << ((p_flags & PF_W) ? "PF_W" : " ") + << (((p_flags & PF_W) && (p_flags & PF_R)) ? '+' : ' ') + << ((p_flags & PF_R) ? "PF_R" : " "); +} + +//---------------------------------------------------------------------- +// DumpELFProgramHeaders +// +// Dump all of the ELF program header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeaders(Stream *s) +{ + if (ParseProgramHeaders()) + { + s->PutCString("Program Headers\n"); + s->PutCString("IDX p_type p_offset p_vaddr p_paddr " + "p_filesz p_memsz p_flags p_align\n"); + s->PutCString("==== --------------- -------- -------- -------- " + "-------- -------- ------------------------- --------\n"); + + uint32_t idx = 0; + for (ProgramHeaderCollConstIter I = m_program_headers.begin(); + I != m_program_headers.end(); ++I, ++idx) + { + s->Printf("[%2u] ", idx); + ObjectFileELF::DumpELFProgramHeader(s, *I); + s->EOL(); + } + } +} + +//---------------------------------------------------------------------- +// DumpELFSectionHeader +// +// Dump a single ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader(Stream *s, const ELFSectionHeaderInfo &sh) +{ + s->Printf("%8.8x ", sh.sh_name); + DumpELFSectionHeader_sh_type(s, sh.sh_type); + s->Printf(" %8.8" PRIx64 " (", sh.sh_flags); + DumpELFSectionHeader_sh_flags(s, sh.sh_flags); + s->Printf(") %8.8" PRIx64 " %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addr, sh.sh_offset, sh.sh_size); + s->Printf(" %8.8x %8.8x", sh.sh_link, sh.sh_info); + s->Printf(" %8.8" PRIx64 " %8.8" PRIx64, sh.sh_addralign, sh.sh_entsize); +} + +//---------------------------------------------------------------------- +// DumpELFSectionHeader_sh_type +// +// Dump an token value for the ELF section header member sh_type which +// describes the type of the section +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader_sh_type(Stream *s, elf_word sh_type) +{ + const int kStrWidth = 12; + switch (sh_type) + { + CASE_AND_STREAM(s, SHT_NULL , kStrWidth); + CASE_AND_STREAM(s, SHT_PROGBITS , kStrWidth); + CASE_AND_STREAM(s, SHT_SYMTAB , kStrWidth); + CASE_AND_STREAM(s, SHT_STRTAB , kStrWidth); + CASE_AND_STREAM(s, SHT_RELA , kStrWidth); + CASE_AND_STREAM(s, SHT_HASH , kStrWidth); + CASE_AND_STREAM(s, SHT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, SHT_NOTE , kStrWidth); + CASE_AND_STREAM(s, SHT_NOBITS , kStrWidth); + CASE_AND_STREAM(s, SHT_REL , kStrWidth); + CASE_AND_STREAM(s, SHT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, SHT_DYNSYM , kStrWidth); + CASE_AND_STREAM(s, SHT_LOPROC , kStrWidth); + CASE_AND_STREAM(s, SHT_HIPROC , kStrWidth); + CASE_AND_STREAM(s, SHT_LOUSER , kStrWidth); + CASE_AND_STREAM(s, SHT_HIUSER , kStrWidth); + default: + s->Printf("0x%8.8x%*s", sh_type, kStrWidth - 10, ""); + break; + } +} + +//---------------------------------------------------------------------- +// DumpELFSectionHeader_sh_flags +// +// Dump an token value for the ELF section header member sh_flags +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader_sh_flags(Stream *s, elf_xword sh_flags) +{ + *s << ((sh_flags & SHF_WRITE) ? "WRITE" : " ") + << (((sh_flags & SHF_WRITE) && (sh_flags & SHF_ALLOC)) ? '+' : ' ') + << ((sh_flags & SHF_ALLOC) ? "ALLOC" : " ") + << (((sh_flags & SHF_ALLOC) && (sh_flags & SHF_EXECINSTR)) ? '+' : ' ') + << ((sh_flags & SHF_EXECINSTR) ? "EXECINSTR" : " "); +} + +//---------------------------------------------------------------------- +// DumpELFSectionHeaders +// +// Dump all of the ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeaders(Stream *s) +{ + if (!ParseSectionHeaders()) + return; + + s->PutCString("Section Headers\n"); + s->PutCString("IDX name type flags " + "addr offset size link info addralgn " + "entsize Name\n"); + s->PutCString("==== -------- ------------ -------------------------------- " + "-------- -------- -------- -------- -------- -------- " + "-------- ====================\n"); + + uint32_t idx = 0; + for (SectionHeaderCollConstIter I = m_section_headers.begin(); + I != m_section_headers.end(); ++I, ++idx) + { + s->Printf("[%2u] ", idx); + ObjectFileELF::DumpELFSectionHeader(s, *I); + const char* section_name = I->section_name.AsCString(""); + if (section_name) + *s << ' ' << section_name << "\n"; + } +} + +void +ObjectFileELF::DumpDependentModules(lldb_private::Stream *s) +{ + size_t num_modules = ParseDependentModules(); + + if (num_modules > 0) + { + s->PutCString("Dependent Modules:\n"); + for (unsigned i = 0; i < num_modules; ++i) + { + const FileSpec &spec = m_filespec_ap->GetFileSpecAtIndex(i); + s->Printf(" %s\n", spec.GetFilename().GetCString()); + } + } +} + +bool +ObjectFileELF::GetArchitecture (ArchSpec &arch) +{ + if (!ParseHeader()) + return false; + + arch.SetArchitecture (eArchTypeELF, m_header.e_machine, LLDB_INVALID_CPUTYPE); + arch.GetTriple().setOSName (Host::GetOSString().GetCString()); + arch.GetTriple().setVendorName(Host::GetVendorString().GetCString()); + return true; +} + +ObjectFile::Type +ObjectFileELF::CalculateType() +{ + switch (m_header.e_type) + { + case llvm::ELF::ET_NONE: + // 0 - No file type + return eTypeUnknown; + + case llvm::ELF::ET_REL: + // 1 - Relocatable file + return eTypeObjectFile; + + case llvm::ELF::ET_EXEC: + // 2 - Executable file + return eTypeExecutable; + + case llvm::ELF::ET_DYN: + // 3 - Shared object file + return eTypeSharedLibrary; + + case ET_CORE: + // 4 - Core file + return eTypeCoreFile; + + default: + break; + } + return eTypeUnknown; +} + +ObjectFile::Strata +ObjectFileELF::CalculateStrata() +{ + switch (m_header.e_type) + { + case llvm::ELF::ET_NONE: + // 0 - No file type + return eStrataUnknown; + + case llvm::ELF::ET_REL: + // 1 - Relocatable file + return eStrataUnknown; + + case llvm::ELF::ET_EXEC: + // 2 - Executable file + // TODO: is there any way to detect that an executable is a kernel + // related executable by inspecting the program headers, section + // headers, symbols, or any other flag bits??? + return eStrataUser; + + case llvm::ELF::ET_DYN: + // 3 - Shared object file + // TODO: is there any way to detect that an shared library is a kernel + // related executable by inspecting the program headers, section + // headers, symbols, or any other flag bits??? + return eStrataUnknown; + + case ET_CORE: + // 4 - Core file + // TODO: is there any way to detect that an core file is a kernel + // related executable by inspecting the program headers, section + // headers, symbols, or any other flag bits??? + return eStrataUnknown; + + default: + break; + } + return eStrataUnknown; +} + diff --git a/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/source/Plugins/ObjectFile/ELF/ObjectFileELF.h new file mode 100644 index 000000000000..2365101f4275 --- /dev/null +++ b/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -0,0 +1,333 @@ +//===-- ObjectFileELF.h --------------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFileELF_h_ +#define liblldb_ObjectFileELF_h_ + +#include <stdint.h> +#include <vector> + +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Core/UUID.h" + +#include "ELFHeader.h" + +//------------------------------------------------------------------------------ +/// @class ObjectFileELF +/// @brief Generic ELF object file reader. +/// +/// This class provides a generic ELF (32/64 bit) reader plugin implementing the +/// ObjectFile protocol. +class ObjectFileELF : + public lldb_private::ObjectFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t file_offset, + lldb::offset_t length); + + static lldb_private::ObjectFile * + CreateMemoryInstance (const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr); + + static size_t + GetModuleSpecifications (const lldb_private::FileSpec& file, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + static bool + MagicBytesMatch (lldb::DataBufferSP& data_sp, + lldb::addr_t offset, + lldb::addr_t length); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + //------------------------------------------------------------------ + // ObjectFile Protocol. + //------------------------------------------------------------------ + virtual + ~ObjectFileELF(); + + virtual bool + ParseHeader(); + + virtual lldb::ByteOrder + GetByteOrder() const; + + virtual bool + IsExecutable () const; + + virtual uint32_t + GetAddressByteSize() const; + + virtual lldb_private::Symtab * + GetSymtab(); + + virtual bool + IsStripped (); + + virtual void + CreateSections (lldb_private::SectionList &unified_section_list); + + virtual void + Dump(lldb_private::Stream *s); + + virtual bool + GetArchitecture (lldb_private::ArchSpec &arch); + + virtual bool + GetUUID(lldb_private::UUID* uuid); + + virtual lldb_private::FileSpecList + GetDebugSymbolFilePaths(); + + virtual uint32_t + GetDependentModules(lldb_private::FileSpecList& files); + + virtual lldb_private::Address + GetImageInfoAddress(); + + virtual lldb_private::Address + GetEntryPointAddress (); + + virtual ObjectFile::Type + CalculateType(); + + virtual ObjectFile::Strata + CalculateStrata(); + + // Returns number of program headers found in the ELF file. + size_t + GetProgramHeaderCount(); + + // Returns the program header with the given index. + const elf::ELFProgramHeader * + GetProgramHeaderByIndex(lldb::user_id_t id); + + // Returns segment data for the given index. + lldb_private::DataExtractor + GetSegmentDataByIndex(lldb::user_id_t id); + +private: + ObjectFileELF(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset, + const lldb_private::FileSpec* file, + lldb::offset_t offset, + lldb::offset_t length); + + typedef std::vector<elf::ELFProgramHeader> ProgramHeaderColl; + typedef ProgramHeaderColl::iterator ProgramHeaderCollIter; + typedef ProgramHeaderColl::const_iterator ProgramHeaderCollConstIter; + + struct ELFSectionHeaderInfo : public elf::ELFSectionHeader + { + lldb_private::ConstString section_name; + }; + typedef std::vector<ELFSectionHeaderInfo> SectionHeaderColl; + typedef SectionHeaderColl::iterator SectionHeaderCollIter; + typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter; + + typedef std::vector<elf::ELFDynamic> DynamicSymbolColl; + typedef DynamicSymbolColl::iterator DynamicSymbolCollIter; + typedef DynamicSymbolColl::const_iterator DynamicSymbolCollConstIter; + + /// Version of this reader common to all plugins based on this class. + static const uint32_t m_plugin_version = 1; + + /// ELF file header. + elf::ELFHeader m_header; + + /// ELF build ID. + lldb_private::UUID m_uuid; + + /// ELF .gnu_debuglink file and crc data if available. + std::string m_gnu_debuglink_file; + uint32_t m_gnu_debuglink_crc; + + /// Collection of program headers. + ProgramHeaderColl m_program_headers; + + /// Collection of section headers. + SectionHeaderColl m_section_headers; + + /// Collection of symbols from the dynamic table. + DynamicSymbolColl m_dynamic_symbols; + + /// List of file specifications corresponding to the modules (shared + /// libraries) on which this object file depends. + mutable std::unique_ptr<lldb_private::FileSpecList> m_filespec_ap; + + /// Cached value of the entry point for this module. + lldb_private::Address m_entry_point_address; + + /// Returns a 1 based index of the given section header. + size_t + SectionIndex(const SectionHeaderCollIter &I); + + /// Returns a 1 based index of the given section header. + size_t + SectionIndex(const SectionHeaderCollConstIter &I) const; + + /// Parses all section headers present in this object file and populates + /// m_program_headers. This method will compute the header list only once. + /// Returns the number of headers parsed. + size_t + ParseProgramHeaders(); + + /// Parses all section headers present in this object file and populates + /// m_section_headers. This method will compute the header list only once. + /// Returns the number of headers parsed. + size_t + ParseSectionHeaders(); + + /// Parses the elf section headers and returns the uuid, debug link name, crc. + static size_t + GetSectionHeaderInfo(SectionHeaderColl §ion_headers, + lldb_private::DataExtractor &data, + const elf::ELFHeader &header, + lldb_private::UUID &uuid, + std::string &gnu_debuglink_file, + uint32_t &gnu_debuglink_crc); + + /// Scans the dynamic section and locates all dependent modules (shared + /// libraries) populating m_filespec_ap. This method will compute the + /// dependent module list only once. Returns the number of dependent + /// modules parsed. + size_t + ParseDependentModules(); + + /// Parses the dynamic symbol table and populates m_dynamic_symbols. The + /// vector retains the order as found in the object file. Returns the + /// number of dynamic symbols parsed. + size_t + ParseDynamicSymbols(); + + /// Populates m_symtab_ap will all non-dynamic linker symbols. This method + /// will parse the symbols only once. Returns the number of symbols parsed. + unsigned + ParseSymbolTable(lldb_private::Symtab *symbol_table, + lldb::user_id_t start_id, + lldb_private::Section *symtab); + + /// Helper routine for ParseSymbolTable(). + unsigned + ParseSymbols(lldb_private::Symtab *symbol_table, + lldb::user_id_t start_id, + lldb_private::SectionList *section_list, + const size_t num_symbols, + const lldb_private::DataExtractor &symtab_data, + const lldb_private::DataExtractor &strtab_data); + + /// Scans the relocation entries and adds a set of artificial symbols to the + /// given symbol table for each PLT slot. Returns the number of symbols + /// added. + unsigned + ParseTrampolineSymbols(lldb_private::Symtab *symbol_table, + lldb::user_id_t start_id, + const ELFSectionHeaderInfo *rela_hdr, + lldb::user_id_t section_id); + + /// Returns the section header with the given id or NULL. + const ELFSectionHeaderInfo * + GetSectionHeaderByIndex(lldb::user_id_t id); + + /// @name ELF header dump routines + //@{ + static void + DumpELFHeader(lldb_private::Stream *s, const elf::ELFHeader& header); + + static void + DumpELFHeader_e_ident_EI_DATA(lldb_private::Stream *s, + unsigned char ei_data); + + static void + DumpELFHeader_e_type(lldb_private::Stream *s, elf::elf_half e_type); + //@} + + /// @name ELF program header dump routines + //@{ + void + DumpELFProgramHeaders(lldb_private::Stream *s); + + static void + DumpELFProgramHeader(lldb_private::Stream *s, + const elf::ELFProgramHeader &ph); + + static void + DumpELFProgramHeader_p_type(lldb_private::Stream *s, elf::elf_word p_type); + + static void + DumpELFProgramHeader_p_flags(lldb_private::Stream *s, + elf::elf_word p_flags); + //@} + + /// @name ELF section header dump routines + //@{ + void + DumpELFSectionHeaders(lldb_private::Stream *s); + + static void + DumpELFSectionHeader(lldb_private::Stream *s, + const ELFSectionHeaderInfo& sh); + + static void + DumpELFSectionHeader_sh_type(lldb_private::Stream *s, + elf::elf_word sh_type); + + static void + DumpELFSectionHeader_sh_flags(lldb_private::Stream *s, + elf::elf_xword sh_flags); + //@} + + /// ELF dependent module dump routine. + void + DumpDependentModules(lldb_private::Stream *s); + + const elf::ELFDynamic * + FindDynamicSymbol(unsigned tag); + + unsigned + PLTRelocationType(); +}; + +#endif // #ifndef liblldb_ObjectFileELF_h_ diff --git a/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp b/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp new file mode 100644 index 000000000000..4cfe38e5840f --- /dev/null +++ b/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp @@ -0,0 +1,417 @@ +//===-- OperatingSystemPython.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#ifndef LLDB_DISABLE_PYTHON + +#include "OperatingSystemPython.h" +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/PythonDataObjects.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/Thread.h" +#include "Plugins/Process/Utility/DynamicRegisterInfo.h" +#include "Plugins/Process/Utility/RegisterContextDummy.h" +#include "Plugins/Process/Utility/RegisterContextMemory.h" +#include "Plugins/Process/Utility/ThreadMemory.h" + +using namespace lldb; +using namespace lldb_private; + +void +OperatingSystemPython::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +OperatingSystemPython::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +OperatingSystem * +OperatingSystemPython::CreateInstance (Process *process, bool force) +{ + // Python OperatingSystem plug-ins must be requested by name, so force must be true + FileSpec python_os_plugin_spec (process->GetPythonOSPluginPath()); + if (python_os_plugin_spec && python_os_plugin_spec.Exists()) + { + std::unique_ptr<OperatingSystemPython> os_ap (new OperatingSystemPython (process, python_os_plugin_spec)); + if (os_ap.get() && os_ap->IsValid()) + return os_ap.release(); + } + return NULL; +} + + +ConstString +OperatingSystemPython::GetPluginNameStatic() +{ + static ConstString g_name("python"); + return g_name; +} + +const char * +OperatingSystemPython::GetPluginDescriptionStatic() +{ + return "Operating system plug-in that gathers OS information from a python class that implements the necessary OperatingSystem functionality."; +} + + +OperatingSystemPython::OperatingSystemPython (lldb_private::Process *process, const FileSpec &python_module_path) : + OperatingSystem (process), + m_thread_list_valobj_sp (), + m_register_info_ap (), + m_interpreter (NULL), + m_python_object_sp () +{ + if (!process) + return; + TargetSP target_sp = process->CalculateTarget(); + if (!target_sp) + return; + m_interpreter = target_sp->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (m_interpreter) + { + + std::string os_plugin_class_name (python_module_path.GetFilename().AsCString("")); + if (!os_plugin_class_name.empty()) + { + const bool init_session = false; + const bool allow_reload = true; + char python_module_path_cstr[PATH_MAX]; + python_module_path.GetPath(python_module_path_cstr, sizeof(python_module_path_cstr)); + Error error; + if (m_interpreter->LoadScriptingModule (python_module_path_cstr, allow_reload, init_session, error)) + { + // Strip the ".py" extension if there is one + size_t py_extension_pos = os_plugin_class_name.rfind(".py"); + if (py_extension_pos != std::string::npos) + os_plugin_class_name.erase (py_extension_pos); + // Add ".OperatingSystemPlugIn" to the module name to get a string like "modulename.OperatingSystemPlugIn" + os_plugin_class_name += ".OperatingSystemPlugIn"; + ScriptInterpreterObjectSP object_sp = m_interpreter->OSPlugin_CreatePluginObject(os_plugin_class_name.c_str(), process->CalculateProcess()); + if (object_sp && object_sp->GetObject()) + m_python_object_sp = object_sp; + } + } + } +} + +OperatingSystemPython::~OperatingSystemPython () +{ +} + +DynamicRegisterInfo * +OperatingSystemPython::GetDynamicRegisterInfo () +{ + if (m_register_info_ap.get() == NULL) + { + if (!m_interpreter || !m_python_object_sp) + return NULL; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OS)); + + if (log) + log->Printf ("OperatingSystemPython::GetDynamicRegisterInfo() fetching thread register definitions from python for pid %" PRIu64, m_process->GetID()); + + PythonDictionary dictionary(m_interpreter->OSPlugin_RegisterInfo(m_python_object_sp)); + if (!dictionary) + return NULL; + + m_register_info_ap.reset (new DynamicRegisterInfo (dictionary)); + assert (m_register_info_ap->GetNumRegisters() > 0); + assert (m_register_info_ap->GetNumRegisterSets() > 0); + } + return m_register_info_ap.get(); +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +OperatingSystemPython::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +OperatingSystemPython::GetPluginVersion() +{ + return 1; +} + +bool +OperatingSystemPython::UpdateThreadList (ThreadList &old_thread_list, + ThreadList &core_thread_list, + ThreadList &new_thread_list) +{ + if (!m_interpreter || !m_python_object_sp) + return false; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OS)); + + // First thing we have to do is get the API lock, and the run lock. We're going to change the thread + // content of the process, and we're going to use python, which requires the API lock to do it. + // So get & hold that. This is a recursive lock so we can grant it to any Python code called on the stack below us. + Target &target = m_process->GetTarget(); + Mutex::Locker api_locker (target.GetAPIMutex()); + + if (log) + log->Printf ("OperatingSystemPython::UpdateThreadList() fetching thread data from python for pid %" PRIu64, m_process->GetID()); + + // The threads that are in "new_thread_list" upon entry are the threads from the + // lldb_private::Process subclass, no memory threads will be in this list. + + auto lock = m_interpreter->AcquireInterpreterLock(); // to make sure threads_list stays alive + PythonList threads_list(m_interpreter->OSPlugin_ThreadsInfo(m_python_object_sp)); + if (threads_list) + { + if (log) + { + StreamString strm; + threads_list.Dump(strm); + log->Printf("threads_list = %s", strm.GetString().c_str()); + } + uint32_t i; + const uint32_t num_threads = threads_list.GetSize(); + if (num_threads > 0) + { + for (i=0; i<num_threads; ++i) + { + PythonDictionary thread_dict(threads_list.GetItemAtIndex(i)); + if (thread_dict) + { + ThreadSP thread_sp (CreateThreadFromThreadInfo (thread_dict, core_thread_list, old_thread_list, NULL)); + if (thread_sp) + new_thread_list.AddThread(thread_sp); + } + } + } + } + + // No new threads added from the thread info array gotten from python, just + // display the core threads. + if (new_thread_list.GetSize(false) == 0) + new_thread_list = core_thread_list; + + return new_thread_list.GetSize(false) > 0; +} + +ThreadSP +OperatingSystemPython::CreateThreadFromThreadInfo (PythonDictionary &thread_dict, + ThreadList &core_thread_list, + ThreadList &old_thread_list, + bool *did_create_ptr) +{ + ThreadSP thread_sp; + if (thread_dict) + { + PythonString tid_pystr("tid"); + const tid_t tid = thread_dict.GetItemForKeyAsInteger (tid_pystr, LLDB_INVALID_THREAD_ID); + if (tid != LLDB_INVALID_THREAD_ID) + { + PythonString core_pystr("core"); + PythonString name_pystr("name"); + PythonString queue_pystr("queue"); + //PythonString state_pystr("state"); + //PythonString stop_reason_pystr("stop_reason"); + PythonString reg_data_addr_pystr ("register_data_addr"); + + const uint32_t core_number = thread_dict.GetItemForKeyAsInteger (core_pystr, UINT32_MAX); + const addr_t reg_data_addr = thread_dict.GetItemForKeyAsInteger (reg_data_addr_pystr, LLDB_INVALID_ADDRESS); + const char *name = thread_dict.GetItemForKeyAsString (name_pystr); + const char *queue = thread_dict.GetItemForKeyAsString (queue_pystr); + //const char *state = thread_dict.GetItemForKeyAsString (state_pystr); + //const char *stop_reason = thread_dict.GetItemForKeyAsString (stop_reason_pystr); + + // See if a thread already exists for "tid" + thread_sp = old_thread_list.FindThreadByID (tid, false); + if (thread_sp) + { + // A thread already does exist for "tid", make sure it was an operating system + // plug-in generated thread. + if (!IsOperatingSystemPluginThread(thread_sp)) + { + // We have thread ID overlap between the protocol threads and the + // operating system threads, clear the thread so we create an + // operating system thread for this. + thread_sp.reset(); + } + } + + if (!thread_sp) + { + if (did_create_ptr) + *did_create_ptr = true; + thread_sp.reset (new ThreadMemory (*m_process, + tid, + name, + queue, + reg_data_addr)); + + } + + if (core_number < core_thread_list.GetSize(false)) + { + ThreadSP core_thread_sp (core_thread_list.GetThreadAtIndex(core_number, false)); + if (core_thread_sp) + { + ThreadSP backing_core_thread_sp (core_thread_sp->GetBackingThread()); + if (backing_core_thread_sp) + { + thread_sp->SetBackingThread(backing_core_thread_sp); + } + else + { + thread_sp->SetBackingThread(core_thread_sp); + } + } + } + } + } + return thread_sp; +} + + + +void +OperatingSystemPython::ThreadWasSelected (Thread *thread) +{ +} + +RegisterContextSP +OperatingSystemPython::CreateRegisterContextForThread (Thread *thread, addr_t reg_data_addr) +{ + RegisterContextSP reg_ctx_sp; + if (!m_interpreter || !m_python_object_sp || !thread) + return reg_ctx_sp; + + if (!IsOperatingSystemPluginThread(thread->shared_from_this())) + return reg_ctx_sp; + + // First thing we have to do is get the API lock, and the run lock. We're going to change the thread + // content of the process, and we're going to use python, which requires the API lock to do it. + // So get & hold that. This is a recursive lock so we can grant it to any Python code called on the stack below us. + Target &target = m_process->GetTarget(); + Mutex::Locker api_locker (target.GetAPIMutex()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + auto lock = m_interpreter->AcquireInterpreterLock(); // to make sure python objects stays alive + if (reg_data_addr != LLDB_INVALID_ADDRESS) + { + // The registers data is in contiguous memory, just create the register + // context using the address provided + if (log) + log->Printf ("OperatingSystemPython::CreateRegisterContextForThread (tid = 0x%" PRIx64 ", 0x%" PRIx64 ", reg_data_addr = 0x%" PRIx64 ") creating memory register context", + thread->GetID(), + thread->GetProtocolID(), + reg_data_addr); + reg_ctx_sp.reset (new RegisterContextMemory (*thread, 0, *GetDynamicRegisterInfo (), reg_data_addr)); + } + else + { + // No register data address is provided, query the python plug-in to let + // it make up the data as it sees fit + if (log) + log->Printf ("OperatingSystemPython::CreateRegisterContextForThread (tid = 0x%" PRIx64 ", 0x%" PRIx64 ") fetching register data from python", + thread->GetID(), + thread->GetProtocolID()); + + PythonString reg_context_data(m_interpreter->OSPlugin_RegisterContextData (m_python_object_sp, thread->GetID())); + if (reg_context_data) + { + DataBufferSP data_sp (new DataBufferHeap (reg_context_data.GetString(), + reg_context_data.GetSize())); + if (data_sp->GetByteSize()) + { + RegisterContextMemory *reg_ctx_memory = new RegisterContextMemory (*thread, 0, *GetDynamicRegisterInfo (), LLDB_INVALID_ADDRESS); + if (reg_ctx_memory) + { + reg_ctx_sp.reset(reg_ctx_memory); + reg_ctx_memory->SetAllRegisterData (data_sp); + } + } + } + } + // if we still have no register data, fallback on a dummy context to avoid crashing + if (!reg_ctx_sp) + { + if (log) + log->Printf ("OperatingSystemPython::CreateRegisterContextForThread (tid = 0x%" PRIx64 ") forcing a dummy register context", thread->GetID()); + reg_ctx_sp.reset(new RegisterContextDummy(*thread,0,target.GetArchitecture().GetAddressByteSize())); + } + return reg_ctx_sp; +} + +StopInfoSP +OperatingSystemPython::CreateThreadStopReason (lldb_private::Thread *thread) +{ + // We should have gotten the thread stop info from the dictionary of data for + // the thread in the initial call to get_thread_info(), this should have been + // cached so we can return it here + StopInfoSP stop_info_sp; //(StopInfo::CreateStopReasonWithSignal (*thread, SIGSTOP)); + return stop_info_sp; +} + +lldb::ThreadSP +OperatingSystemPython::CreateThread (lldb::tid_t tid, addr_t context) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + + if (log) + log->Printf ("OperatingSystemPython::CreateThread (tid = 0x%" PRIx64 ", context = 0x%" PRIx64 ") fetching register data from python", tid, context); + + if (m_interpreter && m_python_object_sp) + { + // First thing we have to do is get the API lock, and the run lock. We're going to change the thread + // content of the process, and we're going to use python, which requires the API lock to do it. + // So get & hold that. This is a recursive lock so we can grant it to any Python code called on the stack below us. + Target &target = m_process->GetTarget(); + Mutex::Locker api_locker (target.GetAPIMutex()); + + auto lock = m_interpreter->AcquireInterpreterLock(); // to make sure thread_info_dict stays alive + PythonDictionary thread_info_dict (m_interpreter->OSPlugin_CreateThread(m_python_object_sp, tid, context)); + if (thread_info_dict) + { + ThreadList core_threads(m_process); + ThreadList &thread_list = m_process->GetThreadList(); + bool did_create = false; + ThreadSP thread_sp (CreateThreadFromThreadInfo (thread_info_dict, core_threads, thread_list, &did_create)); + if (did_create) + thread_list.AddThread(thread_sp); + return thread_sp; + } + } + return ThreadSP(); +} + + + +#endif // #ifndef LLDB_DISABLE_PYTHON diff --git a/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h b/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h new file mode 100644 index 000000000000..077039e50d56 --- /dev/null +++ b/source/Plugins/OperatingSystem/Python/OperatingSystemPython.h @@ -0,0 +1,109 @@ +//===-- OperatingSystemPython.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLDB_DISABLE_PYTHON + +#ifndef liblldb_OperatingSystemPython_h_ +#define liblldb_OperatingSystemPython_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/OperatingSystem.h" + +class DynamicRegisterInfo; + +class OperatingSystemPython : public lldb_private::OperatingSystem +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static lldb_private::OperatingSystem * + CreateInstance (lldb_private::Process *process, bool force); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------------ + OperatingSystemPython (lldb_private::Process *process, + const lldb_private::FileSpec &python_module_path); + + virtual + ~OperatingSystemPython (); + + //------------------------------------------------------------------ + // lldb_private::PluginInterface Methods + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + //------------------------------------------------------------------ + // lldb_private::OperatingSystem Methods + //------------------------------------------------------------------ + virtual bool + UpdateThreadList (lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &real_thread_list, + lldb_private::ThreadList &new_thread_list); + + virtual void + ThreadWasSelected (lldb_private::Thread *thread); + + virtual lldb::RegisterContextSP + CreateRegisterContextForThread (lldb_private::Thread *thread, + lldb::addr_t reg_data_addr); + + virtual lldb::StopInfoSP + CreateThreadStopReason (lldb_private::Thread *thread); + + //------------------------------------------------------------------ + // Method for lazy creation of threads on demand + //------------------------------------------------------------------ + virtual lldb::ThreadSP + CreateThread (lldb::tid_t tid, lldb::addr_t context); + +protected: + + bool IsValid() const + { + return m_python_object_sp && m_python_object_sp->GetObject() != NULL; + } + + lldb::ThreadSP + CreateThreadFromThreadInfo (lldb_private::PythonDictionary &thread_dict, + lldb_private::ThreadList &core_thread_list, + lldb_private::ThreadList &old_thread_list, + bool *did_create_ptr); + + DynamicRegisterInfo * + GetDynamicRegisterInfo (); + + lldb::ValueObjectSP m_thread_list_valobj_sp; + std::unique_ptr<DynamicRegisterInfo> m_register_info_ap; + lldb_private::ScriptInterpreter *m_interpreter; + lldb::ScriptInterpreterObjectSP m_python_object_sp; + +}; + +#endif // #ifndef liblldb_OperatingSystemPython_h_ +#endif // #ifndef LLDB_DISABLE_PYTHON diff --git a/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp new file mode 100644 index 000000000000..dc0917255d9b --- /dev/null +++ b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp @@ -0,0 +1,648 @@ +//===-- PlatformFreeBSD.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "PlatformFreeBSD.h" + +// C Includes +#include <stdio.h> +#include <sys/utsname.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + +Platform * +PlatformFreeBSD::CreateInstance (bool force, const lldb_private::ArchSpec *arch) +{ + // The only time we create an instance is when we are creating a remote + // freebsd platform + const bool is_host = false; + + bool create = force; + if (create == false && arch && arch->IsValid()) + { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getVendor()) + { + case llvm::Triple::PC: + create = true; + break; + +#if defined(__FreeBSD__) || defined(__OpenBSD__) + // Only accept "unknown" for the vendor if the host is BSD and + // it "unknown" wasn't specified (it was just returned becasue it + // was NOT specified) + case llvm::Triple::UnknownArch: + create = !arch->TripleVendorWasSpecified(); + break; +#endif + default: + break; + } + + if (create) + { + switch (triple.getOS()) + { + case llvm::Triple::FreeBSD: + case llvm::Triple::KFreeBSD: + break; + +#if defined(__FreeBSD__) || defined(__OpenBSD__) + // Only accept "unknown" for the OS if the host is BSD and + // it "unknown" wasn't specified (it was just returned becasue it + // was NOT specified) + case llvm::Triple::UnknownOS: + create = arch->TripleOSWasSpecified(); + break; +#endif + default: + create = false; + break; + } + } + } + if (create) + return new PlatformFreeBSD (is_host); + return NULL; + +} + +lldb_private::ConstString +PlatformFreeBSD::GetPluginNameStatic (bool is_host) +{ + if (is_host) + { + static ConstString g_host_name(Platform::GetHostPlatformName ()); + return g_host_name; + } + else + { + static ConstString g_remote_name("remote-freebsd"); + return g_remote_name; + } +} + +const char * +PlatformFreeBSD::GetDescriptionStatic (bool is_host) +{ + if (is_host) + return "Local FreeBSD user platform plug-in."; + else + return "Remote FreeBSD user platform plug-in."; +} + +static uint32_t g_initialize_count = 0; + +void +PlatformFreeBSD::Initialize () +{ + if (g_initialize_count++ == 0) + { +#if defined (__FreeBSD__) + // Force a host flag to true for the default platform object. + PlatformSP default_platform_sp (new PlatformFreeBSD(true)); + default_platform_sp->SetSystemArchitecture (Host::GetArchitecture()); + Platform::SetDefaultPlatform (default_platform_sp); +#endif + PluginManager::RegisterPlugin(PlatformFreeBSD::GetPluginNameStatic(false), + PlatformFreeBSD::GetDescriptionStatic(false), + PlatformFreeBSD::CreateInstance); + } +} + +void +PlatformFreeBSD::Terminate () +{ + if (g_initialize_count > 0 && --g_initialize_count == 0) + PluginManager::UnregisterPlugin (PlatformFreeBSD::CreateInstance); +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformFreeBSD::PlatformFreeBSD (bool is_host) : +Platform(is_host) +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformFreeBSD::~PlatformFreeBSD() +{ +} + + +Error +PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, + const ArchSpec &exe_arch, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + // Nothing special to do here, just use the actual file and architecture + + char exe_path[PATH_MAX]; + FileSpec resolved_exe_file (exe_file); + + if (IsHost()) + { + // If we have "ls" as the exe_file, resolve the executable location based on + // the current path variables + if (!resolved_exe_file.Exists()) + { + exe_file.GetPath(exe_path, sizeof(exe_path)); + resolved_exe_file.SetFile(exe_path, true); + } + + if (!resolved_exe_file.Exists()) + resolved_exe_file.ResolveExecutableLocation (); + + if (resolved_exe_file.Exists()) + error.Clear(); + else + { + exe_file.GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path); + } + } + else + { + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->ResolveExecutable (exe_file, + exe_arch, + exe_module_sp, + module_search_paths_ptr); + } + else + { + // We may connect to a process and use the provided executable (Don't use local $PATH). + + // Resolve any executable within a bundle on MacOSX + Host::ResolveExecutableInBundle (resolved_exe_file); + + if (resolved_exe_file.Exists()) { + error.Clear(); + } + else + { + exe_file.GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path); + } + } + } + + + if (error.Success()) + { + ModuleSpec module_spec (resolved_exe_file, exe_arch); + if (module_spec.GetArchitecture().IsValid()) + { + error = ModuleList::GetSharedModule (module_spec, + exe_module_sp, + module_search_paths_ptr, + NULL, + NULL); + + if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) + { + exe_module_sp.reset(); + error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", + exe_file.GetPath().c_str(), + exe_arch.GetArchitectureName()); + } + } + else + { + // No valid architecture was specified, ask the platform for + // the architectures that we should be using (in the correct order) + // and see if we can find a match that way + StreamString arch_names; + ArchSpec platform_arch; + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, platform_arch); ++idx) + { + error = ModuleList::GetSharedModule (module_spec, + exe_module_sp, + module_search_paths_ptr, + NULL, + NULL); + // Did we find an executable using one of the + if (error.Success()) + { + if (exe_module_sp && exe_module_sp->GetObjectFile()) + break; + else + error.SetErrorToGenericError(); + } + + if (idx > 0) + arch_names.PutCString (", "); + arch_names.PutCString (platform_arch.GetArchitectureName()); + } + + if (error.Fail() || !exe_module_sp) + { + error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", + exe_file.GetPath().c_str(), + GetPluginName().GetCString(), + arch_names.GetString().c_str()); + } + } + } + else + { + error.SetErrorStringWithFormat ("'%s' does not exist", + exe_file.GetPath().c_str()); + } + + return error; +} + +size_t +PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site) +{ + ArchSpec arch = target.GetArchitecture(); + const uint8_t *trap_opcode = NULL; + size_t trap_opcode_size = 0; + + switch (arch.GetCore()) + { + default: + assert(false && "Unhandled architecture in PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode()"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_64_x86_64: + { + static const uint8_t g_i386_opcode[] = { 0xCC }; + trap_opcode = g_i386_opcode; + trap_opcode_size = sizeof(g_i386_opcode); + } + break; + } + + if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) + return trap_opcode_size; + + return 0; +} + +bool +PlatformFreeBSD::GetRemoteOSVersion () +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetOSVersion (m_major_os_version, + m_minor_os_version, + m_update_os_version); + return false; +} + +bool +PlatformFreeBSD::GetRemoteOSBuildString (std::string &s) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteOSBuildString (s); + s.clear(); + return false; +} + +bool +PlatformFreeBSD::GetRemoteOSKernelDescription (std::string &s) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteOSKernelDescription (s); + s.clear(); + return false; +} + +// Remote Platform subclasses need to override this function +ArchSpec +PlatformFreeBSD::GetRemoteSystemArchitecture () +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteSystemArchitecture (); + return ArchSpec(); +} + + +const char * +PlatformFreeBSD::GetHostname () +{ + if (IsHost()) + return Platform::GetHostname(); + + if (m_remote_platform_sp) + return m_remote_platform_sp->GetHostname (); + return NULL; +} + +bool +PlatformFreeBSD::IsConnected () const +{ + if (IsHost()) + return true; + else if (m_remote_platform_sp) + return m_remote_platform_sp->IsConnected(); + return false; +} + +Error +PlatformFreeBSD::ConnectRemote (Args& args) +{ + Error error; + if (IsHost()) + { + error.SetErrorStringWithFormat ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString()); + } + else + { + if (!m_remote_platform_sp) + m_remote_platform_sp = Platform::Create ("remote-gdb-server", error); + + if (m_remote_platform_sp) + { + if (error.Success()) + { + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->ConnectRemote (args); + } + else + { + error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>"); + } + } + } + else + error.SetErrorString ("failed to create a 'remote-gdb-server' platform"); + + if (error.Fail()) + m_remote_platform_sp.reset(); + } + + return error; +} + +Error +PlatformFreeBSD::DisconnectRemote () +{ + Error error; + + if (IsHost()) + { + error.SetErrorStringWithFormat ("can't disconnect from the host platform '%s', always connected", GetPluginName().GetCString()); + } + else + { + if (m_remote_platform_sp) + error = m_remote_platform_sp->DisconnectRemote (); + else + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +bool +PlatformFreeBSD::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + bool success = false; + if (IsHost()) + { + success = Platform::GetProcessInfo (pid, process_info); + } + else if (m_remote_platform_sp) + { + success = m_remote_platform_sp->GetProcessInfo (pid, process_info); + } + return success; +} + + + +uint32_t +PlatformFreeBSD::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + uint32_t match_count = 0; + if (IsHost()) + { + // Let the base class figure out the host details + match_count = Platform::FindProcesses (match_info, process_infos); + } + else + { + // If we are remote, we can only return results if we are connected + if (m_remote_platform_sp) + match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos); + } + return match_count; +} + +Error +PlatformFreeBSD::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + if (IsHost()) + { + error = Platform::LaunchProcess (launch_info); + } + else + { + if (m_remote_platform_sp) + error = m_remote_platform_sp->LaunchProcess (launch_info); + else + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +lldb::ProcessSP +PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, + Listener &listener, + Error &error) +{ + lldb::ProcessSP process_sp; + if (IsHost()) + { + if (target == NULL) + { + TargetSP new_target_sp; + ArchSpec emptyArchSpec; + + error = debugger.GetTargetList().CreateTarget (debugger, + NULL, + emptyArchSpec, + false, + m_remote_platform_sp, + new_target_sp); + target = new_target_sp.get(); + } + else + error.Clear(); + + if (target && error.Success()) + { + debugger.GetTargetList().SetSelectedTarget(target); + // The freebsd always currently uses the GDB remote debugger plug-in + // so even when debugging locally we are debugging remotely! + // Just like the darwin plugin. + process_sp = target->CreateProcess (listener, "gdb-remote", NULL); + + if (process_sp) + error = process_sp->Attach (attach_info); + } + } + else + { + if (m_remote_platform_sp) + process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error); + else + error.SetErrorString ("the platform is not currently connected"); + } + return process_sp; +} + +const char * +PlatformFreeBSD::GetUserName (uint32_t uid) +{ + // Check the cache in Platform in case we have already looked this uid up + const char *user_name = Platform::GetUserName(uid); + if (user_name) + return user_name; + + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetUserName(uid); + return NULL; +} + +const char * +PlatformFreeBSD::GetGroupName (uint32_t gid) +{ + const char *group_name = Platform::GetGroupName(gid); + if (group_name) + return group_name; + + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetGroupName(gid); + return NULL; +} + + +// From PlatformMacOSX only +Error +PlatformFreeBSD::GetFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetFile (platform_file, uuid_ptr, local_file); + } + + // Default to the local case + local_file = platform_file; + return Error(); +} + +Error +PlatformFreeBSD::GetSharedModule (const ModuleSpec &module_spec, + ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + ModuleSP *old_module_sp_ptr, + bool *did_create_ptr) +{ + Error error; + module_sp.reset(); + + if (IsRemote()) + { + // If we have a remote platform always, let it try and locate + // the shared module first. + if (m_remote_platform_sp) + { + error = m_remote_platform_sp->GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + } + } + + if (!module_sp) + { + // Fall back to the local platform and find the file locally + error = Platform::GetSharedModule (module_spec, + module_sp, + module_search_paths_ptr, + old_module_sp_ptr, + did_create_ptr); + } + if (module_sp) + module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); + return error; +} + + +bool +PlatformFreeBSD::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + // From macosx;s plugin code. For FreeBSD we may want to support more archs. + if (idx == 0) + { + arch = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + return arch.IsValid(); + } + else if (idx == 1) + { + ArchSpec platform_arch (Host::GetArchitecture (Host::eSystemDefaultArchitecture)); + ArchSpec platform_arch64 (Host::GetArchitecture (Host::eSystemDefaultArchitecture64)); + if (platform_arch.IsExactMatch(platform_arch64)) + { + // This freebsd platform supports both 32 and 64 bit. Since we already + // returned the 64 bit arch for idx == 0, return the 32 bit arch + // for idx == 1 + arch = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); + return arch.IsValid(); + } + } + return false; +} + +void +PlatformFreeBSD::GetStatus (Stream &strm) +{ + struct utsname un; + + if (uname(&un)) { + strm << "FreeBSD"; + return; + } + + strm << "Host: " << un.sysname << ' ' << un.release << ' ' << un.version << '\n'; + Platform::GetStatus(strm); +} diff --git a/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h new file mode 100644 index 000000000000..4aa158eb6e87 --- /dev/null +++ b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h @@ -0,0 +1,162 @@ +//===-- PlatformFreeBSD.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformFreeBSD_h_ +#define liblldb_PlatformFreeBSD_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Platform.h" + +class PlatformFreeBSD : public lldb_private::Platform +{ +public: + // Mostly taken from PlatformDarwin and PlatformMacOSX + + //------------------------------------------------------------ + // Class functions + //------------------------------------------------------------ + static lldb_private::Platform* + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::ConstString + GetPluginNameStatic (bool is_host); + + static const char * + GetDescriptionStatic (bool is_host); + + //------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------ + PlatformFreeBSD (bool is_host); + + virtual + ~PlatformFreeBSD(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName() + { + return GetPluginNameStatic (IsHost()); + } + + virtual uint32_t + GetPluginVersion() + { + return 1; + } + + virtual const char * + GetDescription () + { + return GetDescriptionStatic(IsHost()); + } + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + virtual lldb_private::Error + ResolveExecutable (const lldb_private::FileSpec &exe_file, + const lldb_private::ArchSpec &arch, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr); + + virtual size_t + GetSoftwareBreakpointTrapOpcode (lldb_private::Target &target, + lldb_private::BreakpointSite *bp_site); + + virtual bool + GetRemoteOSVersion (); + + virtual bool + GetRemoteOSBuildString (std::string &s); + + virtual bool + GetRemoteOSKernelDescription (std::string &s); + + // Remote Platform subclasses need to override this function + virtual lldb_private::ArchSpec + GetRemoteSystemArchitecture (); + + virtual bool + IsConnected () const; + + virtual lldb_private::Error + ConnectRemote (lldb_private::Args& args); + + virtual lldb_private::Error + DisconnectRemote (); + + virtual const char * + GetHostname (); + + virtual const char * + GetUserName (uint32_t uid); + + virtual const char * + GetGroupName (uint32_t gid); + + virtual bool + GetProcessInfo (lldb::pid_t pid, + lldb_private::ProcessInstanceInfo &proc_info); + + virtual uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos); + + virtual lldb_private::Error + LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info); + + virtual lldb::ProcessSP + Attach(lldb_private::ProcessAttachInfo &attach_info, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Listener &listener, + lldb_private::Error &error); + + // FreeBSD processes can not be launched by spawning and attaching. + virtual bool + CanDebugProcess () { return false; } + + // Only on PlatformMacOSX: + virtual lldb_private::Error + GetFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID* uuid, lldb_private::FileSpec &local_file); + + lldb_private::Error + GetSharedModule (const lldb_private::ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr); + + virtual bool + GetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); + + virtual void + GetStatus (lldb_private::Stream &strm); + +protected: + lldb::PlatformSP m_remote_platform_sp; // Allow multiple ways to connect to a remote freebsd OS + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformFreeBSD); +}; + +#endif // liblldb_PlatformFreeBSD_h_ diff --git a/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp new file mode 100644 index 000000000000..684d1921e920 --- /dev/null +++ b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -0,0 +1,418 @@ +//===-- PlatformRemoteGDBServer.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "PlatformRemoteGDBServer.h" + +// C Includes +#include <sys/sysctl.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +static bool g_initialized = false; + +void +PlatformRemoteGDBServer::Initialize () +{ + if (g_initialized == false) + { + g_initialized = true; + PluginManager::RegisterPlugin (PlatformRemoteGDBServer::GetPluginNameStatic(), + PlatformRemoteGDBServer::GetDescriptionStatic(), + PlatformRemoteGDBServer::CreateInstance); + } +} + +void +PlatformRemoteGDBServer::Terminate () +{ + if (g_initialized) + { + g_initialized = false; + PluginManager::UnregisterPlugin (PlatformRemoteGDBServer::CreateInstance); + } +} + +Platform* +PlatformRemoteGDBServer::CreateInstance (bool force, const lldb_private::ArchSpec *arch) +{ + bool create = force; + if (!create) + { + create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified(); + } + if (create) + return new PlatformRemoteGDBServer (); + return NULL; +} + + +lldb_private::ConstString +PlatformRemoteGDBServer::GetPluginNameStatic() +{ + static ConstString g_name("remote-gdb-server"); + return g_name; +} + +const char * +PlatformRemoteGDBServer::GetDescriptionStatic() +{ + return "A platform that uses the GDB remote protocol as the communication transport."; +} + +const char * +PlatformRemoteGDBServer::GetDescription () +{ + if (m_platform_description.empty()) + { + if (IsConnected()) + { + // Send the get description packet + } + } + + if (!m_platform_description.empty()) + return m_platform_description.c_str(); + return GetDescriptionStatic(); +} + +Error +PlatformRemoteGDBServer::ResolveExecutable (const FileSpec &exe_file, + const ArchSpec &exe_arch, + lldb::ModuleSP &exe_module_sp, + const FileSpecList *module_search_paths_ptr) +{ + Error error; + error.SetErrorString ("PlatformRemoteGDBServer::ResolveExecutable() is unimplemented"); + return error; +} + +Error +PlatformRemoteGDBServer::GetFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) +{ + // Default to the local case + local_file = platform_file; + return Error(); +} + +//------------------------------------------------------------------ +/// Default Constructor +//------------------------------------------------------------------ +PlatformRemoteGDBServer::PlatformRemoteGDBServer () : + Platform(false), // This is a remote platform + m_gdb_client(true) +{ +} + +//------------------------------------------------------------------ +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +//------------------------------------------------------------------ +PlatformRemoteGDBServer::~PlatformRemoteGDBServer() +{ +} + +bool +PlatformRemoteGDBServer::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) +{ + return false; +} + +size_t +PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site) +{ + // This isn't needed if the z/Z packets are supported in the GDB remote + // server. But we might need a packet to detect this. + return 0; +} + +bool +PlatformRemoteGDBServer::GetRemoteOSVersion () +{ + uint32_t major, minor, update; + if (m_gdb_client.GetOSVersion (major, minor, update)) + { + m_major_os_version = major; + m_minor_os_version = minor; + m_update_os_version = update; + return true; + } + return false; +} + +bool +PlatformRemoteGDBServer::GetRemoteOSBuildString (std::string &s) +{ + return m_gdb_client.GetOSBuildString (s); +} + +bool +PlatformRemoteGDBServer::GetRemoteOSKernelDescription (std::string &s) +{ + return m_gdb_client.GetOSKernelDescription (s); +} + +// Remote Platform subclasses need to override this function +ArchSpec +PlatformRemoteGDBServer::GetRemoteSystemArchitecture () +{ + return m_gdb_client.GetSystemArchitecture(); +} + +bool +PlatformRemoteGDBServer::IsConnected () const +{ + return m_gdb_client.IsConnected(); +} + +Error +PlatformRemoteGDBServer::ConnectRemote (Args& args) +{ + Error error; + if (IsConnected()) + { + error.SetErrorStringWithFormat ("the platform is already connected to '%s', execute 'platform disconnect' to close the current connection", + GetHostname()); + } + else + { + if (args.GetArgumentCount() == 1) + { + const char *url = args.GetArgumentAtIndex(0); + m_gdb_client.SetConnection (new ConnectionFileDescriptor()); + const ConnectionStatus status = m_gdb_client.Connect(url, &error); + if (status == eConnectionStatusSuccess) + { + if (m_gdb_client.HandshakeWithServer(&error)) + { + m_gdb_client.QueryNoAckModeSupported(); + m_gdb_client.GetHostInfo(); +#if 0 + m_gdb_client.TestPacketSpeed(10000); +#endif + } + else + { + m_gdb_client.Disconnect(); + } + } + } + else + { + error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>"); + } + } + + return error; +} + +Error +PlatformRemoteGDBServer::DisconnectRemote () +{ + Error error; + m_gdb_client.Disconnect(&error); + return error; +} + +const char * +PlatformRemoteGDBServer::GetHostname () +{ + m_gdb_client.GetHostname (m_name); + if (m_name.empty()) + return NULL; + return m_name.c_str(); +} + +const char * +PlatformRemoteGDBServer::GetUserName (uint32_t uid) +{ + // Try and get a cache user name first + const char *cached_user_name = Platform::GetUserName(uid); + if (cached_user_name) + return cached_user_name; + std::string name; + if (m_gdb_client.GetUserName(uid, name)) + return SetCachedUserName(uid, name.c_str(), name.size()); + + SetUserNameNotFound(uid); // Negative cache so we don't keep sending packets + return NULL; +} + +const char * +PlatformRemoteGDBServer::GetGroupName (uint32_t gid) +{ + const char *cached_group_name = Platform::GetGroupName(gid); + if (cached_group_name) + return cached_group_name; + std::string name; + if (m_gdb_client.GetGroupName(gid, name)) + return SetCachedGroupName(gid, name.c_str(), name.size()); + + SetGroupNameNotFound(gid); // Negative cache so we don't keep sending packets + return NULL; +} + +uint32_t +PlatformRemoteGDBServer::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + return m_gdb_client.FindProcesses (match_info, process_infos); +} + +bool +PlatformRemoteGDBServer::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + return m_gdb_client.GetProcessInfo (pid, process_info); +} + + +Error +PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + m_gdb_client.SetSTDIN ("/dev/null"); + m_gdb_client.SetSTDOUT ("/dev/null"); + m_gdb_client.SetSTDERR ("/dev/null"); + m_gdb_client.SetDisableASLR (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)); + + const char *working_dir = launch_info.GetWorkingDirectory(); + if (working_dir && working_dir[0]) + { + m_gdb_client.SetWorkingDir (working_dir); + } + + // Send the environment and the program + arguments after we connect + const char **argv = launch_info.GetArguments().GetConstArgumentVector(); + const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector(); + + if (envp) + { + const char *env_entry; + for (int i=0; (env_entry = envp[i]); ++i) + { + if (m_gdb_client.SendEnvironmentPacket(env_entry) != 0) + break; + } + } + const uint32_t old_packet_timeout = m_gdb_client.SetPacketTimeout (5); + int arg_packet_err = m_gdb_client.SendArgumentsPacket (argv); + m_gdb_client.SetPacketTimeout (old_packet_timeout); + if (arg_packet_err == 0) + { + std::string error_str; + if (m_gdb_client.GetLaunchSuccess (error_str)) + { + pid = m_gdb_client.GetCurrentProcessID (); + if (pid != LLDB_INVALID_PROCESS_ID) + launch_info.SetProcessID (pid); + } + else + { + error.SetErrorString (error_str.c_str()); + } + } + else + { + error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); + } + return error; +} + +lldb::ProcessSP +PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Listener &listener, + Error &error) +{ + lldb::ProcessSP process_sp; + if (IsRemote()) + { + if (IsConnected()) + { + uint16_t port = m_gdb_client.LaunchGDBserverAndGetPort(); + + if (port == 0) + { + error.SetErrorStringWithFormat ("unable to launch a GDB server on '%s'", GetHostname ()); + } + else + { + if (target == NULL) + { + TargetSP new_target_sp; + + error = debugger.GetTargetList().CreateTarget (debugger, + NULL, + NULL, + false, + NULL, + new_target_sp); + target = new_target_sp.get(); + } + else + error.Clear(); + + if (target && error.Success()) + { + debugger.GetTargetList().SetSelectedTarget(target); + + // The darwin always currently uses the GDB remote debugger plug-in + // so even when debugging locally we are debugging remotely! + process_sp = target->CreateProcess (listener, "gdb-remote", NULL); + + if (process_sp) + { + char connect_url[256]; + const int connect_url_len = ::snprintf (connect_url, + sizeof(connect_url), + "connect://%s:%u", + GetHostname (), + port); + assert (connect_url_len < (int)sizeof(connect_url)); + error = process_sp->ConnectRemote (NULL, connect_url); + if (error.Success()) + error = process_sp->Attach(attach_info); + } + } + } + } + else + { + error.SetErrorString("not connected to remote gdb server"); + } + } + return process_sp; +} + + diff --git a/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h new file mode 100644 index 000000000000..22b3dd49be54 --- /dev/null +++ b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -0,0 +1,147 @@ +//===-- PlatformRemoteGDBServer.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PlatformRemoteGDBServer_h_ +#define liblldb_PlatformRemoteGDBServer_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Platform.h" +#include "../../Process/gdb-remote/GDBRemoteCommunicationClient.h" + +class PlatformRemoteGDBServer : public lldb_private::Platform +{ +public: + + static void + Initialize (); + + static void + Terminate (); + + static lldb_private::Platform* + CreateInstance (bool force, const lldb_private::ArchSpec *arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetDescriptionStatic(); + + + PlatformRemoteGDBServer (); + + virtual + ~PlatformRemoteGDBServer(); + + //------------------------------------------------------------ + // lldb_private::PluginInterface functions + //------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName() + { + return GetPluginNameStatic(); + } + + virtual uint32_t + GetPluginVersion() + { + return 1; + } + + + //------------------------------------------------------------ + // lldb_private::Platform functions + //------------------------------------------------------------ + virtual lldb_private::Error + ResolveExecutable (const lldb_private::FileSpec &exe_file, + const lldb_private::ArchSpec &arch, + lldb::ModuleSP &module_sp, + const lldb_private::FileSpecList *module_search_paths_ptr); + + virtual const char * + GetDescription (); + + virtual lldb_private::Error + GetFile (const lldb_private::FileSpec &platform_file, + const lldb_private::UUID *uuid_ptr, + lldb_private::FileSpec &local_file); + + virtual bool + GetProcessInfo (lldb::pid_t pid, + lldb_private::ProcessInstanceInfo &proc_info); + + virtual uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &match_info, + lldb_private::ProcessInstanceInfoList &process_infos); + + virtual lldb_private::Error + LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info); + + virtual lldb::ProcessSP + Attach (lldb_private::ProcessAttachInfo &attach_info, + lldb_private::Debugger &debugger, + lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one + lldb_private::Listener &listener, + lldb_private::Error &error); + + virtual bool + GetSupportedArchitectureAtIndex (uint32_t idx, lldb_private::ArchSpec &arch); + + virtual size_t + GetSoftwareBreakpointTrapOpcode (lldb_private::Target &target, + lldb_private::BreakpointSite *bp_site); + + virtual bool + GetRemoteOSVersion (); + + virtual bool + GetRemoteOSBuildString (std::string &s); + + virtual bool + GetRemoteOSKernelDescription (std::string &s); + + // Remote Platform subclasses need to override this function + virtual lldb_private::ArchSpec + GetRemoteSystemArchitecture (); + + // Remote subclasses should override this and return a valid instance + // name if connected. + virtual const char * + GetHostname (); + + virtual const char * + GetUserName (uint32_t uid); + + virtual const char * + GetGroupName (uint32_t gid); + + virtual bool + IsConnected () const; + + virtual lldb_private::Error + ConnectRemote (lldb_private::Args& args); + + virtual lldb_private::Error + DisconnectRemote (); + +protected: + GDBRemoteCommunicationClient m_gdb_client; + std::string m_platform_description; // After we connect we can get a more complete description of what we are connected to + +private: + DISALLOW_COPY_AND_ASSIGN (PlatformRemoteGDBServer); + +}; + +#endif // liblldb_PlatformRemoteGDBServer_h_ diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp new file mode 100644 index 000000000000..ea26d972b860 --- /dev/null +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp @@ -0,0 +1,132 @@ +//===-- ProcessFreeBSD.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <errno.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Target.h" + +#include "ProcessFreeBSD.h" +#include "ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/InferiorCallPOSIX.h" +#include "ProcessMonitor.h" +#include "POSIXThread.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------------ +// Static functions. + +lldb::ProcessSP +ProcessFreeBSD::CreateInstance(Target& target, + Listener &listener, + const FileSpec *crash_file_path) +{ + lldb::ProcessSP process_sp; + if (crash_file_path == NULL) + process_sp.reset(new ProcessFreeBSD (target, listener)); + return process_sp; +} + +void +ProcessFreeBSD::Initialize() +{ + static bool g_initialized = false; + + if (!g_initialized) + { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + Log::Callbacks log_callbacks = { + ProcessPOSIXLog::DisableLog, + ProcessPOSIXLog::EnableLog, + ProcessPOSIXLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessFreeBSD::GetPluginNameStatic(), log_callbacks); + ProcessPOSIXLog::RegisterPluginName(GetPluginNameStatic()); + g_initialized = true; + } +} + +lldb_private::ConstString +ProcessFreeBSD::GetPluginNameStatic() +{ + static ConstString g_name("freebsd"); + return g_name; +} + +const char * +ProcessFreeBSD::GetPluginDescriptionStatic() +{ + return "Process plugin for FreeBSD"; +} + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + +lldb_private::ConstString +ProcessFreeBSD::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessFreeBSD::GetPluginVersion() +{ + return 1; +} + +void +ProcessFreeBSD::GetPluginCommandHelp(const char *command, Stream *strm) +{ +} + +Error +ProcessFreeBSD::ExecutePluginCommand(Args &command, Stream *strm) +{ + return Error(1, eErrorTypeGeneric); +} + +Log * +ProcessFreeBSD::EnablePluginLogging(Stream *strm, Args &command) +{ + return NULL; +} + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessFreeBSD::ProcessFreeBSD(Target& target, Listener &listener) + : ProcessPOSIX(target, listener) +{ +} + +void +ProcessFreeBSD::Terminate() +{ +} + +bool +ProcessFreeBSD::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // XXX haxx + new_thread_list = old_thread_list; + + return false; +} diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h new file mode 100644 index 000000000000..5f79b74cad30 --- /dev/null +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.h @@ -0,0 +1,82 @@ +//===-- ProcessFreeBSD.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessFreeBSD_H_ +#define liblldb_ProcessFreeBSD_H_ + +// C Includes + +// C++ Includes +#include <queue> + +// Other libraries and framework includes +#include "lldb/Target/Process.h" +#include "lldb/Target/ThreadList.h" +#include "ProcessMessage.h" +#include "ProcessPOSIX.h" + +class ProcessMonitor; + +class ProcessFreeBSD : + public ProcessPOSIX +{ + +public: + //------------------------------------------------------------------ + // Static functions. + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance(lldb_private::Target& target, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessFreeBSD(lldb_private::Target& target, + lldb_private::Listener &listener); + + virtual bool + UpdateThreadList(lldb_private::ThreadList &old_thread_list, lldb_private::ThreadList &new_thread_list); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp(const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand(lldb_private::Args &command, + lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging(lldb_private::Stream *strm, + lldb_private::Args &command); + +}; + +#endif // liblldb_MacOSXProcess_H_ diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp new file mode 100644 index 000000000000..9fd51d2d640a --- /dev/null +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -0,0 +1,1677 @@ +//===-- ProcessMonitor.cpp ------------------------------------ -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <errno.h> +#include <poll.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/wait.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Utility/PseudoTerminal.h" + + +#include "POSIXThread.h" +#include "ProcessFreeBSD.h" +#include "ProcessPOSIXLog.h" +#include "ProcessMonitor.h" + +extern "C" { + extern char ** environ; + } + +using namespace lldb; +using namespace lldb_private; + +// We disable the tracing of ptrace calls for integration builds to +// avoid the additional indirection and checks. +#ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION +// Wrapper for ptrace to catch errors and log calls. + +const char * +Get_PT_IO_OP(int op) +{ + switch (op) { + case PIOD_READ_D: return "READ_D"; + case PIOD_WRITE_D: return "WRITE_D"; + case PIOD_READ_I: return "READ_I"; + case PIOD_WRITE_I: return "WRITE_I"; + default: return "Unknown op"; + } +} + +// Wrapper for ptrace to catch errors and log calls. +// Note that ptrace sets errno on error because -1 is reserved as a valid result. +extern long +PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, + const char* reqName, const char* file, int line) +{ + long int result; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PTRACE)); + + if (log) { + log->Printf("ptrace(%s, %lu, %p, %x) called from file %s line %d", + reqName, pid, addr, data, file, line); + if (req == PT_IO) { + struct ptrace_io_desc *pi = (struct ptrace_io_desc *) addr; + + log->Printf("PT_IO: op=%s offs=%zx size=%ld", + Get_PT_IO_OP(pi->piod_op), (size_t)pi->piod_offs, pi->piod_len); + } + } + + //PtraceDisplayBytes(req, data); + + errno = 0; + result = ptrace(req, pid, (caddr_t) addr, data); + + //PtraceDisplayBytes(req, data); + + if (log && errno != 0) + { + const char* str; + switch (errno) + { + case ESRCH: str = "ESRCH"; break; + case EINVAL: str = "EINVAL"; break; + case EBUSY: str = "EBUSY"; break; + case EPERM: str = "EPERM"; break; + default: str = "<unknown>"; + } + log->Printf("ptrace() failed; errno=%d (%s)", errno, str); + } + +#ifdef __amd64__ + if (log) { + if (req == PT_GETREGS) { + struct reg *r = (struct reg *) addr; + + log->Printf("PT_GETREGS: ip=0x%lx", r->r_rip); + log->Printf("PT_GETREGS: sp=0x%lx", r->r_rsp); + log->Printf("PT_GETREGS: bp=0x%lx", r->r_rbp); + log->Printf("PT_GETREGS: ax=0x%lx", r->r_rax); + } + } +#endif + + return result; +} + +// Wrapper for ptrace when logging is not required. +// Sets errno to 0 prior to calling ptrace. +extern long +PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data) +{ + long result = 0; + errno = 0; + result = ptrace(req, pid, (caddr_t)addr, data); + return result; +} + +#define PTRACE(req, pid, addr, data) \ + PtraceWrapper((req), (pid), (addr), (data), #req, __FILE__, __LINE__) +#else + PtraceWrapper((req), (pid), (addr), (data)) +#endif + +//------------------------------------------------------------------------------ +// Static implementations of ProcessMonitor::ReadMemory and +// ProcessMonitor::WriteMemory. This enables mutual recursion between these +// functions without needed to go thru the thread funnel. + +static size_t +DoReadMemory(lldb::pid_t pid, lldb::addr_t vm_addr, void *buf, size_t size, + Error &error) +{ + struct ptrace_io_desc pi_desc; + + pi_desc.piod_op = PIOD_READ_D; + pi_desc.piod_offs = (void *)vm_addr; + pi_desc.piod_addr = buf; + pi_desc.piod_len = size; + + if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) + error.SetErrorToErrno(); + return pi_desc.piod_len; +} + +static size_t +DoWriteMemory(lldb::pid_t pid, lldb::addr_t vm_addr, const void *buf, + size_t size, Error &error) +{ + struct ptrace_io_desc pi_desc; + + pi_desc.piod_op = PIOD_WRITE_D; + pi_desc.piod_offs = (void *)vm_addr; + pi_desc.piod_addr = (void *)buf; + pi_desc.piod_len = size; + + if (PTRACE(PT_IO, pid, (caddr_t)&pi_desc, 0) < 0) + error.SetErrorToErrno(); + return pi_desc.piod_len; +} + +// Simple helper function to ensure flags are enabled on the given file +// descriptor. +static bool +EnsureFDFlags(int fd, int flags, Error &error) +{ + int status; + + if ((status = fcntl(fd, F_GETFL)) == -1) + { + error.SetErrorToErrno(); + return false; + } + + if (fcntl(fd, F_SETFL, status | flags) == -1) + { + error.SetErrorToErrno(); + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +/// @class Operation +/// @brief Represents a ProcessMonitor operation. +/// +/// Under FreeBSD, it is not possible to ptrace() from any other thread but the +/// one that spawned or attached to the process from the start. Therefore, when +/// a ProcessMonitor is asked to deliver or change the state of an inferior +/// process the operation must be "funneled" to a specific thread to perform the +/// task. The Operation class provides an abstract base for all services the +/// ProcessMonitor must perform via the single virtual function Execute, thus +/// encapsulating the code that needs to run in the privileged context. +class Operation +{ +public: + virtual ~Operation() {} + virtual void Execute(ProcessMonitor *monitor) = 0; +}; + +//------------------------------------------------------------------------------ +/// @class ReadOperation +/// @brief Implements ProcessMonitor::ReadMemory. +class ReadOperation : public Operation +{ +public: + ReadOperation(lldb::addr_t addr, void *buff, size_t size, + Error &error, size_t &result) + : m_addr(addr), m_buff(buff), m_size(size), + m_error(error), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::addr_t m_addr; + void *m_buff; + size_t m_size; + Error &m_error; + size_t &m_result; +}; + +void +ReadOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + + m_result = DoReadMemory(pid, m_addr, m_buff, m_size, m_error); +} + +//------------------------------------------------------------------------------ +/// @class WriteOperation +/// @brief Implements ProcessMonitor::WriteMemory. +class WriteOperation : public Operation +{ +public: + WriteOperation(lldb::addr_t addr, const void *buff, size_t size, + Error &error, size_t &result) + : m_addr(addr), m_buff(buff), m_size(size), + m_error(error), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::addr_t m_addr; + const void *m_buff; + size_t m_size; + Error &m_error; + size_t &m_result; +}; + +void +WriteOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + + m_result = DoWriteMemory(pid, m_addr, m_buff, m_size, m_error); +} + +//------------------------------------------------------------------------------ +/// @class ReadRegOperation +/// @brief Implements ProcessMonitor::ReadRegisterValue. +class ReadRegOperation : public Operation +{ +public: + ReadRegOperation(lldb::tid_t tid, unsigned offset, unsigned size, + RegisterValue &value, bool &result) + : m_tid(tid), m_offset(offset), m_size(size), + m_value(value), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + unsigned m_offset; + unsigned m_size; + RegisterValue &m_value; + bool &m_result; +}; + +void +ReadRegOperation::Execute(ProcessMonitor *monitor) +{ + struct reg regs; + int rc; + + if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0)) < 0) { + m_result = false; + } else { + if (m_size == sizeof(uintptr_t)) + m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); + else + memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); + m_result = true; + } +} + +//------------------------------------------------------------------------------ +/// @class WriteRegOperation +/// @brief Implements ProcessMonitor::WriteRegisterValue. +class WriteRegOperation : public Operation +{ +public: + WriteRegOperation(lldb::tid_t tid, unsigned offset, + const RegisterValue &value, bool &result) + : m_tid(tid), m_offset(offset), + m_value(value), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + unsigned m_offset; + const RegisterValue &m_value; + bool &m_result; +}; + +void +WriteRegOperation::Execute(ProcessMonitor *monitor) +{ + struct reg regs; + + if (PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0) < 0) { + m_result = false; + return; + } + *(uintptr_t *)(((caddr_t)®s) + m_offset) = (uintptr_t)m_value.GetAsUInt64(); + if (PTRACE(PT_SETREGS, m_tid, (caddr_t)®s, 0) < 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class ReadGPROperation +/// @brief Implements ProcessMonitor::ReadGPR. +class ReadGPROperation : public Operation +{ +public: + ReadGPROperation(lldb::tid_t tid, void *buf, bool &result) + : m_tid(tid), m_buf(buf), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + void *m_buf; + bool &m_result; +}; + +void +ReadGPROperation::Execute(ProcessMonitor *monitor) +{ + int rc; + + errno = 0; + rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)m_buf, 0); + if (errno != 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class ReadFPROperation +/// @brief Implements ProcessMonitor::ReadFPR. +class ReadFPROperation : public Operation +{ +public: + ReadFPROperation(lldb::tid_t tid, void *buf, bool &result) + : m_tid(tid), m_buf(buf), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + void *m_buf; + bool &m_result; +}; + +void +ReadFPROperation::Execute(ProcessMonitor *monitor) +{ + if (PTRACE(PT_GETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class WriteGPROperation +/// @brief Implements ProcessMonitor::WriteGPR. +class WriteGPROperation : public Operation +{ +public: + WriteGPROperation(lldb::tid_t tid, void *buf, bool &result) + : m_tid(tid), m_buf(buf), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + void *m_buf; + bool &m_result; +}; + +void +WriteGPROperation::Execute(ProcessMonitor *monitor) +{ + if (PTRACE(PT_SETREGS, m_tid, (caddr_t)m_buf, 0) < 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class WriteFPROperation +/// @brief Implements ProcessMonitor::WriteFPR. +class WriteFPROperation : public Operation +{ +public: + WriteFPROperation(lldb::tid_t tid, void *buf, bool &result) + : m_tid(tid), m_buf(buf), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + void *m_buf; + bool &m_result; +}; + +void +WriteFPROperation::Execute(ProcessMonitor *monitor) +{ + if (PTRACE(PT_SETFPREGS, m_tid, (caddr_t)m_buf, 0) < 0) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class ResumeOperation +/// @brief Implements ProcessMonitor::Resume. +class ResumeOperation : public Operation +{ +public: + ResumeOperation(lldb::tid_t tid, uint32_t signo, bool &result) : + m_tid(tid), m_signo(signo), m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + uint32_t m_signo; + bool &m_result; +}; + +void +ResumeOperation::Execute(ProcessMonitor *monitor) +{ + int data = 0; + + if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) + data = m_signo; + + if (PTRACE(PT_CONTINUE, m_tid, (caddr_t)1, data)) + { + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + if (log) + log->Printf ("ResumeOperation (%" PRIu64 ") failed: %s", m_tid, strerror(errno)); + m_result = false; + } + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class SingleStepOperation +/// @brief Implements ProcessMonitor::SingleStep. +class SingleStepOperation : public Operation +{ +public: + SingleStepOperation(lldb::tid_t tid, uint32_t signo, bool &result) + : m_tid(tid), m_signo(signo), m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + uint32_t m_signo; + bool &m_result; +}; + +void +SingleStepOperation::Execute(ProcessMonitor *monitor) +{ + int data = 0; + + if (m_signo != LLDB_INVALID_SIGNAL_NUMBER) + data = m_signo; + + if (PTRACE(PT_STEP, m_tid, NULL, data)) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class LwpInfoOperation +/// @brief Implements ProcessMonitor::GetLwpInfo. +class LwpInfoOperation : public Operation +{ +public: + LwpInfoOperation(lldb::tid_t tid, void *info, bool &result, int &ptrace_err) + : m_tid(tid), m_info(info), m_result(result), m_err(ptrace_err) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + void *m_info; + bool &m_result; + int &m_err; +}; + +void +LwpInfoOperation::Execute(ProcessMonitor *monitor) +{ + struct ptrace_lwpinfo plwp; + + if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) { + m_result = false; + m_err = errno; + } else { + memcpy(m_info, &plwp, sizeof(plwp)); + m_result = true; + } +} + +//------------------------------------------------------------------------------ +/// @class EventMessageOperation +/// @brief Implements ProcessMonitor::GetEventMessage. +class EventMessageOperation : public Operation +{ +public: + EventMessageOperation(lldb::tid_t tid, unsigned long *message, bool &result) + : m_tid(tid), m_message(message), m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + unsigned long *m_message; + bool &m_result; +}; + +void +EventMessageOperation::Execute(ProcessMonitor *monitor) +{ + struct ptrace_lwpinfo plwp; + + if (PTRACE(PT_LWPINFO, m_tid, (caddr_t)&plwp, sizeof(plwp))) + m_result = false; + else { + if (plwp.pl_flags & PL_FLAG_FORKED) { + m_message = (unsigned long *)plwp.pl_child_pid; + m_result = true; + } else + m_result = false; + } +} + +//------------------------------------------------------------------------------ +/// @class KillOperation +/// @brief Implements ProcessMonitor::BringProcessIntoLimbo. +class KillOperation : public Operation +{ +public: + KillOperation(bool &result) : m_result(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + bool &m_result; +}; + +void +KillOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + + if (PTRACE(PT_KILL, pid, NULL, 0)) + m_result = false; + else + m_result = true; +} + +//------------------------------------------------------------------------------ +/// @class DetachOperation +/// @brief Implements ProcessMonitor::BringProcessIntoLimbo. +class DetachOperation : public Operation +{ +public: + DetachOperation(Error &result) : m_error(result) { } + + void Execute(ProcessMonitor *monitor); + +private: + Error &m_error; +}; + +void +DetachOperation::Execute(ProcessMonitor *monitor) +{ + lldb::pid_t pid = monitor->GetPID(); + + if (PTRACE(PT_DETACH, pid, NULL, 0) < 0) + m_error.SetErrorToErrno(); + +} + +ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor) + : m_monitor(monitor) +{ + sem_init(&m_semaphore, 0, 0); +} + +ProcessMonitor::OperationArgs::~OperationArgs() +{ + sem_destroy(&m_semaphore); +} + +ProcessMonitor::LaunchArgs::LaunchArgs(ProcessMonitor *monitor, + lldb_private::Module *module, + char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_dir) + : OperationArgs(monitor), + m_module(module), + m_argv(argv), + m_envp(envp), + m_stdin_path(stdin_path), + m_stdout_path(stdout_path), + m_stderr_path(stderr_path), + m_working_dir(working_dir) { } + +ProcessMonitor::LaunchArgs::~LaunchArgs() +{ } + +ProcessMonitor::AttachArgs::AttachArgs(ProcessMonitor *monitor, + lldb::pid_t pid) + : OperationArgs(monitor), m_pid(pid) { } + +ProcessMonitor::AttachArgs::~AttachArgs() +{ } + +//------------------------------------------------------------------------------ +/// The basic design of the ProcessMonitor is built around two threads. +/// +/// One thread (@see SignalThread) simply blocks on a call to waitpid() looking +/// for changes in the debugee state. When a change is detected a +/// ProcessMessage is sent to the associated ProcessFreeBSD instance. This thread +/// "drives" state changes in the debugger. +/// +/// The second thread (@see OperationThread) is responsible for two things 1) +/// launching or attaching to the inferior process, and then 2) servicing +/// operations such as register reads/writes, stepping, etc. See the comments +/// on the Operation class for more info as to why this is needed. +ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, + Module *module, + const char *argv[], + const char *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_dir, + lldb_private::Error &error) + : m_process(static_cast<ProcessFreeBSD *>(process)), + m_operation_thread(LLDB_INVALID_HOST_THREAD), + m_monitor_thread(LLDB_INVALID_HOST_THREAD), + m_pid(LLDB_INVALID_PROCESS_ID), + m_server_mutex(Mutex::eMutexTypeRecursive), + m_terminal_fd(-1), + m_client_fd(-1), + m_server_fd(-1) +{ + std::unique_ptr<LaunchArgs> args; + + args.reset(new LaunchArgs(this, module, argv, envp, + stdin_path, stdout_path, stderr_path, working_dir)); + + + // Server/client descriptors. + if (!EnableIPC()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Monitor failed to initialize."); + } + + StartLaunchOpThread(args.get(), error); + if (!error.Success()) + return; + +WAIT_AGAIN: + // Wait for the operation thread to initialize. + if (sem_wait(&args->m_semaphore)) + { + if (errno == EINTR) + goto WAIT_AGAIN; + else + { + error.SetErrorToErrno(); + return; + } + } + + // Check that the launch was a success. + if (!args->m_error.Success()) + { + StopOpThread(); + error = args->m_error; + return; + } + + // Finally, start monitoring the child process for change in state. + m_monitor_thread = Host::StartMonitoringChildProcess( + ProcessMonitor::MonitorCallback, this, GetPID(), true); + if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + { + error.SetErrorToGenericError(); + error.SetErrorString("Process launch failed."); + return; + } +} + +ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, + lldb::pid_t pid, + lldb_private::Error &error) + : m_process(static_cast<ProcessFreeBSD *>(process)), + m_operation_thread(LLDB_INVALID_HOST_THREAD), + m_monitor_thread(LLDB_INVALID_HOST_THREAD), + m_pid(pid), + m_server_mutex(Mutex::eMutexTypeRecursive), + m_terminal_fd(-1), + m_client_fd(-1), + m_server_fd(-1) +{ + std::unique_ptr<AttachArgs> args; + + args.reset(new AttachArgs(this, pid)); + + // Server/client descriptors. + if (!EnableIPC()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Monitor failed to initialize."); + } + + StartAttachOpThread(args.get(), error); + if (!error.Success()) + return; + +WAIT_AGAIN: + // Wait for the operation thread to initialize. + if (sem_wait(&args->m_semaphore)) + { + if (errno == EINTR) + goto WAIT_AGAIN; + else + { + error.SetErrorToErrno(); + return; + } + } + + // Check that the attach was a success. + if (!args->m_error.Success()) + { + StopOpThread(); + error = args->m_error; + return; + } + + // Finally, start monitoring the child process for change in state. + m_monitor_thread = Host::StartMonitoringChildProcess( + ProcessMonitor::MonitorCallback, this, GetPID(), true); + if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + { + error.SetErrorToGenericError(); + error.SetErrorString("Process attach failed."); + return; + } +} + +ProcessMonitor::~ProcessMonitor() +{ + StopMonitor(); +} + +//------------------------------------------------------------------------------ +// Thread setup and tear down. +void +ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Error &error) +{ + static const char *g_thread_name = "lldb.process.freebsd.operation"; + + if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + return; + + m_operation_thread = + Host::ThreadCreate(g_thread_name, LaunchOpThread, args, &error); +} + +void * +ProcessMonitor::LaunchOpThread(void *arg) +{ + LaunchArgs *args = static_cast<LaunchArgs*>(arg); + + if (!Launch(args)) { + sem_post(&args->m_semaphore); + return NULL; + } + + ServeOperation(args); + return NULL; +} + +bool +ProcessMonitor::Launch(LaunchArgs *args) +{ + ProcessMonitor *monitor = args->m_monitor; + ProcessFreeBSD &process = monitor->GetProcess(); + lldb::ProcessSP processSP = process.shared_from_this(); + const char **argv = args->m_argv; + const char **envp = args->m_envp; + const char *stdin_path = args->m_stdin_path; + const char *stdout_path = args->m_stdout_path; + const char *stderr_path = args->m_stderr_path; + const char *working_dir = args->m_working_dir; + + lldb_utility::PseudoTerminal terminal; + const size_t err_len = 1024; + char err_str[err_len]; + lldb::pid_t pid; + + lldb::ThreadSP inferior; + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + // Propagate the environment if one is not supplied. + if (envp == NULL || envp[0] == NULL) + envp = const_cast<const char **>(environ); + + // Pseudo terminal setup. + if (!terminal.OpenFirstAvailableMaster(O_RDWR | O_NOCTTY, err_str, err_len)) + { + args->m_error.SetErrorToGenericError(); + args->m_error.SetErrorString("Could not open controlling TTY."); + goto FINISH; + } + + if ((pid = terminal.Fork(err_str, err_len)) == -1) + { + args->m_error.SetErrorToGenericError(); + args->m_error.SetErrorString("Process fork failed."); + goto FINISH; + } + + // Recognized child exit status codes. + enum { + ePtraceFailed = 1, + eDupStdinFailed, + eDupStdoutFailed, + eDupStderrFailed, + eChdirFailed, + eExecFailed + }; + + // Child process. + if (pid == 0) + { + // Trace this process. + if (PTRACE(PT_TRACE_ME, 0, NULL, 0) < 0) + exit(ePtraceFailed); + + // Do not inherit setgid powers. + setgid(getgid()); + + // Let us have our own process group. + setpgid(0, 0); + + // Dup file descriptors if needed. + // + // FIXME: If two or more of the paths are the same we needlessly open + // the same file multiple times. + if (stdin_path != NULL && stdin_path[0]) + if (!DupDescriptor(stdin_path, STDIN_FILENO, O_RDONLY)) + exit(eDupStdinFailed); + + if (stdout_path != NULL && stdout_path[0]) + if (!DupDescriptor(stdout_path, STDOUT_FILENO, O_WRONLY | O_CREAT)) + exit(eDupStdoutFailed); + + if (stderr_path != NULL && stderr_path[0]) + if (!DupDescriptor(stderr_path, STDERR_FILENO, O_WRONLY | O_CREAT)) + exit(eDupStderrFailed); + + // Change working directory + if (working_dir != NULL && working_dir[0]) + if (0 != ::chdir(working_dir)) + exit(eChdirFailed); + + // Execute. We should never return. + execve(argv[0], + const_cast<char *const *>(argv), + const_cast<char *const *>(envp)); + exit(eExecFailed); + } + + // Wait for the child process to to trap on its call to execve. + ::pid_t wpid; + int status; + if ((wpid = waitpid(pid, &status, 0)) < 0) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + else if (WIFEXITED(status)) + { + // open, dup or execve likely failed for some reason. + args->m_error.SetErrorToGenericError(); + switch (WEXITSTATUS(status)) + { + case ePtraceFailed: + args->m_error.SetErrorString("Child ptrace failed."); + break; + case eDupStdinFailed: + args->m_error.SetErrorString("Child open stdin failed."); + break; + case eDupStdoutFailed: + args->m_error.SetErrorString("Child open stdout failed."); + break; + case eDupStderrFailed: + args->m_error.SetErrorString("Child open stderr failed."); + break; + case eChdirFailed: + args->m_error.SetErrorString("Child failed to set working directory."); + break; + case eExecFailed: + args->m_error.SetErrorString("Child exec failed."); + break; + default: + args->m_error.SetErrorString("Child returned unknown exit status."); + break; + } + goto FINISH; + } + assert(WIFSTOPPED(status) && wpid == pid && + "Could not sync with inferior process."); + +#ifdef notyet + // Have the child raise an event on exit. This is used to keep the child in + // limbo until it is destroyed. + if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEEXIT) < 0) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } +#endif + // Release the master terminal descriptor and pass it off to the + // ProcessMonitor instance. Similarly stash the inferior pid. + monitor->m_terminal_fd = terminal.ReleaseMasterFileDescriptor(); + monitor->m_pid = pid; + + // Set the terminal fd to be in non blocking mode (it simplifies the + // implementation of ProcessFreeBSD::GetSTDOUT to have a non-blocking + // descriptor to read from). + if (!EnsureFDFlags(monitor->m_terminal_fd, O_NONBLOCK, args->m_error)) + goto FINISH; + + // Update the process thread list with this new thread. + inferior.reset(process.CreateNewPOSIXThread(*processSP, pid)); + if (log) + log->Printf ("ProcessMonitor::%s() adding pid = %" PRIu64, __FUNCTION__, pid); + process.GetThreadList().AddThread(inferior); + + // Let our process instance know the thread has stopped. + process.SendMessage(ProcessMessage::Trace(pid)); + +FINISH: + return args->m_error.Success(); +} + +bool +ProcessMonitor::EnableIPC() +{ + int fd[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) + return false; + + m_client_fd = fd[0]; + m_server_fd = fd[1]; + return true; +} + +void +ProcessMonitor::StartAttachOpThread(AttachArgs *args, lldb_private::Error &error) +{ + static const char *g_thread_name = "lldb.process.freebsd.operation"; + + if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + return; + + m_operation_thread = + Host::ThreadCreate(g_thread_name, AttachOpThread, args, &error); +} + +void * +ProcessMonitor::AttachOpThread(void *arg) +{ + AttachArgs *args = static_cast<AttachArgs*>(arg); + + if (!Attach(args)) + return NULL; + + ServeOperation(args); + return NULL; +} + +bool +ProcessMonitor::Attach(AttachArgs *args) +{ + lldb::pid_t pid = args->m_pid; + + ProcessMonitor *monitor = args->m_monitor; + ProcessFreeBSD &process = monitor->GetProcess(); + lldb::ProcessSP processSP = process.shared_from_this(); + ThreadList &tl = process.GetThreadList(); + lldb::ThreadSP inferior; + + if (pid <= 1) + { + args->m_error.SetErrorToGenericError(); + args->m_error.SetErrorString("Attaching to process 1 is not allowed."); + goto FINISH; + } + + // Attach to the requested process. + if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + + int status; + if ((status = waitpid(pid, NULL, 0)) < 0) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + + // Update the process thread list with the attached thread. + inferior.reset(process.CreateNewPOSIXThread(*processSP, pid)); + tl.AddThread(inferior); + + // Let our process instance know the thread has stopped. + process.SendMessage(ProcessMessage::Trace(pid)); + + FINISH: + return args->m_error.Success(); +} + +bool +ProcessMonitor::MonitorCallback(void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, + int status) +{ + ProcessMessage message; + ProcessMonitor *monitor = static_cast<ProcessMonitor*>(callback_baton); + ProcessFreeBSD *process = monitor->m_process; + assert(process); + bool stop_monitoring; + struct ptrace_lwpinfo plwp; + int ptrace_err; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + if (exited) + { + if (log) + log->Printf ("ProcessMonitor::%s() got exit signal, tid = %" PRIu64, __FUNCTION__, pid); + message = ProcessMessage::Exit(pid, status); + process->SendMessage(message); + return pid == process->GetID(); + } + + if (!monitor->GetLwpInfo(pid, &plwp, ptrace_err)) + stop_monitoring = true; // pid is gone. Bail. + else { + switch (plwp.pl_siginfo.si_signo) + { + case SIGTRAP: + message = MonitorSIGTRAP(monitor, &plwp.pl_siginfo, pid); + break; + + default: + message = MonitorSignal(monitor, &plwp.pl_siginfo, pid); + break; + } + + process->SendMessage(message); + stop_monitoring = message.GetKind() == ProcessMessage::eExitMessage; + } + + return stop_monitoring; +} + +ProcessMessage +ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, + const siginfo_t *info, lldb::pid_t pid) +{ + ProcessMessage message; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + assert(monitor); + assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!"); + + switch (info->si_code) + { + default: + assert(false && "Unexpected SIGTRAP code!"); + break; + + case (SIGTRAP /* | (PTRACE_EVENT_EXIT << 8) */): + { + // The inferior process is about to exit. Maintain the process in a + // state of "limbo" until we are explicitly commanded to detach, + // destroy, resume, etc. + unsigned long data = 0; + if (!monitor->GetEventMessage(pid, &data)) + data = -1; + if (log) + log->Printf ("ProcessMonitor::%s() received exit? event, data = %lx, pid = %" PRIu64, __FUNCTION__, data, pid); + message = ProcessMessage::Limbo(pid, (data >> 8)); + break; + } + + case 0: + case TRAP_TRACE: + if (log) + log->Printf ("ProcessMonitor::%s() received trace event, pid = %" PRIu64, __FUNCTION__, pid); + message = ProcessMessage::Trace(pid); + break; + + case SI_KERNEL: + case TRAP_BRKPT: + if (log) + log->Printf ("ProcessMonitor::%s() received breakpoint event, pid = %" PRIu64, __FUNCTION__, pid); + message = ProcessMessage::Break(pid); + break; + } + + return message; +} + +ProcessMessage +ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, + const siginfo_t *info, lldb::pid_t pid) +{ + ProcessMessage message; + int signo = info->si_signo; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, + // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a + // kill(2) or raise(3). Similarly for tgkill(2) on FreeBSD. + // + // IOW, user generated signals never generate what we consider to be a + // "crash". + // + // Similarly, ACK signals generated by this monitor. + if (info->si_code == SI_USER) + { + if (log) + log->Printf ("ProcessMonitor::%s() received signal %s with code %s, pid = %d", + __FUNCTION__, + monitor->m_process->GetUnixSignals().GetSignalAsCString (signo), + "SI_USER", + info->si_pid); + if (info->si_pid == getpid()) + return ProcessMessage::SignalDelivered(pid, signo); + else + return ProcessMessage::Signal(pid, signo); + } + + if (log) + log->Printf ("ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals().GetSignalAsCString (signo)); + + if (signo == SIGSEGV) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGILL) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGILL(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGFPE) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGFPE(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + if (signo == SIGBUS) { + lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); + ProcessMessage::CrashReason reason = GetCrashReasonForSIGBUS(info); + return ProcessMessage::Crash(pid, reason, signo, fault_addr); + } + + // Everything else is "normal" and does not require any special action on + // our part. + return ProcessMessage::Signal(pid, signo); +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGSEGV(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGSEGV); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGSEGV"); + break; + case SEGV_MAPERR: + reason = ProcessMessage::eInvalidAddress; + break; + case SEGV_ACCERR: + reason = ProcessMessage::ePrivilegedAddress; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGILL(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGILL); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGILL"); + break; + case ILL_ILLOPC: + reason = ProcessMessage::eIllegalOpcode; + break; + case ILL_ILLOPN: + reason = ProcessMessage::eIllegalOperand; + break; + case ILL_ILLADR: + reason = ProcessMessage::eIllegalAddressingMode; + break; + case ILL_ILLTRP: + reason = ProcessMessage::eIllegalTrap; + break; + case ILL_PRVOPC: + reason = ProcessMessage::ePrivilegedOpcode; + break; + case ILL_PRVREG: + reason = ProcessMessage::ePrivilegedRegister; + break; + case ILL_COPROC: + reason = ProcessMessage::eCoprocessorError; + break; + case ILL_BADSTK: + reason = ProcessMessage::eInternalStackError; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGFPE(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGFPE); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGFPE"); + break; + case FPE_INTDIV: + reason = ProcessMessage::eIntegerDivideByZero; + break; + case FPE_INTOVF: + reason = ProcessMessage::eIntegerOverflow; + break; + case FPE_FLTDIV: + reason = ProcessMessage::eFloatDivideByZero; + break; + case FPE_FLTOVF: + reason = ProcessMessage::eFloatOverflow; + break; + case FPE_FLTUND: + reason = ProcessMessage::eFloatUnderflow; + break; + case FPE_FLTRES: + reason = ProcessMessage::eFloatInexactResult; + break; + case FPE_FLTINV: + reason = ProcessMessage::eFloatInvalidOperation; + break; + case FPE_FLTSUB: + reason = ProcessMessage::eFloatSubscriptRange; + break; + } + + return reason; +} + +ProcessMessage::CrashReason +ProcessMonitor::GetCrashReasonForSIGBUS(const siginfo_t *info) +{ + ProcessMessage::CrashReason reason; + assert(info->si_signo == SIGBUS); + + reason = ProcessMessage::eInvalidCrashReason; + + switch (info->si_code) + { + default: + assert(false && "unexpected si_code for SIGBUS"); + break; + case BUS_ADRALN: + reason = ProcessMessage::eIllegalAlignment; + break; + case BUS_ADRERR: + reason = ProcessMessage::eIllegalAddress; + break; + case BUS_OBJERR: + reason = ProcessMessage::eHardwareError; + break; + } + + return reason; +} + +void +ProcessMonitor::ServeOperation(OperationArgs *args) +{ + int status; + pollfd fdset; + + ProcessMonitor *monitor = args->m_monitor; + + fdset.fd = monitor->m_server_fd; + fdset.events = POLLIN | POLLPRI; + fdset.revents = 0; + + // We are finised with the arguments and are ready to go. Sync with the + // parent thread and start serving operations on the inferior. + sem_post(&args->m_semaphore); + + for (;;) + { + if ((status = poll(&fdset, 1, -1)) < 0) + { + switch (errno) + { + default: + assert(false && "Unexpected poll() failure!"); + continue; + + case EINTR: continue; // Just poll again. + case EBADF: return; // Connection terminated. + } + } + + assert(status == 1 && "Too many descriptors!"); + + if (fdset.revents & POLLIN) + { + Operation *op = NULL; + + READ_AGAIN: + if ((status = read(fdset.fd, &op, sizeof(op))) < 0) + { + // There is only one acceptable failure. + assert(errno == EINTR); + goto READ_AGAIN; + } + if (status == 0) + continue; // Poll again. The connection probably terminated. + assert(status == sizeof(op)); + op->Execute(monitor); + write(fdset.fd, &op, sizeof(op)); + } + } +} + +void +ProcessMonitor::DoOperation(Operation *op) +{ + int status; + Operation *ack = NULL; + Mutex::Locker lock(m_server_mutex); + + // FIXME: Do proper error checking here. + write(m_client_fd, &op, sizeof(op)); + +READ_AGAIN: + if ((status = read(m_client_fd, &ack, sizeof(ack))) < 0) + { + // If interrupted by a signal handler try again. Otherwise the monitor + // thread probably died and we have a stale file descriptor -- abort the + // operation. + if (errno == EINTR) + goto READ_AGAIN; + return; + } + + assert(status == sizeof(ack)); + assert(ack == op && "Invalid monitor thread response!"); +} + +size_t +ProcessMonitor::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + Error &error) +{ + size_t result; + ReadOperation op(vm_addr, buf, size, error, result); + DoOperation(&op); + return result; +} + +size_t +ProcessMonitor::WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + lldb_private::Error &error) +{ + size_t result; + WriteOperation op(vm_addr, buf, size, error, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char* reg_name, + unsigned size, RegisterValue &value) +{ + bool result; + ReadRegOperation op(tid, offset, size, value, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::WriteRegisterValue(lldb::tid_t tid, unsigned offset, + const char* reg_name, const RegisterValue &value) +{ + bool result; + WriteRegOperation op(tid, offset, value, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size) +{ + bool result; + ReadGPROperation op(tid, buf, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size) +{ + bool result; + ReadFPROperation op(tid, buf, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) +{ + return false; +} + +bool +ProcessMonitor::WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size) +{ + bool result; + WriteGPROperation op(tid, buf, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size) +{ + bool result; + WriteFPROperation op(tid, buf, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset) +{ + return false; +} + +bool +ProcessMonitor::Resume(lldb::tid_t tid, uint32_t signo) +{ + bool result; + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessMonitor::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid, + m_process->GetUnixSignals().GetSignalAsCString (signo)); + ResumeOperation op(tid, signo, result); + DoOperation(&op); + if (log) + log->Printf ("ProcessMonitor::%s() resuming result = %s", __FUNCTION__, result ? "true" : "false"); + return result; +} + +bool +ProcessMonitor::SingleStep(lldb::tid_t tid, uint32_t signo) +{ + bool result; + SingleStepOperation op(tid, signo, result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::BringProcessIntoLimbo() +{ + bool result; + KillOperation op(result); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &ptrace_err) +{ + bool result; + LwpInfoOperation op(tid, lwpinfo, result, ptrace_err); + DoOperation(&op); + return result; +} + +bool +ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) +{ + bool result; + EventMessageOperation op(tid, message, result); + DoOperation(&op); + return result; +} + +lldb_private::Error +ProcessMonitor::Detach(lldb::tid_t tid) +{ + lldb_private::Error error; + if (tid != LLDB_INVALID_THREAD_ID) + { + DetachOperation op(error); + DoOperation(&op); + } + return error; +} + +bool +ProcessMonitor::DupDescriptor(const char *path, int fd, int flags) +{ + int target_fd = open(path, flags, 0666); + + if (target_fd == -1) + return false; + + return (dup2(target_fd, fd) == -1) ? false : true; +} + +void +ProcessMonitor::StopMonitoringChildProcess() +{ + lldb::thread_result_t thread_result; + + if (IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + { + Host::ThreadCancel(m_monitor_thread, NULL); + Host::ThreadJoin(m_monitor_thread, &thread_result, NULL); + m_monitor_thread = LLDB_INVALID_HOST_THREAD; + } +} + +void +ProcessMonitor::StopMonitor() +{ + StopMonitoringChildProcess(); + StopOpThread(); + CloseFD(m_terminal_fd); + CloseFD(m_client_fd); + CloseFD(m_server_fd); +} + +void +ProcessMonitor::StopOpThread() +{ + lldb::thread_result_t result; + + if (!IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + return; + + Host::ThreadCancel(m_operation_thread, NULL); + Host::ThreadJoin(m_operation_thread, &result, NULL); + m_operation_thread = LLDB_INVALID_HOST_THREAD; +} + +void +ProcessMonitor::CloseFD(int &fd) +{ + if (fd != -1) + { + close(fd); + fd = -1; + } +} diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/source/Plugins/Process/FreeBSD/ProcessMonitor.h new file mode 100644 index 000000000000..ce66c03f2f8c --- /dev/null +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.h @@ -0,0 +1,322 @@ +//===-- ProcessMonitor.h -------------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessMonitor_H_ +#define liblldb_ProcessMonitor_H_ + +// C Includes +#include <semaphore.h> +#include <signal.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/lldb-types.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private +{ +class Error; +class Module; +class Scalar; +} // End lldb_private namespace. + +class ProcessFreeBSD; +class Operation; + +/// @class ProcessMonitor +/// @brief Manages communication with the inferior (debugee) process. +/// +/// Upon construction, this class prepares and launches an inferior process for +/// debugging. +/// +/// Changes in the inferior process state are propagated to the associated +/// ProcessFreeBSD instance by calling ProcessFreeBSD::SendMessage with the +/// appropriate ProcessMessage events. +/// +/// A purposely minimal set of operations are provided to interrogate and change +/// the inferior process state. +class ProcessMonitor +{ +public: + + /// Launches an inferior process ready for debugging. Forms the + /// implementation of Process::DoLaunch. + ProcessMonitor(ProcessPOSIX *process, + lldb_private::Module *module, + char const *argv[], + char const *envp[], + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_dir, + lldb_private::Error &error); + + ProcessMonitor(ProcessPOSIX *process, + lldb::pid_t pid, + lldb_private::Error &error); + + ~ProcessMonitor(); + + /// Provides the process number of debugee. + lldb::pid_t + GetPID() const { return m_pid; } + + /// Returns the process associated with this ProcessMonitor. + ProcessFreeBSD & + GetProcess() { return *m_process; } + + /// Returns a file descriptor to the controlling terminal of the inferior + /// process. + /// + /// Reads from this file descriptor yield both the standard output and + /// standard error of this debugee. Even if stderr and stdout were + /// redirected on launch it may still happen that data is available on this + /// descriptor (if the inferior process opens /dev/tty, for example). + /// + /// If this monitor was attached to an existing process this method returns + /// -1. + int + GetTerminalFD() const { return m_terminal_fd; } + + /// Reads @p size bytes from address @vm_adder in the inferior process + /// address space. + /// + /// This method is provided to implement Process::DoReadMemory. + size_t + ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, + lldb_private::Error &error); + + /// Writes @p size bytes from address @p vm_adder in the inferior process + /// address space. + /// + /// This method is provided to implement Process::DoWriteMemory. + size_t + WriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + lldb_private::Error &error); + + /// Reads the contents from the register identified by the given (architecture + /// dependent) offset. + /// + /// This method is provided for use by RegisterContextFreeBSD derivatives. + /// FIXME: The FreeBSD implementation of this function should use tid in order + /// to enable support for debugging threaded programs. + bool + ReadRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, + unsigned size, lldb_private::RegisterValue &value); + + /// Writes the given value to the register identified by the given + /// (architecture dependent) offset. + /// + /// This method is provided for use by RegisterContextFreeBSD derivatives. + /// FIXME: The FreeBSD implementation of this function should use tid in order + /// to enable support for debugging threaded programs. + bool + WriteRegisterValue(lldb::tid_t tid, unsigned offset, const char *reg_name, + const lldb_private::RegisterValue &value); + + /// Reads all general purpose registers into the specified buffer. + /// FIXME: The FreeBSD implementation of this function should use tid in order + /// to enable support for debugging threaded programs. + bool + ReadGPR(lldb::tid_t tid, void *buf, size_t buf_size); + + /// Reads all floating point registers into the specified buffer. + /// FIXME: The FreeBSD implementation of this function should use tid in order + /// to enable support for debugging threaded programs. + bool + ReadFPR(lldb::tid_t tid, void *buf, size_t buf_size); + + /// Reads the specified register set into the specified buffer. + /// + /// This method is provided for use by RegisterContextFreeBSD derivatives. + /// FIXME: The FreeBSD implementation of this function should use tid in order + /// to enable support for debugging threaded programs. + bool + ReadRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset); + + /// Writes all general purpose registers into the specified buffer. + /// FIXME: The FreeBSD implementation of this function should use tid in order + /// to enable support for debugging threaded programs. + bool + WriteGPR(lldb::tid_t tid, void *buf, size_t buf_size); + + /// Writes all floating point registers into the specified buffer. + /// FIXME: The FreeBSD implementation of this function should use tid in order + /// to enable support for debugging threaded programs. + bool + WriteFPR(lldb::tid_t tid, void *buf, size_t buf_size); + + /// Writes the specified register set into the specified buffer. + /// + /// This method is provided for use by RegisterContextFreeBSD derivatives. + /// FIXME: The FreeBSD implementation of this function should use tid in order + /// to enable support for debugging threaded programs. + bool + WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset); + + /// Writes a ptrace_lwpinfo structure corresponding to the given thread ID + /// to the memory region pointed to by @p lwpinfo. + bool + GetLwpInfo(lldb::tid_t tid, void *lwpinfo, int &error_no); + + /// Writes the raw event message code (vis-a-vis PTRACE_GETEVENTMSG) + /// corresponding to the given thread IDto the memory pointed to by @p + /// message. + bool + GetEventMessage(lldb::tid_t tid, unsigned long *message); + + /// Resumes the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + bool + Resume(lldb::tid_t tid, uint32_t signo); + + /// Single steps the given thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + bool + SingleStep(lldb::tid_t tid, uint32_t signo); + + /// Sends the inferior process a PTRACE_KILL signal. The inferior will + /// still exists and can be interrogated. Once resumed it will exit as + /// though it received a SIGKILL. + bool + BringProcessIntoLimbo(); + + lldb_private::Error + Detach(lldb::tid_t tid); + + void + StopMonitor(); + +private: + ProcessFreeBSD *m_process; + + lldb::thread_t m_operation_thread; + lldb::thread_t m_monitor_thread; + lldb::pid_t m_pid; + + + lldb_private::Mutex m_server_mutex; + int m_terminal_fd; + int m_client_fd; + int m_server_fd; + + struct OperationArgs + { + OperationArgs(ProcessMonitor *monitor); + + ~OperationArgs(); + + ProcessMonitor *m_monitor; // The monitor performing the attach. + sem_t m_semaphore; // Posted to once operation complete. + lldb_private::Error m_error; // Set if process operation failed. + }; + + /// @class LauchArgs + /// + /// @brief Simple structure to pass data to the thread responsible for + /// launching a child process. + struct LaunchArgs : OperationArgs + { + LaunchArgs(ProcessMonitor *monitor, + lldb_private::Module *module, + char const **argv, + char const **envp, + const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_dir); + + ~LaunchArgs(); + + lldb_private::Module *m_module; // The executable image to launch. + char const **m_argv; // Process arguments. + char const **m_envp; // Process environment. + const char *m_stdin_path; // Redirect stdin or NULL. + const char *m_stdout_path; // Redirect stdout or NULL. + const char *m_stderr_path; // Redirect stderr or NULL. + const char *m_working_dir; // Working directory or NULL. + }; + + void + StartLaunchOpThread(LaunchArgs *args, lldb_private::Error &error); + + static void * + LaunchOpThread(void *arg); + + static bool + Launch(LaunchArgs *args); + + bool + EnableIPC(); + + struct AttachArgs : OperationArgs + { + AttachArgs(ProcessMonitor *monitor, + lldb::pid_t pid); + + ~AttachArgs(); + + lldb::pid_t m_pid; // pid of the process to be attached. + }; + + void + StartAttachOpThread(AttachArgs *args, lldb_private::Error &error); + + static void * + AttachOpThread(void *args); + + static bool + Attach(AttachArgs *args); + + static void + ServeOperation(OperationArgs *args); + + static bool + DupDescriptor(const char *path, int fd, int flags); + + static bool + MonitorCallback(void *callback_baton, + lldb::pid_t pid, bool exited, int signal, int status); + + static ProcessMessage + MonitorSIGTRAP(ProcessMonitor *monitor, + const siginfo_t *info, lldb::pid_t pid); + + static ProcessMessage + MonitorSignal(ProcessMonitor *monitor, + const siginfo_t *info, lldb::pid_t pid); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGSEGV(const siginfo_t *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGILL(const siginfo_t *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGFPE(const siginfo_t *info); + + static ProcessMessage::CrashReason + GetCrashReasonForSIGBUS(const siginfo_t *info); + + void + DoOperation(Operation *op); + + /// Stops the child monitor thread. + void + StopMonitoringChildProcess(); + + /// Stops the operation thread used to attach/launch a process. + void + StopOpThread(); + + void + CloseFD(int &fd); +}; + +#endif // #ifndef liblldb_ProcessMonitor_H_ diff --git a/source/Plugins/Process/POSIX/POSIXStopInfo.cpp b/source/Plugins/Process/POSIX/POSIXStopInfo.cpp new file mode 100644 index 000000000000..6e2c140682ba --- /dev/null +++ b/source/Plugins/Process/POSIX/POSIXStopInfo.cpp @@ -0,0 +1,89 @@ +//===-- POSIXStopInfo.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "POSIXStopInfo.h" + +using namespace lldb; +using namespace lldb_private; + + +//===----------------------------------------------------------------------===// +// POSIXLimboStopInfo + +POSIXLimboStopInfo::~POSIXLimboStopInfo() { } + +lldb::StopReason +POSIXLimboStopInfo::GetStopReason() const +{ + return lldb::eStopReasonThreadExiting; +} + +const char * +POSIXLimboStopInfo::GetDescription() +{ + return "thread exiting"; +} + +bool +POSIXLimboStopInfo::ShouldStop(Event *event_ptr) +{ + return false; +} + +bool +POSIXLimboStopInfo::ShouldNotify(Event *event_ptr) +{ + return false; +} + +//===----------------------------------------------------------------------===// +// POSIXCrashStopInfo + +POSIXCrashStopInfo::~POSIXCrashStopInfo() { } + +lldb::StopReason +POSIXCrashStopInfo::GetStopReason() const +{ + return lldb::eStopReasonException; +} + +const char * +POSIXCrashStopInfo::GetDescription() +{ + return ProcessMessage::GetCrashReasonString(m_crash_reason, m_fault_addr); +} + +//===----------------------------------------------------------------------===// +// POSIXNewThreadStopInfo + +POSIXNewThreadStopInfo::~POSIXNewThreadStopInfo() { } + +lldb::StopReason +POSIXNewThreadStopInfo::GetStopReason() const +{ + return lldb::eStopReasonNone; +} + +const char * +POSIXNewThreadStopInfo::GetDescription() +{ + return "thread spawned"; +} + +bool +POSIXNewThreadStopInfo::ShouldStop(Event *event_ptr) +{ + return false; +} + +bool +POSIXNewThreadStopInfo::ShouldNotify(Event *event_ptr) +{ + return false; +} diff --git a/source/Plugins/Process/POSIX/POSIXStopInfo.h b/source/Plugins/Process/POSIX/POSIXStopInfo.h new file mode 100644 index 000000000000..cbf309e53506 --- /dev/null +++ b/source/Plugins/Process/POSIX/POSIXStopInfo.h @@ -0,0 +1,120 @@ +//===-- POSIXStopInfo.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_POSIXStopInfo_H_ +#define liblldb_POSIXStopInfo_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/StopInfo.h" + +#include "POSIXThread.h" +#include "ProcessMessage.h" + +//===----------------------------------------------------------------------===// +/// @class POSIXStopInfo +/// @brief Simple base class for all POSIX-specific StopInfo objects. +/// +class POSIXStopInfo + : public lldb_private::StopInfo +{ +public: + POSIXStopInfo(lldb_private::Thread &thread, uint32_t status) + : StopInfo(thread, status) + { } +}; + +//===----------------------------------------------------------------------===// +/// @class POSIXLimboStopInfo +/// @brief Represents the stop state of a process ready to exit. +/// +class POSIXLimboStopInfo + : public POSIXStopInfo +{ +public: + POSIXLimboStopInfo(POSIXThread &thread) + : POSIXStopInfo(thread, 0) + { } + + ~POSIXLimboStopInfo(); + + lldb::StopReason + GetStopReason() const; + + const char * + GetDescription(); + + bool + ShouldStop(lldb_private::Event *event_ptr); + + bool + ShouldNotify(lldb_private::Event *event_ptr); +}; + + +//===----------------------------------------------------------------------===// +/// @class POSIXCrashStopInfo +/// @brief Represents the stop state of process that is ready to crash. +/// +class POSIXCrashStopInfo + : public POSIXStopInfo +{ +public: + POSIXCrashStopInfo(POSIXThread &thread, uint32_t status, + ProcessMessage::CrashReason reason, + lldb::addr_t fault_addr) + : POSIXStopInfo(thread, status), + m_crash_reason(reason), + m_fault_addr(fault_addr) + { } + + ~POSIXCrashStopInfo(); + + lldb::StopReason + GetStopReason() const; + + const char * + GetDescription(); + +private: + ProcessMessage::CrashReason m_crash_reason; + lldb::addr_t m_fault_addr; +}; + +//===----------------------------------------------------------------------===// +/// @class POSIXNewThreadStopInfo +/// @brief Represents the stop state of process when a new thread is spawned. +/// + +class POSIXNewThreadStopInfo + : public POSIXStopInfo +{ +public: + POSIXNewThreadStopInfo (POSIXThread &thread) + : POSIXStopInfo (thread, 0) + { } + + ~POSIXNewThreadStopInfo(); + + lldb::StopReason + GetStopReason() const; + + const char * + GetDescription(); + + bool + ShouldStop(lldb_private::Event *event_ptr); + + bool + ShouldNotify(lldb_private::Event *event_ptr); +}; + +#endif diff --git a/source/Plugins/Process/POSIX/POSIXThread.cpp b/source/Plugins/Process/POSIX/POSIXThread.cpp new file mode 100644 index 000000000000..93c296679df2 --- /dev/null +++ b/source/Plugins/Process/POSIX/POSIXThread.cpp @@ -0,0 +1,578 @@ +//===-- POSIXThread.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include <errno.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "POSIXStopInfo.h" +#include "POSIXThread.h" +#include "ProcessPOSIX.h" +#include "ProcessPOSIXLog.h" +#include "ProcessMonitor.h" +#include "RegisterContext_i386.h" +#include "RegisterContext_x86_64.h" +#include "RegisterContextPOSIX.h" +#include "RegisterContextLinux_x86_64.h" +#include "RegisterContextFreeBSD_x86_64.h" + +#include "UnwindLLDB.h" + +using namespace lldb; +using namespace lldb_private; + + +POSIXThread::POSIXThread(Process &process, lldb::tid_t tid) + : Thread(process, tid), + m_frame_ap (), + m_breakpoint (), + m_thread_name_valid (false), + m_thread_name () +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("POSIXThread::%s (tid = %" PRIi64 ")", __FUNCTION__, tid); + + // Set the current watchpoints for this thread. + Target &target = GetProcess()->GetTarget(); + const WatchpointList &wp_list = target.GetWatchpointList(); + size_t wp_size = wp_list.GetSize(); + + for (uint32_t wp_idx = 0; wp_idx < wp_size; wp_idx++) + { + lldb::WatchpointSP wp = wp_list.GetByIndex(wp_idx); + if (wp.get() && wp->IsEnabled()) + { + assert(EnableHardwareWatchpoint(wp.get())); + } + } +} + +POSIXThread::~POSIXThread() +{ + DestroyThread(); +} + +ProcessMonitor & +POSIXThread::GetMonitor() +{ + ProcessSP base = GetProcess(); + ProcessPOSIX &process = static_cast<ProcessPOSIX&>(*base); + return process.GetMonitor(); +} + +void +POSIXThread::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The KDPRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + //if (StateIsStoppedState(GetState()) + { + const bool force = false; + GetRegisterContext()->InvalidateIfNeeded (force); + } + // FIXME: This should probably happen somewhere else. + SetResumeState(eStateRunning); + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to running", __FUNCTION__, GetID()); +} + +const char * +POSIXThread::GetInfo() +{ + return NULL; +} + +void +POSIXThread::SetName (const char *name) +{ + m_thread_name_valid = (name && name[0]); + if (m_thread_name_valid) + m_thread_name.assign (name); + else + m_thread_name.clear(); +} + +const char * +POSIXThread::GetName () +{ + if (!m_thread_name_valid) + { + SetName(Host::GetThreadName(GetProcess()->GetID(), GetID()).c_str()); + m_thread_name_valid = true; + } + + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + +lldb::RegisterContextSP +POSIXThread::GetRegisterContext() +{ + if (!m_reg_context_sp) + { + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetCore()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + m_reg_context_sp.reset(new RegisterContext_i386(*this, 0)); + break; + + case ArchSpec::eCore_x86_64_x86_64: + switch (arch.GetTriple().getOS()) + { + case llvm::Triple::FreeBSD: + m_reg_context_sp.reset(new RegisterContextFreeBSD_x86_64(*this, 0)); + break; + case llvm::Triple::Linux: + m_reg_context_sp.reset(new RegisterContextLinux_x86_64(*this, 0)); + break; + default: + assert(false && "OS not supported"); + break; + } + break; + } + } + return m_reg_context_sp; +} + +lldb::RegisterContextSP +POSIXThread::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("POSIXThread::%s ()", __FUNCTION__); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) + reg_ctx_sp = GetRegisterContext(); + else + { + assert(GetUnwinder()); + reg_ctx_sp = GetUnwinder()->CreateRegisterContextForFrame(frame); + } + + return reg_ctx_sp; +} + +bool +POSIXThread::CalculateStopInfo() +{ + SetStopInfo (m_stop_info_sp); + return true; +} + +Unwind * +POSIXThread::GetUnwinder() +{ + if (m_unwinder_ap.get() == NULL) + m_unwinder_ap.reset(new UnwindLLDB(*this)); + + return m_unwinder_ap.get(); +} + +void +POSIXThread::WillResume(lldb::StateType resume_state) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to %s", __FUNCTION__, GetID(), StateAsCString(resume_state)); + // TODO: the line below shouldn't really be done, but + // the POSIXThread might rely on this so I will leave this in for now + SetResumeState(resume_state); +} + +void +POSIXThread::DidStop() +{ + // Don't set the thread state to stopped unless we really stopped. +} + +bool +POSIXThread::Resume() +{ + lldb::StateType resume_state = GetResumeState(); + ProcessMonitor &monitor = GetMonitor(); + bool status; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf ("POSIXThread::%s (), resume_state = %s", __FUNCTION__, + StateAsCString(resume_state)); + + switch (resume_state) + { + default: + assert(false && "Unexpected state for resume!"); + status = false; + break; + + case lldb::eStateRunning: + SetState(resume_state); + status = monitor.Resume(GetID(), GetResumeSignal()); + break; + + case lldb::eStateStepping: + SetState(resume_state); + status = monitor.SingleStep(GetID(), GetResumeSignal()); + break; + case lldb::eStateStopped: + case lldb::eStateSuspended: + status = true; + break; + } + + return status; +} + +void +POSIXThread::Notify(const ProcessMessage &message) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf ("POSIXThread::%s () message kind = '%s' for tid %" PRIu64, + __FUNCTION__, message.PrintKind(), GetID()); + + switch (message.GetKind()) + { + default: + assert(false && "Unexpected message kind!"); + break; + + case ProcessMessage::eExitMessage: + // Nothing to be done. + break; + + case ProcessMessage::eLimboMessage: + LimboNotify(message); + break; + + case ProcessMessage::eSignalMessage: + SignalNotify(message); + break; + + case ProcessMessage::eSignalDeliveredMessage: + SignalDeliveredNotify(message); + break; + + case ProcessMessage::eTraceMessage: + TraceNotify(message); + break; + + case ProcessMessage::eBreakpointMessage: + BreakNotify(message); + break; + + case ProcessMessage::eWatchpointMessage: + WatchNotify(message); + break; + + case ProcessMessage::eCrashMessage: + CrashNotify(message); + break; + + case ProcessMessage::eNewThreadMessage: + ThreadNotify(message); + break; + } +} + +bool +POSIXThread::EnableHardwareWatchpoint(Watchpoint *wp) +{ + bool wp_set = false; + if (wp) + { + addr_t wp_addr = wp->GetLoadAddress(); + size_t wp_size = wp->GetByteSize(); + bool wp_read = wp->WatchpointRead(); + bool wp_write = wp->WatchpointWrite(); + uint32_t wp_hw_index = wp->GetHardwareIndex(); + RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX(); + if (reg_ctx) + wp_set = reg_ctx->SetHardwareWatchpointWithIndex(wp_addr, wp_size, + wp_read, wp_write, + wp_hw_index); + } + return wp_set; +} + +bool +POSIXThread::DisableHardwareWatchpoint(Watchpoint *wp) +{ + bool result = false; + if (wp) + { + lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext(); + if (reg_ctx_sp.get()) + result = reg_ctx_sp->ClearHardwareWatchpoint(wp->GetHardwareIndex()); + } + return result; +} + +uint32_t +POSIXThread::NumSupportedHardwareWatchpoints() +{ + lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext(); + if (reg_ctx_sp.get()) + return reg_ctx_sp->NumSupportedHardwareWatchpoints(); + return 0; +} + +uint32_t +POSIXThread::FindVacantWatchpointIndex() +{ + uint32_t hw_index = LLDB_INVALID_INDEX32; + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + uint32_t wp_idx; + RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX(); + if (reg_ctx) + { + for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) + { + if (reg_ctx->IsWatchpointVacant(wp_idx)) + { + hw_index = wp_idx; + break; + } + } + } + return hw_index; +} + +void +POSIXThread::BreakNotify(const ProcessMessage &message) +{ + bool status; + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + + assert(GetRegisterContext()); + status = GetRegisterContextPOSIX()->UpdateAfterBreakpoint(); + assert(status && "Breakpoint update failed!"); + + // With our register state restored, resolve the breakpoint object + // corresponding to our current PC. + assert(GetRegisterContext()); + lldb::addr_t pc = GetRegisterContext()->GetPC(); + if (log) + log->Printf ("POSIXThread::%s () PC=0x%8.8" PRIx64, __FUNCTION__, pc); + lldb::BreakpointSiteSP bp_site(GetProcess()->GetBreakpointSiteList().FindByAddress(pc)); + + // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, + // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that + // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. + if (bp_site && bp_site->ValidForThisThread(this)) + { + lldb::break_id_t bp_id = bp_site->GetID(); + if (GetProcess()->GetThreadList().SetSelectedThreadByID(GetID())) + SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id)); + else + assert(false && "Invalid thread ID during BreakNotify."); + } + else + { + const ThreadSpec *spec = bp_site ? + bp_site->GetOwnerAtIndex(0)->GetOptionsNoCreate()->GetThreadSpecNoCreate() : 0; + + if (spec && spec->TIDMatches(*this)) + assert(false && "BreakpointSite is invalid for the current ThreadSpec."); + else + { + if (!m_stop_info_sp) { + StopInfoSP invalid_stop_info_sp; + SetStopInfo (invalid_stop_info_sp); + } + } + } +} + +void +POSIXThread::WatchNotify(const ProcessMessage &message) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + + lldb::addr_t halt_addr = message.GetHWAddress(); + if (log) + log->Printf ("POSIXThread::%s () Hardware Watchpoint Address = 0x%8.8" + PRIx64, __FUNCTION__, halt_addr); + + RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX(); + if (reg_ctx) + { + uint32_t num_hw_wps = reg_ctx->NumSupportedHardwareWatchpoints(); + uint32_t wp_idx; + for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) + { + if (reg_ctx->IsWatchpointHit(wp_idx)) + { + // Clear the watchpoint hit here + reg_ctx->ClearWatchpointHits(); + break; + } + } + + if (wp_idx == num_hw_wps) + return; + + Target &target = GetProcess()->GetTarget(); + lldb::addr_t wp_monitor_addr = reg_ctx->GetWatchpointAddress(wp_idx); + const WatchpointList &wp_list = target.GetWatchpointList(); + lldb::WatchpointSP wp_sp = wp_list.FindByAddress(wp_monitor_addr); + + assert(wp_sp.get() && "No watchpoint found"); + SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID(*this, + wp_sp->GetID())); + } +} + +void +POSIXThread::TraceNotify(const ProcessMessage &message) +{ + SetStopInfo (StopInfo::CreateStopReasonToTrace(*this)); +} + +void +POSIXThread::LimboNotify(const ProcessMessage &message) +{ + SetStopInfo (lldb::StopInfoSP(new POSIXLimboStopInfo(*this))); +} + +void +POSIXThread::SignalNotify(const ProcessMessage &message) +{ + int signo = message.GetSignal(); + + SetStopInfo (StopInfo::CreateStopReasonWithSignal(*this, signo)); + SetResumeSignal(signo); +} + +void +POSIXThread::SignalDeliveredNotify(const ProcessMessage &message) +{ + int signo = message.GetSignal(); + + SetStopInfo (StopInfo::CreateStopReasonWithSignal(*this, signo)); + SetResumeSignal(signo); +} + +void +POSIXThread::CrashNotify(const ProcessMessage &message) +{ + // FIXME: Update stop reason as per bugzilla 14598 + int signo = message.GetSignal(); + + assert(message.GetKind() == ProcessMessage::eCrashMessage); + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf ("POSIXThread::%s () signo = %i, reason = '%s'", + __FUNCTION__, signo, message.PrintCrashReason()); + + SetStopInfo (lldb::StopInfoSP(new POSIXCrashStopInfo(*this, signo, + message.GetCrashReason(), + message.GetFaultAddress()))); + SetResumeSignal(signo); +} + +void +POSIXThread::ThreadNotify(const ProcessMessage &message) +{ + SetStopInfo (lldb::StopInfoSP(new POSIXNewThreadStopInfo(*this))); +} + +unsigned +POSIXThread::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg = LLDB_INVALID_REGNUM; + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetCore()) + { + default: + llvm_unreachable("CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + case ArchSpec::eCore_x86_64_x86_64: + { + RegisterContextSP base = GetRegisterContext(); + if (base) { + RegisterContextPOSIX &context = static_cast<RegisterContextPOSIX &>(*base); + reg = context.GetRegisterIndexFromOffset(offset); + } + } + break; + } + return reg; +} + +const char * +POSIXThread::GetRegisterName(unsigned reg) +{ + const char * name = nullptr; + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetCore()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + case ArchSpec::eCore_x86_64_x86_64: + name = GetRegisterContext()->GetRegisterName(reg); + break; + } + return name; +} + +const char * +POSIXThread::GetRegisterNameFromOffset(unsigned offset) +{ + return GetRegisterName(GetRegisterIndexFromOffset(offset)); +} + diff --git a/source/Plugins/Process/POSIX/POSIXThread.h b/source/Plugins/Process/POSIX/POSIXThread.h new file mode 100644 index 000000000000..d051d23860df --- /dev/null +++ b/source/Plugins/Process/POSIX/POSIXThread.h @@ -0,0 +1,133 @@ +//===-- POSIXThread.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_POSIXThread_H_ +#define liblldb_POSIXThread_H_ + +// C Includes +// C++ Includes +#include <memory> +#include <string> + +// Other libraries and framework includes +#include "lldb/Target/Thread.h" +#include "RegisterContextPOSIX.h" + +class ProcessMessage; +class ProcessMonitor; +class RegisterContextPOSIX; + +//------------------------------------------------------------------------------ +// @class POSIXThread +// @brief Abstraction of a POSIX thread. +class POSIXThread + : public lldb_private::Thread +{ +public: + POSIXThread(lldb_private::Process &process, lldb::tid_t tid); + + virtual ~POSIXThread(); + + void + RefreshStateAfterStop(); + + virtual void + WillResume(lldb::StateType resume_state); + + // This notifies the thread when a private stop occurs. + virtual void + DidStop (); + + const char * + GetInfo(); + + void + SetName (const char *name); + + const char * + GetName (); + + virtual lldb::RegisterContextSP + GetRegisterContext(); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + //-------------------------------------------------------------------------- + // These functions provide a mapping from the register offset + // back to the register index or name for use in debugging or log + // output. + + unsigned + GetRegisterIndexFromOffset(unsigned offset); + + const char * + GetRegisterName(unsigned reg); + + const char * + GetRegisterNameFromOffset(unsigned offset); + + //-------------------------------------------------------------------------- + // These methods form a specialized interface to POSIX threads. + // + bool Resume(); + + void Notify(const ProcessMessage &message); + + //-------------------------------------------------------------------------- + // These methods provide an interface to watchpoints + // + bool EnableHardwareWatchpoint(lldb_private::Watchpoint *wp); + + bool DisableHardwareWatchpoint(lldb_private::Watchpoint *wp); + + uint32_t NumSupportedHardwareWatchpoints(); + + uint32_t FindVacantWatchpointIndex(); + +protected: + RegisterContextPOSIX * + GetRegisterContextPOSIX () + { + if (!m_reg_context_sp) + m_reg_context_sp = GetRegisterContext(); +#if 0 + return dynamic_cast<RegisterContextPOSIX*>(m_reg_context_sp.get()); +#endif + return (RegisterContextPOSIX *)m_reg_context_sp.get(); + } + + std::unique_ptr<lldb_private::StackFrame> m_frame_ap; + + lldb::BreakpointSiteSP m_breakpoint; + + bool m_thread_name_valid; + std::string m_thread_name; + + ProcessMonitor & + GetMonitor(); + + virtual bool + CalculateStopInfo(); + + void BreakNotify(const ProcessMessage &message); + void WatchNotify(const ProcessMessage &message); + virtual void TraceNotify(const ProcessMessage &message); + void LimboNotify(const ProcessMessage &message); + void SignalNotify(const ProcessMessage &message); + void SignalDeliveredNotify(const ProcessMessage &message); + void CrashNotify(const ProcessMessage &message); + void ThreadNotify(const ProcessMessage &message); + void ExitNotify(const ProcessMessage &message); + + lldb_private::Unwind * + GetUnwinder(); +}; + +#endif // #ifndef liblldb_POSIXThread_H_ diff --git a/source/Plugins/Process/POSIX/ProcessMessage.cpp b/source/Plugins/Process/POSIX/ProcessMessage.cpp new file mode 100644 index 000000000000..60a29e07cea8 --- /dev/null +++ b/source/Plugins/Process/POSIX/ProcessMessage.cpp @@ -0,0 +1,258 @@ +//===-- ProcessMessage.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessMessage.h" + +#include <sstream> + +using namespace lldb_private; + +namespace { + +inline void AppendFaultAddr(std::string& str, lldb::addr_t addr) +{ + std::stringstream ss; + ss << " (fault address: 0x" << std::hex << addr << ")"; + str += ss.str(); +} + +} + +const char * +ProcessMessage::GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr) +{ + static std::string str; + + switch (reason) + { + default: + assert(false && "invalid CrashReason"); + break; + + case eInvalidAddress: + str = "invalid address"; + AppendFaultAddr(str, fault_addr); + break; + case ePrivilegedAddress: + str = "address access protected"; + AppendFaultAddr(str, fault_addr); + break; + case eIllegalOpcode: + str = "illegal instruction"; + break; + case eIllegalOperand: + str = "illegal instruction operand"; + break; + case eIllegalAddressingMode: + str = "illegal addressing mode"; + break; + case eIllegalTrap: + str = "illegal trap"; + break; + case ePrivilegedOpcode: + str = "privileged instruction"; + break; + case ePrivilegedRegister: + str = "privileged register"; + break; + case eCoprocessorError: + str = "coprocessor error"; + break; + case eInternalStackError: + str = "internal stack error"; + break; + case eIllegalAlignment: + str = "illegal alignment"; + break; + case eIllegalAddress: + str = "illegal address"; + break; + case eHardwareError: + str = "hardware error"; + break; + case eIntegerDivideByZero: + str = "integer divide by zero"; + break; + case eIntegerOverflow: + str = "integer overflow"; + break; + case eFloatDivideByZero: + str = "floating point divide by zero"; + break; + case eFloatOverflow: + str = "floating point overflow"; + break; + case eFloatUnderflow: + str = "floating point underflow"; + break; + case eFloatInexactResult: + str = "inexact floating point result"; + break; + case eFloatInvalidOperation: + str = "invalid floating point operation"; + break; + case eFloatSubscriptRange: + str = "invalid floating point subscript range"; + break; + } + + return str.c_str(); +} + +const char * +ProcessMessage::PrintCrashReason(CrashReason reason) +{ +#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION + // Just return the code in asci for integration builds. + chcar str[8]; + sprintf(str, "%d", reason); +#else + const char *str = NULL; + + switch (reason) + { + case eInvalidCrashReason: + str = "eInvalidCrashReason"; + break; + + // SIGSEGV crash reasons. + case eInvalidAddress: + str = "eInvalidAddress"; + break; + case ePrivilegedAddress: + str = "ePrivilegedAddress"; + break; + + // SIGILL crash reasons. + case eIllegalOpcode: + str = "eIllegalOpcode"; + break; + case eIllegalOperand: + str = "eIllegalOperand"; + break; + case eIllegalAddressingMode: + str = "eIllegalAddressingMode"; + break; + case eIllegalTrap: + str = "eIllegalTrap"; + break; + case ePrivilegedOpcode: + str = "ePrivilegedOpcode"; + break; + case ePrivilegedRegister: + str = "ePrivilegedRegister"; + break; + case eCoprocessorError: + str = "eCoprocessorError"; + break; + case eInternalStackError: + str = "eInternalStackError"; + break; + + // SIGBUS crash reasons: + case eIllegalAlignment: + str = "eIllegalAlignment"; + break; + case eIllegalAddress: + str = "eIllegalAddress"; + break; + case eHardwareError: + str = "eHardwareError"; + break; + + // SIGFPE crash reasons: + case eIntegerDivideByZero: + str = "eIntegerDivideByZero"; + break; + case eIntegerOverflow: + str = "eIntegerOverflow"; + break; + case eFloatDivideByZero: + str = "eFloatDivideByZero"; + break; + case eFloatOverflow: + str = "eFloatOverflow"; + break; + case eFloatUnderflow: + str = "eFloatUnderflow"; + break; + case eFloatInexactResult: + str = "eFloatInexactResult"; + break; + case eFloatInvalidOperation: + str = "eFloatInvalidOperation"; + break; + case eFloatSubscriptRange: + str = "eFloatSubscriptRange"; + break; + } +#endif + + return str; +} + +const char * +ProcessMessage::PrintCrashReason() const +{ + return PrintCrashReason(m_crash_reason); +} + +const char * +ProcessMessage::PrintKind(Kind kind) +{ +#ifdef LLDB_CONFIGURATION_BUILDANDINTEGRATION + // Just return the code in asci for integration builds. + chcar str[8]; + sprintf(str, "%d", reason); +#else + const char *str = NULL; + + switch (kind) + { + case eInvalidMessage: + str = "eInvalidMessage"; + break; + case eExitMessage: + str = "eExitMessage"; + break; + case eLimboMessage: + str = "eLimboMessage"; + break; + case eSignalMessage: + str = "eSignalMessage"; + break; + case eSignalDeliveredMessage: + str = "eSignalDeliveredMessage"; + break; + case eTraceMessage: + str = "eTraceMessage"; + break; + case eBreakpointMessage: + str = "eBreakpointMessage"; + break; + case eWatchpointMessage: + str = "eWatchpointMessage"; + break; + case eCrashMessage: + str = "eCrashMessage"; + break; + case eNewThreadMessage: + str = "eNewThreadMessage"; + break; + } +#endif + + return str; +} + +const char * +ProcessMessage::PrintKind() const +{ + return PrintKind(m_kind); +} diff --git a/source/Plugins/Process/POSIX/ProcessMessage.h b/source/Plugins/Process/POSIX/ProcessMessage.h new file mode 100644 index 000000000000..c6c460c13445 --- /dev/null +++ b/source/Plugins/Process/POSIX/ProcessMessage.h @@ -0,0 +1,207 @@ +//===-- ProcessMessage.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessMessage_H_ +#define liblldb_ProcessMessage_H_ + +#include <cassert> + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +class ProcessMessage +{ +public: + + /// The type of signal this message can correspond to. + enum Kind + { + eInvalidMessage, + eExitMessage, + eLimboMessage, + eSignalMessage, + eSignalDeliveredMessage, + eTraceMessage, + eBreakpointMessage, + eWatchpointMessage, + eCrashMessage, + eNewThreadMessage + }; + + enum CrashReason + { + eInvalidCrashReason, + + // SIGSEGV crash reasons. + eInvalidAddress, + ePrivilegedAddress, + + // SIGILL crash reasons. + eIllegalOpcode, + eIllegalOperand, + eIllegalAddressingMode, + eIllegalTrap, + ePrivilegedOpcode, + ePrivilegedRegister, + eCoprocessorError, + eInternalStackError, + + // SIGBUS crash reasons, + eIllegalAlignment, + eIllegalAddress, + eHardwareError, + + // SIGFPE crash reasons, + eIntegerDivideByZero, + eIntegerOverflow, + eFloatDivideByZero, + eFloatOverflow, + eFloatUnderflow, + eFloatInexactResult, + eFloatInvalidOperation, + eFloatSubscriptRange + }; + + ProcessMessage() + : m_tid(LLDB_INVALID_PROCESS_ID), + m_kind(eInvalidMessage), + m_crash_reason(eInvalidCrashReason), + m_status(0), + m_addr(0) { } + + Kind GetKind() const { return m_kind; } + + lldb::tid_t GetTID() const { return m_tid; } + + /// Indicates that the thread @p tid is about to exit with status @p status. + static ProcessMessage Limbo(lldb::tid_t tid, int status) { + return ProcessMessage(tid, eLimboMessage, status); + } + + /// Indicates that the thread @p tid had the signal @p signum delivered. + static ProcessMessage Signal(lldb::tid_t tid, int signum) { + return ProcessMessage(tid, eSignalMessage, signum); + } + + /// Indicates that a signal @p signum generated by the debugging process was + /// delivered to the thread @p tid. + static ProcessMessage SignalDelivered(lldb::tid_t tid, int signum) { + return ProcessMessage(tid, eSignalDeliveredMessage, signum); + } + + /// Indicates that the thread @p tid encountered a trace point. + static ProcessMessage Trace(lldb::tid_t tid) { + return ProcessMessage(tid, eTraceMessage); + } + + /// Indicates that the thread @p tid encountered a break point. + static ProcessMessage Break(lldb::tid_t tid) { + return ProcessMessage(tid, eBreakpointMessage); + } + + static ProcessMessage Watch(lldb::tid_t tid, lldb::addr_t wp_addr) { + return ProcessMessage(tid, eWatchpointMessage, 0, wp_addr); + } + + /// Indicates that the thread @p tid crashed. + static ProcessMessage Crash(lldb::pid_t pid, CrashReason reason, + int signo, lldb::addr_t fault_addr) { + ProcessMessage message(pid, eCrashMessage, signo, fault_addr); + message.m_crash_reason = reason; + return message; + } + + /// Indicates that the thread @p child_tid was spawned. + static ProcessMessage NewThread(lldb::tid_t parent_tid, lldb::tid_t child_tid) { + return ProcessMessage(parent_tid, eNewThreadMessage, child_tid); + } + + /// Indicates that the thread @p tid is about to exit with status @p status. + static ProcessMessage Exit(lldb::tid_t tid, int status) { + return ProcessMessage(tid, eExitMessage, status); + } + + int GetExitStatus() const { + assert(GetKind() == eExitMessage || GetKind() == eLimboMessage); + return m_status; + } + + int GetSignal() const { + assert(GetKind() == eSignalMessage || GetKind() == eCrashMessage || + GetKind() == eSignalDeliveredMessage); + return m_status; + } + + int GetStopStatus() const { + assert(GetKind() == eSignalMessage); + return m_status; + } + + CrashReason GetCrashReason() const { + assert(GetKind() == eCrashMessage); + return m_crash_reason; + } + + lldb::addr_t GetFaultAddress() const { + assert(GetKind() == eCrashMessage); + return m_addr; + } + + lldb::addr_t GetHWAddress() const { + assert(GetKind() == eWatchpointMessage || GetKind() == eTraceMessage); + return m_addr; + } + + lldb::tid_t GetChildTID() const { + assert(GetKind() == eNewThreadMessage); + return m_child_tid; + } + + static const char * + GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr); + + const char * + PrintCrashReason() const; + + static const char * + PrintCrashReason(CrashReason reason); + + const char * + PrintKind() const; + + static const char * + PrintKind(Kind); + +private: + ProcessMessage(lldb::tid_t tid, Kind kind, + int status = 0, lldb::addr_t addr = 0) + : m_tid(tid), + m_kind(kind), + m_crash_reason(eInvalidCrashReason), + m_status(status), + m_addr(addr), + m_child_tid(0) { } + + ProcessMessage(lldb::tid_t tid, Kind kind, lldb::tid_t child_tid) + : m_tid(tid), + m_kind(kind), + m_crash_reason(eInvalidCrashReason), + m_status(0), + m_addr(0), + m_child_tid(child_tid) { } + + lldb::tid_t m_tid; + Kind m_kind : 8; + CrashReason m_crash_reason : 8; + int m_status; + lldb::addr_t m_addr; + lldb::tid_t m_child_tid; +}; + +#endif // #ifndef liblldb_ProcessMessage_H_ diff --git a/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/source/Plugins/Process/POSIX/ProcessPOSIX.cpp new file mode 100644 index 000000000000..f04631ddf914 --- /dev/null +++ b/source/Plugins/Process/POSIX/ProcessPOSIX.cpp @@ -0,0 +1,911 @@ +//===-- ProcessPOSIX.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include <errno.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Target.h" + +#include "ProcessPOSIX.h" +#include "ProcessPOSIXLog.h" +#include "Plugins/Process/Utility/InferiorCallPOSIX.h" +#include "ProcessMonitor.h" +#include "POSIXThread.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------------ +// Static functions. +#if 0 +Process* +ProcessPOSIX::CreateInstance(Target& target, Listener &listener) +{ + return new ProcessPOSIX(target, listener); +} + + +void +ProcessPOSIX::Initialize() +{ + static bool g_initialized = false; + + if (!g_initialized) + { + g_initialized = true; + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + + Log::Callbacks log_callbacks = { + ProcessPOSIXLog::DisableLog, + ProcessPOSIXLog::EnableLog, + ProcessPOSIXLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessPOSIX::GetPluginNameStatic(), log_callbacks); + } +} +#endif + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessPOSIX::ProcessPOSIX(Target& target, Listener &listener) + : Process(target, listener), + m_byte_order(lldb::endian::InlHostByteOrder()), + m_monitor(NULL), + m_module(NULL), + m_message_mutex (Mutex::eMutexTypeRecursive), + m_exit_now(false), + m_seen_initial_stop() +{ + // FIXME: Putting this code in the ctor and saving the byte order in a + // member variable is a hack to avoid const qual issues in GetByteOrder. + lldb::ModuleSP module = GetTarget().GetExecutableModule(); + if (module && module->GetObjectFile()) + m_byte_order = module->GetObjectFile()->GetByteOrder(); +} + +ProcessPOSIX::~ProcessPOSIX() +{ + delete m_monitor; +} + +//------------------------------------------------------------------------------ +// Process protocol. +void +ProcessPOSIX::Finalize() +{ + Process::Finalize(); + + if (m_monitor) + m_monitor->StopMonitor(); +} + +bool +ProcessPOSIX::CanDebug(Target &target, bool plugin_specified_by_name) +{ + // For now we are just making sure the file exists for a given module + ModuleSP exe_module_sp(target.GetExecutableModule()); + if (exe_module_sp.get()) + return exe_module_sp->GetFileSpec().Exists(); + // If there is no executable module, we return true since we might be preparing to attach. + return true; +} + +Error +ProcessPOSIX::DoAttachToProcessWithID(lldb::pid_t pid) +{ + Error error; + assert(m_monitor == NULL); + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessPOSIX::%s(pid = %" PRIi64 ")", __FUNCTION__, GetID()); + + m_monitor = new ProcessMonitor(this, pid, error); + + if (!error.Success()) + return error; + + PlatformSP platform_sp (m_target.GetPlatform ()); + assert (platform_sp.get()); + if (!platform_sp) + return error; // FIXME: Detatch? + + // Find out what we can about this process + ProcessInstanceInfo process_info; + platform_sp->GetProcessInfo (pid, process_info); + + // Resolve the executable module + ModuleSP exe_module_sp; + FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); + error = platform_sp->ResolveExecutable(process_info.GetExecutableFile(), + m_target.GetArchitecture(), + exe_module_sp, + executable_search_paths.GetSize() ? &executable_search_paths : NULL); + if (!error.Success()) + return error; + + // Fix the target architecture if necessary + const ArchSpec &module_arch = exe_module_sp->GetArchitecture(); + if (module_arch.IsValid() && !m_target.GetArchitecture().IsExactMatch(module_arch)) + m_target.SetArchitecture(module_arch); + + // Initialize the target module list + m_target.SetExecutableModule (exe_module_sp, true); + + SetSTDIOFileDescriptor(m_monitor->GetTerminalFD()); + + SetID(pid); + + return error; +} + +Error +ProcessPOSIX::DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info) +{ + return DoAttachToProcessWithID(pid); +} + +Error +ProcessPOSIX::WillLaunch(Module* module) +{ + Error error; + return error; +} + +const char * +ProcessPOSIX::GetFilePath( + const lldb_private::ProcessLaunchInfo::FileAction *file_action, + const char *default_path) +{ + const char *pts_name = "/dev/pts/"; + const char *path = NULL; + + if (file_action) + { + if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) + path = file_action->GetPath(); + // By default the stdio paths passed in will be pseudo-terminal + // (/dev/pts). If so, convert to using a different default path + // instead to redirect I/O to the debugger console. This should + // also handle user overrides to /dev/null or a different file. + if (::strncmp(path, pts_name, ::strlen(pts_name)) == 0) + path = default_path; + } + + return path; +} + +Error +ProcessPOSIX::DoLaunch (Module *module, + const ProcessLaunchInfo &launch_info) +{ + Error error; + assert(m_monitor == NULL); + + const char* working_dir = launch_info.GetWorkingDirectory(); + if (working_dir) { + FileSpec WorkingDir(working_dir, true); + if (!WorkingDir || WorkingDir.GetFileType() != FileSpec::eFileTypeDirectory) + { + error.SetErrorStringWithFormat("No such file or directory: %s", working_dir); + return error; + } + } + + SetPrivateState(eStateLaunching); + + const lldb_private::ProcessLaunchInfo::FileAction *file_action; + + // Default of NULL will mean to use existing open file descriptors + const char *stdin_path = NULL; + const char *stdout_path = NULL; + const char *stderr_path = NULL; + + file_action = launch_info.GetFileActionForFD (STDIN_FILENO); + stdin_path = GetFilePath(file_action, stdin_path); + + file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); + stdout_path = GetFilePath(file_action, stdout_path); + + file_action = launch_info.GetFileActionForFD (STDERR_FILENO); + stderr_path = GetFilePath(file_action, stderr_path); + + m_monitor = new ProcessMonitor (this, + module, + launch_info.GetArguments().GetConstArgumentVector(), + launch_info.GetEnvironmentEntries().GetConstArgumentVector(), + stdin_path, + stdout_path, + stderr_path, + working_dir, + error); + + m_module = module; + + if (!error.Success()) + return error; + + SetSTDIOFileDescriptor(m_monitor->GetTerminalFD()); + + SetID(m_monitor->GetPID()); + return error; +} + +void +ProcessPOSIX::DidLaunch() +{ +} + +Error +ProcessPOSIX::DoResume() +{ + StateType state = GetPrivateState(); + + assert(state == eStateStopped); + + SetPrivateState(eStateRunning); + + bool did_resume = false; + + Mutex::Locker lock(m_thread_list.GetMutex()); + + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + did_resume = thread->Resume() || did_resume; + } + assert(did_resume && "Process resume failed!"); + + return Error(); +} + +addr_t +ProcessPOSIX::GetImageInfoAddress() +{ + Target *target = &GetTarget(); + ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(); + + if (addr.IsValid()) + return addr.GetLoadAddress(target); + else + return LLDB_INVALID_ADDRESS; +} + +Error +ProcessPOSIX::DoHalt(bool &caused_stop) +{ + Error error; + + if (IsStopped()) + { + caused_stop = false; + } + else if (kill(GetID(), SIGSTOP)) + { + caused_stop = false; + error.SetErrorToErrno(); + } + else + { + caused_stop = true; + } + return error; +} + +Error +ProcessPOSIX::DoDetach(bool keep_stopped) +{ + Error error; + if (keep_stopped) + { + // FIXME: If you want to implement keep_stopped, + // this would be the place to do it. + error.SetErrorString("Detaching with keep_stopped true is not currently supported on this platform."); + return error; + } + + Mutex::Locker lock(m_thread_list.GetMutex()); + + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + error = m_monitor->Detach(thread->GetID()); + } + + if (error.Success()) + SetPrivateState(eStateDetached); + + return error; +} + +Error +ProcessPOSIX::DoSignal(int signal) +{ + Error error; + + if (kill(GetID(), signal)) + error.SetErrorToErrno(); + + return error; +} + +Error +ProcessPOSIX::DoDestroy() +{ + Error error; + + if (!HasExited()) + { + // Drive the exit event to completion (do not keep the inferior in + // limbo). + m_exit_now = true; + + if ((m_monitor == NULL || kill(m_monitor->GetPID(), SIGKILL)) && error.Success()) + { + error.SetErrorToErrno(); + return error; + } + + SetPrivateState(eStateExited); + } + + return error; +} + +void +ProcessPOSIX::SendMessage(const ProcessMessage &message) +{ + Mutex::Locker lock(m_message_mutex); + + Mutex::Locker thread_lock(m_thread_list.GetMutex()); + + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.FindThreadByID(message.GetTID(), false).get()); + + switch (message.GetKind()) + { + case ProcessMessage::eInvalidMessage: + return; + + case ProcessMessage::eLimboMessage: + assert(thread); + thread->SetState(eStateStopped); + if (message.GetTID() == GetID()) + { + m_exit_status = message.GetExitStatus(); + if (m_exit_now) + { + SetPrivateState(eStateExited); + m_monitor->Detach(GetID()); + } + else + { + StopAllThreads(message.GetTID()); + SetPrivateState(eStateStopped); + } + } + else + { + StopAllThreads(message.GetTID()); + SetPrivateState(eStateStopped); + } + break; + + case ProcessMessage::eExitMessage: + assert(thread); + thread->SetState(eStateExited); + // FIXME: I'm not sure we need to do this. + if (message.GetTID() == GetID()) + { + m_exit_status = message.GetExitStatus(); + SetExitStatus(m_exit_status, NULL); + } + else if (!IsAThreadRunning()) + SetPrivateState(eStateStopped); + break; + + case ProcessMessage::eSignalMessage: + case ProcessMessage::eSignalDeliveredMessage: + if (message.GetSignal() == SIGSTOP && + AddThreadForInitialStopIfNeeded(message.GetTID())) + return; + // Intentional fall-through + + case ProcessMessage::eBreakpointMessage: + case ProcessMessage::eTraceMessage: + case ProcessMessage::eWatchpointMessage: + case ProcessMessage::eNewThreadMessage: + case ProcessMessage::eCrashMessage: + assert(thread); + thread->SetState(eStateStopped); + StopAllThreads(message.GetTID()); + SetPrivateState(eStateStopped); + break; + } + + m_message_queue.push(message); +} + +void +ProcessPOSIX::StopAllThreads(lldb::tid_t stop_tid) +{ + // FIXME: Will this work the same way on FreeBSD and Linux? +} + +bool +ProcessPOSIX::AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid) +{ + bool added_to_set = false; + ThreadStopSet::iterator it = m_seen_initial_stop.find(stop_tid); + if (it == m_seen_initial_stop.end()) + { + m_seen_initial_stop.insert(stop_tid); + added_to_set = true; + } + return added_to_set; +} + +POSIXThread * +ProcessPOSIX::CreateNewPOSIXThread(lldb_private::Process &process, lldb::tid_t tid) +{ + return new POSIXThread(process, tid); +} + +void +ProcessPOSIX::RefreshStateAfterStop() +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessPOSIX::%s(), message_queue size = %d", __FUNCTION__, (int)m_message_queue.size()); + + Mutex::Locker lock(m_message_mutex); + + // This method used to only handle one message. Changing it to loop allows + // it to handle the case where we hit a breakpoint while handling a different + // breakpoint. + while (!m_message_queue.empty()) + { + ProcessMessage &message = m_message_queue.front(); + + // Resolve the thread this message corresponds to and pass it along. + lldb::tid_t tid = message.GetTID(); + if (log) + log->Printf ("ProcessPOSIX::%s(), message_queue size = %d, pid = %" PRIi64, __FUNCTION__, (int)m_message_queue.size(), tid); + POSIXThread *thread = static_cast<POSIXThread*>( + GetThreadList().FindThreadByID(tid, false).get()); + + if (message.GetKind() == ProcessMessage::eNewThreadMessage) + { + if (log) + log->Printf ("ProcessPOSIX::%s() adding thread, tid = %" PRIi64, __FUNCTION__, message.GetChildTID()); + lldb::tid_t child_tid = message.GetChildTID(); + ThreadSP thread_sp; + thread_sp.reset(CreateNewPOSIXThread(*this, child_tid)); + + Mutex::Locker lock(m_thread_list.GetMutex()); + + m_thread_list.AddThread(thread_sp); + } + + m_thread_list.RefreshStateAfterStop(); + + if (thread) + thread->Notify(message); + + if (message.GetKind() == ProcessMessage::eExitMessage) + { + // FIXME: We should tell the user about this, but the limbo message is probably better for that. + if (log) + log->Printf ("ProcessPOSIX::%s() removing thread, tid = %" PRIi64, __FUNCTION__, tid); + + Mutex::Locker lock(m_thread_list.GetMutex()); + + ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false); + thread_sp.reset(); + m_seen_initial_stop.erase(tid); + } + + m_message_queue.pop(); + } +} + +bool +ProcessPOSIX::IsAlive() +{ + StateType state = GetPrivateState(); + return state != eStateDetached + && state != eStateExited + && state != eStateInvalid + && state != eStateUnloaded; +} + +size_t +ProcessPOSIX::DoReadMemory(addr_t vm_addr, + void *buf, size_t size, Error &error) +{ + assert(m_monitor); + return m_monitor->ReadMemory(vm_addr, buf, size, error); +} + +size_t +ProcessPOSIX::DoWriteMemory(addr_t vm_addr, const void *buf, size_t size, + Error &error) +{ + assert(m_monitor); + return m_monitor->WriteMemory(vm_addr, buf, size, error); +} + +addr_t +ProcessPOSIX::DoAllocateMemory(size_t size, uint32_t permissions, + Error &error) +{ + addr_t allocated_addr = LLDB_INVALID_ADDRESS; + + unsigned prot = 0; + if (permissions & lldb::ePermissionsReadable) + prot |= eMmapProtRead; + if (permissions & lldb::ePermissionsWritable) + prot |= eMmapProtWrite; + if (permissions & lldb::ePermissionsExecutable) + prot |= eMmapProtExec; + + if (InferiorCallMmap(this, allocated_addr, 0, size, prot, + eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) { + m_addr_to_mmap_size[allocated_addr] = size; + error.Clear(); + } else { + allocated_addr = LLDB_INVALID_ADDRESS; + error.SetErrorStringWithFormat("unable to allocate %zu bytes of memory with permissions %s", size, GetPermissionsAsCString (permissions)); + } + + return allocated_addr; +} + +Error +ProcessPOSIX::DoDeallocateMemory(lldb::addr_t addr) +{ + Error error; + MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); + if (pos != m_addr_to_mmap_size.end() && + InferiorCallMunmap(this, addr, pos->second)) + m_addr_to_mmap_size.erase (pos); + else + error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); + + return error; +} + +addr_t +ProcessPOSIX::ResolveIndirectFunction(const Address *address, Error &error) +{ + addr_t function_addr = LLDB_INVALID_ADDRESS; + if (address == NULL) { + error.SetErrorStringWithFormat("unable to determine direct function call for NULL address"); + } else if (!InferiorCall(this, address, function_addr)) { + function_addr = LLDB_INVALID_ADDRESS; + error.SetErrorStringWithFormat("unable to determine direct function call for indirect function %s", + address->CalculateSymbolContextSymbol()->GetName().AsCString()); + } + return function_addr; +} + +size_t +ProcessPOSIX::GetSoftwareBreakpointTrapOpcode(BreakpointSite* bp_site) +{ + static const uint8_t g_i386_opcode[] = { 0xCC }; + + ArchSpec arch = GetTarget().GetArchitecture(); + const uint8_t *opcode = NULL; + size_t opcode_size = 0; + + switch (arch.GetCore()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_64_x86_64: + opcode = g_i386_opcode; + opcode_size = sizeof(g_i386_opcode); + break; + } + + bp_site->SetTrapOpcode(opcode, opcode_size); + return opcode_size; +} + +Error +ProcessPOSIX::EnableBreakpointSite(BreakpointSite *bp_site) +{ + return EnableSoftwareBreakpoint(bp_site); +} + +Error +ProcessPOSIX::DisableBreakpointSite(BreakpointSite *bp_site) +{ + return DisableSoftwareBreakpoint(bp_site); +} + +Error +ProcessPOSIX::EnableWatchpoint(Watchpoint *wp, bool notify) +{ + Error error; + if (wp) + { + user_id_t watchID = wp->GetID(); + addr_t addr = wp->GetLoadAddress(); + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + if (log) + log->Printf ("ProcessPOSIX::EnableWatchpoint(watchID = %" PRIu64 ")", + watchID); + if (wp->IsEnabled()) + { + if (log) + log->Printf("ProcessPOSIX::EnableWatchpoint(watchID = %" PRIu64 + ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", + watchID, (uint64_t)addr); + return error; + } + + // Try to find a vacant watchpoint slot in the inferiors' main thread + uint32_t wp_hw_index = LLDB_INVALID_INDEX32; + Mutex::Locker lock(m_thread_list.GetMutex()); + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(0, false).get()); + + if (thread) + wp_hw_index = thread->FindVacantWatchpointIndex(); + + if (wp_hw_index == LLDB_INVALID_INDEX32) + { + error.SetErrorString("Setting hardware watchpoint failed."); + } + else + { + wp->SetHardwareIndex(wp_hw_index); + bool wp_enabled = true; + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + if (thread) + wp_enabled &= thread->EnableHardwareWatchpoint(wp); + else + wp_enabled = false; + } + if (wp_enabled) + { + wp->SetEnabled(true, notify); + return error; + } + else + { + // Watchpoint enabling failed on at least one + // of the threads so roll back all of them + DisableWatchpoint(wp, false); + error.SetErrorString("Setting hardware watchpoint failed"); + } + } + } + else + error.SetErrorString("Watchpoint argument was NULL."); + return error; +} + +Error +ProcessPOSIX::DisableWatchpoint(Watchpoint *wp, bool notify) +{ + Error error; + if (wp) + { + user_id_t watchID = wp->GetID(); + addr_t addr = wp->GetLoadAddress(); + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_WATCHPOINTS)); + if (log) + log->Printf("ProcessPOSIX::DisableWatchpoint(watchID = %" PRIu64 ")", + watchID); + if (!wp->IsEnabled()) + { + if (log) + log->Printf("ProcessPOSIX::DisableWatchpoint(watchID = %" PRIu64 + ") addr = 0x%8.8" PRIx64 ": watchpoint already disabled.", + watchID, (uint64_t)addr); + // This is needed (for now) to keep watchpoints disabled correctly + wp->SetEnabled(false, notify); + return error; + } + + if (wp->IsHardware()) + { + bool wp_disabled = true; + Mutex::Locker lock(m_thread_list.GetMutex()); + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + if (thread) + wp_disabled &= thread->DisableHardwareWatchpoint(wp); + else + wp_disabled = false; + } + if (wp_disabled) + { + wp->SetHardwareIndex(LLDB_INVALID_INDEX32); + wp->SetEnabled(false, notify); + return error; + } + else + error.SetErrorString("Disabling hardware watchpoint failed"); + } + } + else + error.SetErrorString("Watchpoint argument was NULL."); + return error; +} + +Error +ProcessPOSIX::GetWatchpointSupportInfo(uint32_t &num) +{ + Error error; + Mutex::Locker lock(m_thread_list.GetMutex()); + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(0, false).get()); + if (thread) + num = thread->NumSupportedHardwareWatchpoints(); + else + error.SetErrorString("Process does not exist."); + return error; +} + +Error +ProcessPOSIX::GetWatchpointSupportInfo(uint32_t &num, bool &after) +{ + Error error = GetWatchpointSupportInfo(num); + // Watchpoints trigger and halt the inferior after + // the corresponding instruction has been executed. + after = true; + return error; +} + +uint32_t +ProcessPOSIX::UpdateThreadListIfNeeded() +{ + Mutex::Locker lock(m_thread_list.GetMutex()); + // Do not allow recursive updates. + return m_thread_list.GetSize(false); +} + +bool +ProcessPOSIX::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessPOSIX::%s() (pid = %" PRIi64 ")", __FUNCTION__, GetID()); + + // Update the process thread list with this new thread. + // FIXME: We should be using tid, not pid. + assert(m_monitor); + ThreadSP thread_sp (old_thread_list.FindThreadByID (GetID(), false)); + if (!thread_sp) { + thread_sp.reset(CreateNewPOSIXThread(*this, GetID())); + } + + if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) + log->Printf ("ProcessPOSIX::%s() updated pid = %" PRIi64, __FUNCTION__, GetID()); + new_thread_list.AddThread(thread_sp); + + return new_thread_list.GetSize(false) > 0; +} + +ByteOrder +ProcessPOSIX::GetByteOrder() const +{ + // FIXME: We should be able to extract this value directly. See comment in + // ProcessPOSIX(). + return m_byte_order; +} + +size_t +ProcessPOSIX::PutSTDIN(const char *buf, size_t len, Error &error) +{ + ssize_t status; + if ((status = write(m_monitor->GetTerminalFD(), buf, len)) < 0) + { + error.SetErrorToErrno(); + return 0; + } + return status; +} + +UnixSignals & +ProcessPOSIX::GetUnixSignals() +{ + return m_signals; +} + +//------------------------------------------------------------------------------ +// Utility functions. + +bool +ProcessPOSIX::HasExited() +{ + switch (GetPrivateState()) + { + default: + break; + + case eStateDetached: + case eStateExited: + return true; + } + + return false; +} + +bool +ProcessPOSIX::IsStopped() +{ + switch (GetPrivateState()) + { + default: + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + return true; + } + + return false; +} + +bool +ProcessPOSIX::IsAThreadRunning() +{ + bool is_running = false; + Mutex::Locker lock(m_thread_list.GetMutex()); + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + StateType thread_state = thread->GetState(); + if (thread_state == eStateRunning || thread_state == eStateStepping) + { + is_running = true; + break; + } + } + return is_running; +} diff --git a/source/Plugins/Process/POSIX/ProcessPOSIX.h b/source/Plugins/Process/POSIX/ProcessPOSIX.h new file mode 100644 index 000000000000..48b19bac47e7 --- /dev/null +++ b/source/Plugins/Process/POSIX/ProcessPOSIX.h @@ -0,0 +1,211 @@ +//===-- ProcessPOSIX.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessPOSIX_H_ +#define liblldb_ProcessPOSIX_H_ + +// C Includes + +// C++ Includes +#include <queue> +#include <set> + +// Other libraries and framework includes +#include "lldb/Target/Process.h" +#include "lldb/Target/UnixSignals.h" +#include "ProcessMessage.h" + +class ProcessMonitor; +class POSIXThread; + +class ProcessPOSIX : + public lldb_private::Process +{ +public: + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessPOSIX(lldb_private::Target& target, + lldb_private::Listener &listener); + + virtual + ~ProcessPOSIX(); + + //------------------------------------------------------------------ + // Process protocol. + //------------------------------------------------------------------ + virtual void + Finalize(); + + virtual bool + CanDebug(lldb_private::Target &target, bool plugin_specified_by_name); + + virtual lldb_private::Error + WillLaunch(lldb_private::Module *module); + + virtual lldb_private::Error + DoAttachToProcessWithID(lldb::pid_t pid); + + virtual lldb_private::Error + DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module *exe_module, + const lldb_private::ProcessLaunchInfo &launch_info); + + virtual void + DidLaunch(); + + virtual lldb_private::Error + DoResume(); + + virtual lldb_private::Error + DoHalt(bool &caused_stop); + + virtual lldb_private::Error + DoDetach(bool keep_stopped); + + virtual lldb_private::Error + DoSignal(int signal); + + virtual lldb_private::Error + DoDestroy(); + + virtual void + RefreshStateAfterStop(); + + virtual bool + IsAlive(); + + virtual size_t + DoReadMemory(lldb::addr_t vm_addr, + void *buf, + size_t size, + lldb_private::Error &error); + + virtual size_t + DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory(size_t size, uint32_t permissions, + lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory(lldb::addr_t ptr); + + virtual lldb::addr_t + ResolveIndirectFunction(const lldb_private::Address *address, lldb_private::Error &error); + + virtual size_t + GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site); + + virtual lldb_private::Error + EnableBreakpointSite(lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpointSite(lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + GetWatchpointSupportInfo(uint32_t &num); + + virtual lldb_private::Error + GetWatchpointSupportInfo(uint32_t &num, bool &after); + + virtual uint32_t + UpdateThreadListIfNeeded(); + + virtual bool + UpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list) = 0; + + virtual lldb::ByteOrder + GetByteOrder() const; + + virtual lldb::addr_t + GetImageInfoAddress(); + + virtual size_t + PutSTDIN(const char *buf, size_t len, lldb_private::Error &error); + + //-------------------------------------------------------------------------- + // ProcessPOSIX internal API. + + /// Registers the given message with this process. + void SendMessage(const ProcessMessage &message); + + ProcessMonitor & + GetMonitor() { assert(m_monitor); return *m_monitor; } + + lldb_private::UnixSignals & + GetUnixSignals(); + + const char * + GetFilePath(const lldb_private::ProcessLaunchInfo::FileAction *file_action, + const char *default_path); + + /// Stops all threads in the process. + /// The \p stop_tid parameter indicates the thread which initiated the stop. + virtual void + StopAllThreads(lldb::tid_t stop_tid); + + /// Adds the thread to the list of threads for which we have received the initial stopping signal. + /// The \p stop_tid paramter indicates the thread which the stop happened for. + bool + AddThreadForInitialStopIfNeeded(lldb::tid_t stop_tid); + + virtual POSIXThread * + CreateNewPOSIXThread(lldb_private::Process &process, lldb::tid_t tid); + +protected: + /// Target byte order. + lldb::ByteOrder m_byte_order; + + /// Process monitor; + ProcessMonitor *m_monitor; + + /// The module we are executing. + lldb_private::Module *m_module; + + /// Message queue notifying this instance of inferior process state changes. + lldb_private::Mutex m_message_mutex; + std::queue<ProcessMessage> m_message_queue; + + /// Drive any exit events to completion. + bool m_exit_now; + + /// OS-specific signal set. + lldb_private::UnixSignals m_signals; + + /// Returns true if the process has exited. + bool HasExited(); + + /// Returns true if the process is stopped. + bool IsStopped(); + + /// Returns true if at least one running is currently running + bool IsAThreadRunning(); + + typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap; + MMapMap m_addr_to_mmap_size; + + typedef std::set<lldb::tid_t> ThreadStopSet; + /// Every thread begins with a stop signal. This keeps track + /// of the threads for which we have received the stop signal. + ThreadStopSet m_seen_initial_stop; +}; + +#endif // liblldb_MacOSXProcess_H_ diff --git a/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp b/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp new file mode 100644 index 000000000000..624ca87b883a --- /dev/null +++ b/source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp @@ -0,0 +1,193 @@ +//===-- ProcessPOSIXLog.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessPOSIXLog.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessPOSIX.h" +#include "ProcessPOSIXLog.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_log_sp the first time this function is +// called. +static bool g_log_enabled = false; +static Log * g_log = NULL; +static Log * +GetLog () +{ + if (!g_log_enabled) + return NULL; + return g_log; +} + + +Log * +ProcessPOSIXLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log(GetLog ()); + if (log && mask) + { + uint32_t log_mask = log->GetMask().Get(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +static uint32_t +GetFlagBits (const char *arg) +{ + if (::strcasecmp (arg, "all") == 0 ) return POSIX_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) return POSIX_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) return POSIX_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) return POSIX_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) return POSIX_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) return POSIX_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) return POSIX_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) return POSIX_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) return POSIX_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) return POSIX_LOG_PROCESS; + else if (::strcasecmp (arg, "ptrace") == 0 ) return POSIX_LOG_PTRACE; + else if (::strcasecmp (arg, "registers") == 0 ) return POSIX_LOG_REGISTERS; + else if (::strcasecmp (arg, "step") == 0 ) return POSIX_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) return POSIX_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) return POSIX_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) return POSIX_LOG_WATCHPOINTS; + return 0; +} + +void +ProcessPOSIXLog::DisableLog (const char **args, Stream *feedback_strm) +{ + Log *log (GetLog ()); + if (log) + { + uint32_t flag_bits = 0; + + flag_bits = log->GetMask().Get(); + for (; args[0]; args++) + { + const char *arg = args[0]; + uint32_t bits = GetFlagBits(arg); + + if (bits) + { + flag_bits &= ~bits; + } + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories (feedback_strm); + } + } + + log->GetMask().Reset (flag_bits); + if (flag_bits == 0) + g_log_enabled = false; + } + + return; +} + +Log * +ProcessPOSIXLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const char **args, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits = 0; + if (g_log) + flag_bits = g_log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + bool got_unknown_category = false; + for (; args[0]; args++) + { + const char *arg = args[0]; + uint32_t bits = GetFlagBits(arg); + + if (bits) + { + flag_bits |= bits; + } + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = POSIX_LOG_DEFAULT; + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + g_log_enabled = true; + } + return g_log; +} + +void +ProcessPOSIXLog::ListLogCategories (Stream *strm) +{ + strm->Printf ("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " async - log asynchronous activity\n" + " break - log breakpoints\n" + " communication - log communication activity\n" + " default - enable the default set of logging categories for liblldb\n" + " packets - log gdb remote packets\n" + " memory - log memory reads and writes\n" + " data-short - log memory bytes for memory reads and writes for short transactions only\n" + " data-long - log memory bytes for memory reads and writes for all transactions\n" + " process - log process events and activities\n" +#ifndef LLDB_CONFIGURATION_BUILDANDINTEGRATION + " ptrace - log all calls to ptrace\n" +#endif + " registers - log register read/writes\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n" + " watch - log watchpoint related activities\n", ProcessPOSIXLog::m_pluginname); +} + + +void +ProcessPOSIXLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} + +int ProcessPOSIXLog::m_nestinglevel; +const char *ProcessPOSIXLog::m_pluginname = ""; diff --git a/source/Plugins/Process/POSIX/ProcessPOSIXLog.h b/source/Plugins/Process/POSIX/ProcessPOSIXLog.h new file mode 100644 index 000000000000..a1e2e3747d21 --- /dev/null +++ b/source/Plugins/Process/POSIX/ProcessPOSIXLog.h @@ -0,0 +1,111 @@ +//===-- ProcessPOSIXLog.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessPOSIXLog_h_ +#define liblldb_ProcessPOSIXLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define POSIX_LOG_VERBOSE (1u << 0) +#define POSIX_LOG_PROCESS (1u << 1) +#define POSIX_LOG_THREAD (1u << 2) +#define POSIX_LOG_PACKETS (1u << 3) +#define POSIX_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define POSIX_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define POSIX_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define POSIX_LOG_BREAKPOINTS (1u << 7) +#define POSIX_LOG_WATCHPOINTS (1u << 8) +#define POSIX_LOG_STEP (1u << 9) +#define POSIX_LOG_COMM (1u << 10) +#define POSIX_LOG_ASYNC (1u << 11) +#define POSIX_LOG_PTRACE (1u << 12) +#define POSIX_LOG_REGISTERS (1u << 13) +#define POSIX_LOG_ALL (UINT32_MAX) +#define POSIX_LOG_DEFAULT POSIX_LOG_PACKETS + +// The size which determines "short memory reads/writes". +#define POSIX_LOG_MEMORY_SHORT_BYTES (4 * sizeof(ptrdiff_t)) + +class ProcessPOSIXLog +{ + static int m_nestinglevel; + static const char *m_pluginname; + +public: + static void + RegisterPluginName(const char *pluginName) + { + m_pluginname = pluginName; + } + + static void + RegisterPluginName(lldb_private::ConstString pluginName) + { + m_pluginname = pluginName.GetCString(); + } + + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static void + DisableLog (const char **args, lldb_private::Stream *feedback_strm); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, + const char **args, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); + + // The following functions can be used to enable the client to limit + // logging to only the top level function calls. This is useful for + // recursive functions. FIXME: not thread safe! + // Example: + // void NestingFunc() { + // LogSP log (ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_ALL)); + // if (log) + // { + // ProcessPOSIXLog::IncNestLevel(); + // if (ProcessPOSIXLog::AtTopNestLevel()) + // log->Print(msg); + // } + // NestingFunc(); + // if (log) + // ProcessPOSIXLog::DecNestLevel(); + // } + + static bool + AtTopNestLevel() + { + return m_nestinglevel == 1; + } + + static void + IncNestLevel() + { + ++m_nestinglevel; + } + + static void + DecNestLevel() + { + --m_nestinglevel; + assert(m_nestinglevel >= 0); + } +}; + +#endif // liblldb_ProcessPOSIXLog_h_ diff --git a/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp b/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp new file mode 100644 index 000000000000..0fb9dc1cb3dc --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp @@ -0,0 +1,136 @@ +//===-- RegisterContextFreeBSD_x86_64.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include "RegisterContextFreeBSD_x86_64.h" +#include <vector> + +using namespace lldb_private; + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (offsetof(GPR, regname)) + +// Update the FreeBSD specific information (offset and size). +#define UPDATE_GPR_INFO(reg) \ +do { \ + GetRegisterContext()[gpr_##reg].byte_size = sizeof(GPR::reg); \ + GetRegisterContext()[gpr_##reg].byte_offset = GPR_OFFSET(reg); \ +} while(false); + +#define UPDATE_I386_GPR_INFO(i386_reg, reg) \ +do { \ + GetRegisterContext()[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \ +} while(false); + +typedef struct _GPR +{ + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rcx; + uint64_t rax; + uint32_t trapno; + uint16_t fs; + uint16_t gs; + uint32_t err; + uint16_t es; + uint16_t ds; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; +} GPR; + +// Use a singleton function to avoid global constructors in shared libraries. +static std::vector<RegisterInfo> & GetRegisterContext () { + static std::vector<RegisterInfo> g_register_infos; + return g_register_infos; +} + + +RegisterContextFreeBSD_x86_64::RegisterContextFreeBSD_x86_64(Thread &thread, uint32_t concrete_frame_idx): + RegisterContext_x86_64(thread, concrete_frame_idx) +{ +} + +size_t +RegisterContextFreeBSD_x86_64::GetGPRSize() +{ + return sizeof(GPR); +} + +const RegisterInfo * +RegisterContextFreeBSD_x86_64::GetRegisterInfo() +{ + // Allocate RegisterInfo only once + if (GetRegisterContext().empty()) + { + // Copy the register information from base class + const RegisterInfo *base_info = RegisterContext_x86_64::GetRegisterInfo(); + if (base_info) + { + GetRegisterContext().insert(GetRegisterContext().end(), &base_info[0], &base_info[k_num_registers]); + // Update the FreeBSD specific register information (offset and size). + UpdateRegisterInfo(); + } + } + return &GetRegisterContext()[0]; +} + +void +RegisterContextFreeBSD_x86_64::UpdateRegisterInfo() +{ + UPDATE_GPR_INFO(rax); + UPDATE_GPR_INFO(rbx); + UPDATE_GPR_INFO(rcx); + UPDATE_GPR_INFO(rdx); + UPDATE_GPR_INFO(rdi); + UPDATE_GPR_INFO(rsi); + UPDATE_GPR_INFO(rbp); + UPDATE_GPR_INFO(rsp); + UPDATE_GPR_INFO(r8); + UPDATE_GPR_INFO(r9); + UPDATE_GPR_INFO(r10); + UPDATE_GPR_INFO(r11); + UPDATE_GPR_INFO(r12); + UPDATE_GPR_INFO(r13); + UPDATE_GPR_INFO(r14); + UPDATE_GPR_INFO(r15); + UPDATE_GPR_INFO(rip); + UPDATE_GPR_INFO(rflags); + UPDATE_GPR_INFO(cs); + UPDATE_GPR_INFO(fs); + UPDATE_GPR_INFO(gs); + UPDATE_GPR_INFO(ss); + UPDATE_GPR_INFO(ds); + UPDATE_GPR_INFO(es); + + UPDATE_I386_GPR_INFO(eax, rax); + UPDATE_I386_GPR_INFO(ebx, rbx); + UPDATE_I386_GPR_INFO(ecx, rcx); + UPDATE_I386_GPR_INFO(edx, rdx); + UPDATE_I386_GPR_INFO(edi, rdi); + UPDATE_I386_GPR_INFO(esi, rsi); + UPDATE_I386_GPR_INFO(ebp, rbp); + UPDATE_I386_GPR_INFO(esp, rsp); + UPDATE_I386_GPR_INFO(eip, rip); + UPDATE_I386_GPR_INFO(eflags, rflags); +} + diff --git a/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h b/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h new file mode 100644 index 000000000000..ffff40a9c65b --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h @@ -0,0 +1,32 @@ +//===-- RegisterContextFreeBSD_x86_64.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextFreeBSD_x86_64_H_ +#define liblldb_RegisterContextFreeBSD_x86_64_H_ + +#include "Plugins/Process/POSIX/RegisterContext_x86_64.h" + +class RegisterContextFreeBSD_x86_64: + public RegisterContext_x86_64 +{ +public: + RegisterContextFreeBSD_x86_64(lldb_private::Thread &thread, uint32_t concrete_frame_idx); + + size_t + GetGPRSize(); + +protected: + virtual const lldb_private::RegisterInfo * + GetRegisterInfo(); + + virtual void + UpdateRegisterInfo(); +}; + +#endif diff --git a/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp b/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp new file mode 100644 index 000000000000..c1aea2a41a1f --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp @@ -0,0 +1,180 @@ +//===-- RegisterContextLinux_x86_64.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include "llvm/Support/Compiler.h" +#include "RegisterContextLinux_x86_64.h" +#include <vector> + +using namespace lldb_private; + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (offsetof(GPR, regname)) + +// Update the Linux specific information (offset and size). +#define UPDATE_GPR_INFO(reg) \ +do { \ + GetRegisterContext()[gpr_##reg].byte_size = sizeof(GPR::reg); \ + GetRegisterContext()[gpr_##reg].byte_offset = GPR_OFFSET(reg); \ +} while(false); + +#define UPDATE_I386_GPR_INFO(i386_reg, reg) \ +do { \ + GetRegisterContext()[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \ +} while(false); + +#define DR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(UserArea, u_debugreg[reg_index])) + +#define UPDATE_DR_INFO(reg_index) \ +do { \ + GetRegisterContext()[dr##reg_index].byte_size = sizeof(UserArea::u_debugreg[0]); \ + GetRegisterContext()[dr##reg_index].byte_offset = DR_OFFSET(reg_index); \ +} while(false); + +typedef struct _GPR +{ + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t rbp; + uint64_t rbx; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rsi; + uint64_t rdi; + uint64_t orig_ax; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t rsp; + uint64_t ss; + uint64_t fs_base; + uint64_t gs_base; + uint64_t ds; + uint64_t es; + uint64_t fs; + uint64_t gs; +} GPR; + +typedef RegisterContext_x86_64::FXSAVE FXSAVE; + +struct UserArea +{ + GPR gpr; // General purpose registers. + int32_t fpvalid; // True if FPU is being used. + int32_t pad0; + FXSAVE i387; // General purpose floating point registers (see FPR for extended register sets). + uint64_t tsize; // Text segment size. + uint64_t dsize; // Data segment size. + uint64_t ssize; // Stack segment size. + uint64_t start_code; // VM address of text. + uint64_t start_stack; // VM address of stack bottom (top in rsp). + int64_t signal; // Signal causing core dump. + int32_t reserved; // Unused. + int32_t pad1; + uint64_t ar0; // Location of GPR's. + FXSAVE* fpstate; // Location of FPR's. + uint64_t magic; // Identifier for core dumps. + char u_comm[32]; // Command causing core dump. + uint64_t u_debugreg[8]; // Debug registers (DR0 - DR7). + uint64_t error_code; // CPU error code. + uint64_t fault_address; // Control register CR3. +}; + +// Use a singleton function to avoid global constructors in shared libraries. +static std::vector<RegisterInfo> & GetRegisterContext () { + static std::vector<RegisterInfo> g_register_infos; + return g_register_infos; +} + +RegisterContextLinux_x86_64::RegisterContextLinux_x86_64(Thread &thread, uint32_t concrete_frame_idx): + RegisterContext_x86_64(thread, concrete_frame_idx) +{ +} + +size_t +RegisterContextLinux_x86_64::GetGPRSize() +{ + return sizeof(GPR); +} + +const RegisterInfo * +RegisterContextLinux_x86_64::GetRegisterInfo() +{ + // Allocate RegisterInfo only once + if (GetRegisterContext().empty()) + { + // Copy the register information from base class + const RegisterInfo *base_info = RegisterContext_x86_64::GetRegisterInfo(); + if (base_info) + { + GetRegisterContext().insert(GetRegisterContext().end(), &base_info[0], &base_info[k_num_registers]); + // Update the Linux specific register information (offset and size). + UpdateRegisterInfo(); + } + } + return &GetRegisterContext()[0]; +} + +void +RegisterContextLinux_x86_64::UpdateRegisterInfo() +{ + UPDATE_GPR_INFO(rax); + UPDATE_GPR_INFO(rbx); + UPDATE_GPR_INFO(rcx); + UPDATE_GPR_INFO(rdx); + UPDATE_GPR_INFO(rdi); + UPDATE_GPR_INFO(rsi); + UPDATE_GPR_INFO(rbp); + UPDATE_GPR_INFO(rsp); + UPDATE_GPR_INFO(r8); + UPDATE_GPR_INFO(r9); + UPDATE_GPR_INFO(r10); + UPDATE_GPR_INFO(r11); + UPDATE_GPR_INFO(r12); + UPDATE_GPR_INFO(r13); + UPDATE_GPR_INFO(r14); + UPDATE_GPR_INFO(r15); + UPDATE_GPR_INFO(rip); + UPDATE_GPR_INFO(rflags); + UPDATE_GPR_INFO(cs); + UPDATE_GPR_INFO(fs); + UPDATE_GPR_INFO(gs); + UPDATE_GPR_INFO(ss); + UPDATE_GPR_INFO(ds); + UPDATE_GPR_INFO(es); + + UPDATE_I386_GPR_INFO(eax, rax); + UPDATE_I386_GPR_INFO(ebx, rbx); + UPDATE_I386_GPR_INFO(ecx, rcx); + UPDATE_I386_GPR_INFO(edx, rdx); + UPDATE_I386_GPR_INFO(edi, rdi); + UPDATE_I386_GPR_INFO(esi, rsi); + UPDATE_I386_GPR_INFO(ebp, rbp); + UPDATE_I386_GPR_INFO(esp, rsp); + UPDATE_I386_GPR_INFO(eip, rip); + UPDATE_I386_GPR_INFO(eflags, rflags); + + UPDATE_DR_INFO(0); + UPDATE_DR_INFO(1); + UPDATE_DR_INFO(2); + UPDATE_DR_INFO(3); + UPDATE_DR_INFO(4); + UPDATE_DR_INFO(5); + UPDATE_DR_INFO(6); + UPDATE_DR_INFO(7); +} + diff --git a/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h b/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h new file mode 100644 index 000000000000..1509ef55b8dc --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h @@ -0,0 +1,32 @@ +//===-- RegisterContextLinux_x86_64.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextLinux_x86_64_H_ +#define liblldb_RegisterContextLinux_x86_64_H_ + +#include "Plugins/Process/POSIX/RegisterContext_x86_64.h" + +class RegisterContextLinux_x86_64: + public RegisterContext_x86_64 +{ +public: + RegisterContextLinux_x86_64(lldb_private::Thread &thread, uint32_t concrete_frame_idx); + + size_t + GetGPRSize(); + +protected: + virtual const lldb_private::RegisterInfo * + GetRegisterInfo(); + + virtual void + UpdateRegisterInfo(); +}; + +#endif diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIX.h b/source/Plugins/Process/POSIX/RegisterContextPOSIX.h new file mode 100644 index 000000000000..63ae01e83a90 --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContextPOSIX.h @@ -0,0 +1,70 @@ +//===-- RegisterContextPOSIX.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextPOSIX_H_ +#define liblldb_RegisterContextPOSIX_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Target/RegisterContext.h" + +//------------------------------------------------------------------------------ +/// @class RegisterContextPOSIX +/// +/// @brief Extends RegisterClass with a few virtual operations useful on POSIX. +class RegisterContextPOSIX + : public lldb_private::RegisterContext +{ +public: + RegisterContextPOSIX(lldb_private::Thread &thread, + uint32_t concrete_frame_idx) + : RegisterContext(thread, concrete_frame_idx) + { m_watchpoints_initialized = false; } + + /// Updates the register state of the associated thread after hitting a + /// breakpoint (if that make sense for the architecture). Default + /// implementation simply returns true for architectures which do not + /// require any update. + /// + /// @return + /// True if the operation succeeded and false otherwise. + virtual bool UpdateAfterBreakpoint() { return true; } + + /// Determines the index in lldb's register file given a kernel byte offset. + virtual unsigned + GetRegisterIndexFromOffset(unsigned offset) { return LLDB_INVALID_REGNUM; } + + // Checks to see if a watchpoint specified by hw_index caused the inferior + // to stop. + virtual bool + IsWatchpointHit (uint32_t hw_index) { return false; } + + // Resets any watchpoints that have been hit. + virtual bool + ClearWatchpointHits () { return false; } + + // Returns the watchpoint address associated with a watchpoint hardware + // index. + virtual lldb::addr_t + GetWatchpointAddress (uint32_t hw_index) { return LLDB_INVALID_ADDRESS; } + + virtual bool + IsWatchpointVacant (uint32_t hw_index) { return false; } + + virtual bool + SetHardwareWatchpointWithIndex (lldb::addr_t addr, size_t size, + bool read, bool write, + uint32_t hw_index) { return false; } + +protected: + bool m_watchpoints_initialized; +}; + +#endif // #ifndef liblldb_RegisterContextPOSIX_H_ diff --git a/source/Plugins/Process/POSIX/RegisterContext_i386.cpp b/source/Plugins/Process/POSIX/RegisterContext_i386.cpp new file mode 100644 index 000000000000..49676bd3fc73 --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContext_i386.cpp @@ -0,0 +1,551 @@ +//===-- RegisterContextPOSIX_i386.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/Thread.h" +#include "lldb/Host/Endian.h" +#include "llvm/Support/Compiler.h" + +#include "ProcessPOSIX.h" +#include "ProcessPOSIXLog.h" +#include "ProcessMonitor.h" +#include "RegisterContext_i386.h" +#include "RegisterContext_x86.h" + +using namespace lldb_private; +using namespace lldb; + +enum +{ + k_first_gpr, + gpr_eax = k_first_gpr, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, +#ifdef __FreeBSD__ + gpr_orig_ax, +#endif + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs, + k_last_gpr = gpr_gs, + + k_first_fpr, + fpu_fcw = k_first_fpr, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_foo, + fpu_fos, + fpu_mxcsr, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + k_last_fpr = fpu_xmm7, + + k_num_registers, + k_num_gpr_registers = k_last_gpr - k_first_gpr + 1, + k_num_fpu_registers = k_last_fpr - k_first_fpr + 1 +}; + +// Number of register sets provided by this context. +enum +{ + k_num_register_sets = 2 +}; + +static const +uint32_t g_gpr_regnums[k_num_gpr_registers] = +{ + gpr_eax, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, +#ifdef __FreeBSD__ + gpr_orig_ax, +#endif + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs, +}; + +static const uint32_t +g_fpu_regnums[k_num_fpu_registers] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_foo, + fpu_fos, + fpu_mxcsr, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, +}; + +static const RegisterSet +g_reg_sets[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums } +}; + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (offsetof(RegisterContext_i386::UserArea, regs) + \ + offsetof(RegisterContext_i386::GPR, regname)) + +// Computes the offset of the given FPR in the user data area. +#define FPR_OFFSET(regname) \ + (offsetof(RegisterContext_i386::UserArea, i387) + \ + offsetof(RegisterContext_i386::FPU, regname)) + +// Number of bytes needed to represent a GPR. +#define GPR_SIZE(reg) sizeof(((RegisterContext_i386::GPR*)NULL)->reg) + +// Number of bytes needed to represent a FPR. +#define FPR_SIZE(reg) sizeof(((RegisterContext_i386::FPU*)NULL)->reg) + +// Number of bytes needed to represent the i'th FP register. +#define FP_SIZE sizeof(((RegisterContext_i386::MMSReg*)NULL)->bytes) + +// Number of bytes needed to represent an XMM register. +#define XMM_SIZE sizeof(RegisterContext_i386::XMMReg) + +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg }, NULL, NULL } + +#define DEFINE_FPR(reg, kind1, kind2, kind3, kind4) \ + { #reg, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, fpu_##reg }, NULL, NULL } + +#define DEFINE_FP(reg, i) \ + { #reg#i, NULL, FP_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + { dwarf_##reg##i, dwarf_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i }, NULL, NULL } + +#define DEFINE_XMM(reg, i) \ + { #reg#i, NULL, XMM_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + { dwarf_##reg##i, dwarf_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i }, NULL, NULL } + +static RegisterInfo +g_register_infos[k_num_registers] = +{ + // General purpose registers. + DEFINE_GPR(eax, NULL, gcc_eax, dwarf_eax, LLDB_INVALID_REGNUM, gdb_eax), + DEFINE_GPR(ebx, NULL, gcc_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, gdb_ebx), + DEFINE_GPR(ecx, NULL, gcc_ecx, dwarf_ecx, LLDB_INVALID_REGNUM, gdb_ecx), + DEFINE_GPR(edx, NULL, gcc_edx, dwarf_edx, LLDB_INVALID_REGNUM, gdb_edx), + DEFINE_GPR(edi, NULL, gcc_edi, dwarf_edi, LLDB_INVALID_REGNUM, gdb_edi), + DEFINE_GPR(esi, NULL, gcc_esi, dwarf_esi, LLDB_INVALID_REGNUM, gdb_esi), + DEFINE_GPR(ebp, "fp", gcc_ebp, dwarf_ebp, LLDB_INVALID_REGNUM, gdb_ebp), + DEFINE_GPR(esp, "sp", gcc_esp, dwarf_esp, LLDB_INVALID_REGNUM, gdb_esp), + DEFINE_GPR(ss, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ss), + DEFINE_GPR(eflags, "flags", gcc_eflags, dwarf_eflags, LLDB_INVALID_REGNUM, gdb_eflags), + DEFINE_GPR(eip, "pc", gcc_eip, dwarf_eip, LLDB_INVALID_REGNUM, gdb_eip), + DEFINE_GPR(cs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_cs), + DEFINE_GPR(ds, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ds), + DEFINE_GPR(es, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_es), + DEFINE_GPR(fs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fs), + DEFINE_GPR(gs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gs), + + // Floating point registers. + DEFINE_FPR(fcw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fcw), + DEFINE_FPR(fsw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fsw), + DEFINE_FPR(ftw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ftw), + DEFINE_FPR(fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fop), + DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_ip), + DEFINE_FPR(cs, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_cs), + DEFINE_FPR(foo, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_dp), + DEFINE_FPR(fos, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ds), + DEFINE_FPR(mxcsr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_mxcsr), + + DEFINE_FP(stmm, 0), + DEFINE_FP(stmm, 1), + DEFINE_FP(stmm, 2), + DEFINE_FP(stmm, 3), + DEFINE_FP(stmm, 4), + DEFINE_FP(stmm, 5), + DEFINE_FP(stmm, 6), + DEFINE_FP(stmm, 7), + + // XMM registers + DEFINE_XMM(xmm, 0), + DEFINE_XMM(xmm, 1), + DEFINE_XMM(xmm, 2), + DEFINE_XMM(xmm, 3), + DEFINE_XMM(xmm, 4), + DEFINE_XMM(xmm, 5), + DEFINE_XMM(xmm, 6), + DEFINE_XMM(xmm, 7), + +}; + +#ifndef NDEBUG +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); +#endif + +static unsigned GetRegOffset(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register number."); + return g_register_infos[reg].byte_offset; +} + +static unsigned GetRegSize(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register number."); + return g_register_infos[reg].byte_size; +} + +RegisterContext_i386::RegisterContext_i386(Thread &thread, + uint32_t concrete_frame_idx) + : RegisterContextPOSIX(thread, concrete_frame_idx) +{ +} + +RegisterContext_i386::~RegisterContext_i386() +{ +} + +ProcessMonitor & +RegisterContext_i386::GetMonitor() +{ + ProcessSP base = CalculateProcess(); + ProcessPOSIX *process = static_cast<ProcessPOSIX*>(base.get()); + return process->GetMonitor(); +} + +void +RegisterContext_i386::Invalidate() +{ +} + +void +RegisterContext_i386::InvalidateAllRegisters() +{ +} + +size_t +RegisterContext_i386::GetRegisterCount() +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContext_i386::GetRegisterInfoAtIndex(size_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + else + return NULL; +} + +size_t +RegisterContext_i386::GetRegisterSetCount() +{ + return k_num_register_sets; +} + +const RegisterSet * +RegisterContext_i386::GetRegisterSet(size_t set) +{ + if (set < k_num_register_sets) + return &g_reg_sets[set]; + else + return NULL; +} + +unsigned +RegisterContext_i386::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + for (reg = 0; reg < k_num_registers; reg++) + { + if (g_register_infos[reg].byte_offset == offset) + break; + } + assert(reg < k_num_registers && "Invalid register offset."); + return reg; +} + +const char * +RegisterContext_i386::GetRegisterName(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register offset."); + return g_register_infos[reg].name; +} + +bool +RegisterContext_i386::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadRegisterValue(m_thread.GetID(), GetRegOffset(reg), + GetRegisterName(reg), GetRegSize(reg), value); +} + +bool +RegisterContext_i386::ReadAllRegisterValues(DataBufferSP &data_sp) +{ + return false; +} + +bool RegisterContext_i386::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteRegisterValue(m_thread.GetID(), GetRegOffset(reg), + GetRegisterName(reg), value); +} + +bool +RegisterContext_i386::WriteAllRegisterValues(const DataBufferSP &data) +{ + return false; +} + +bool +RegisterContext_i386::UpdateAfterBreakpoint() +{ + // PC points one byte past the int3 responsible for the breakpoint. + lldb::addr_t pc; + + if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + return false; + + SetPC(pc - 1); + return true; +} + +uint32_t +RegisterContext_i386::ConvertRegisterKindToRegisterNumber(uint32_t kind, + uint32_t num) +{ + if (kind == eRegisterKindGeneric) + { + switch (num) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_eip; + case LLDB_REGNUM_GENERIC_SP: return gpr_esp; + case LLDB_REGNUM_GENERIC_FP: return gpr_ebp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags; + case LLDB_REGNUM_GENERIC_RA: + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (num) + { + case dwarf_eax: return gpr_eax; + case dwarf_edx: return gpr_edx; + case dwarf_ecx: return gpr_ecx; + case dwarf_ebx: return gpr_ebx; + case dwarf_esi: return gpr_esi; + case dwarf_edi: return gpr_edi; + case dwarf_ebp: return gpr_ebp; + case dwarf_esp: return gpr_esp; + case dwarf_eip: return gpr_eip; + case dwarf_xmm0: return fpu_xmm0; + case dwarf_xmm1: return fpu_xmm1; + case dwarf_xmm2: return fpu_xmm2; + case dwarf_xmm3: return fpu_xmm3; + case dwarf_xmm4: return fpu_xmm4; + case dwarf_xmm5: return fpu_xmm5; + case dwarf_xmm6: return fpu_xmm6; + case dwarf_xmm7: return fpu_xmm7; + case dwarf_stmm0: return fpu_stmm0; + case dwarf_stmm1: return fpu_stmm1; + case dwarf_stmm2: return fpu_stmm2; + case dwarf_stmm3: return fpu_stmm3; + case dwarf_stmm4: return fpu_stmm4; + case dwarf_stmm5: return fpu_stmm5; + case dwarf_stmm6: return fpu_stmm6; + case dwarf_stmm7: return fpu_stmm7; + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGDB) + { + switch (num) + { + case gdb_eax : return gpr_eax; + case gdb_ebx : return gpr_ebx; + case gdb_ecx : return gpr_ecx; + case gdb_edx : return gpr_edx; + case gdb_esi : return gpr_esi; + case gdb_edi : return gpr_edi; + case gdb_ebp : return gpr_ebp; + case gdb_esp : return gpr_esp; + case gdb_eip : return gpr_eip; + case gdb_eflags : return gpr_eflags; + case gdb_cs : return gpr_cs; + case gdb_ss : return gpr_ss; + case gdb_ds : return gpr_ds; + case gdb_es : return gpr_es; + case gdb_fs : return gpr_fs; + case gdb_gs : return gpr_gs; + case gdb_stmm0 : return fpu_stmm0; + case gdb_stmm1 : return fpu_stmm1; + case gdb_stmm2 : return fpu_stmm2; + case gdb_stmm3 : return fpu_stmm3; + case gdb_stmm4 : return fpu_stmm4; + case gdb_stmm5 : return fpu_stmm5; + case gdb_stmm6 : return fpu_stmm6; + case gdb_stmm7 : return fpu_stmm7; + case gdb_fcw : return fpu_fcw; + case gdb_fsw : return fpu_fsw; + case gdb_ftw : return fpu_ftw; + case gdb_fpu_cs : return fpu_cs; + case gdb_ip : return fpu_ip; + case gdb_fpu_ds : return fpu_fos; + case gdb_dp : return fpu_foo; + case gdb_fop : return fpu_fop; + case gdb_xmm0 : return fpu_xmm0; + case gdb_xmm1 : return fpu_xmm1; + case gdb_xmm2 : return fpu_xmm2; + case gdb_xmm3 : return fpu_xmm3; + case gdb_xmm4 : return fpu_xmm4; + case gdb_xmm5 : return fpu_xmm5; + case gdb_xmm6 : return fpu_xmm6; + case gdb_xmm7 : return fpu_xmm7; + case gdb_mxcsr : return fpu_mxcsr; + default: + return LLDB_INVALID_REGNUM; + } + } + else if (kind == eRegisterKindLLDB) + { + return num; + } + + return LLDB_INVALID_REGNUM; +} + +bool +RegisterContext_i386::HardwareSingleStep(bool enable) +{ + enum { TRACE_BIT = 0x100 }; + uint64_t eflags; + + if ((eflags = ReadRegisterAsUnsigned(gpr_eflags, -1UL)) == -1UL) + return false; + + if (enable) + { + if (eflags & TRACE_BIT) + return true; + + eflags |= TRACE_BIT; + } + else + { + if (!(eflags & TRACE_BIT)) + return false; + + eflags &= ~TRACE_BIT; + } + + return WriteRegisterFromUnsigned(gpr_eflags, eflags); +} + +void +RegisterContext_i386::LogGPR(const char *title) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS)); + if (log) + { + if (title) + log->Printf ("%s", title); + for (uint32_t i=0; i<k_num_gpr_registers; i++) + { + uint32_t reg = gpr_eax + i; + log->Printf("%12s = 0x%8.8" PRIx64, g_register_infos[reg].name, ((uint64_t*)&user.regs)[reg]); + } + } +} + +bool +RegisterContext_i386::ReadGPR() +{ + bool result; + + ProcessMonitor &monitor = GetMonitor(); + result = monitor.ReadGPR(m_thread.GetID(), &user.regs, sizeof(user.regs)); + LogGPR("RegisterContext_i386::ReadGPR()"); + return result; +} + +bool +RegisterContext_i386::ReadFPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadFPR(m_thread.GetID(), &user.i387, sizeof(user.i387)); +} diff --git a/source/Plugins/Process/POSIX/RegisterContext_i386.h b/source/Plugins/Process/POSIX/RegisterContext_i386.h new file mode 100644 index 000000000000..96066c47b815 --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContext_i386.h @@ -0,0 +1,169 @@ +//===-- RegisterContext_i386.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContext_i386_h_ +#define liblldb_RegisterContext_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "RegisterContextPOSIX.h" + +class RegisterContext_i386 : public RegisterContextPOSIX +{ +public: + RegisterContext_i386(lldb_private::Thread &thread, + uint32_t concreate_frame_idx); + + ~RegisterContext_i386(); + + void + Invalidate(); + + void + InvalidateAllRegisters(); + + size_t + GetRegisterCount(); + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex(size_t reg); + + size_t + GetRegisterSetCount(); + + const lldb_private::RegisterSet * + GetRegisterSet(size_t set); + + unsigned + GetRegisterIndexFromOffset(unsigned offset); + + const char * + GetRegisterName(unsigned reg); + + bool + ReadRegisterValue(uint32_t reg, lldb_private::Scalar &value); + + bool + ReadRegisterBytes(uint32_t reg, lldb_private::DataExtractor &data); + + virtual bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + bool + WriteRegisterValue(uint32_t reg, const lldb_private::Scalar &value); + + bool + WriteRegisterBytes(uint32_t reg, lldb_private::DataExtractor &data, + uint32_t data_offset = 0); + + virtual bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + uint32_t + ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num); + + bool + HardwareSingleStep(bool enable); + + bool + UpdateAfterBreakpoint(); + + struct GPR + { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + uint32_t orig_ax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; + }; + + struct MMSReg + { + uint8_t bytes[8]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint16_t fcw; + uint16_t fsw; + uint16_t ftw; + uint16_t fop; + uint32_t ip; + uint32_t cs; + uint32_t foo; + uint32_t fos; + uint32_t mxcsr; + uint32_t reserved; + MMSReg stmm[8]; + XMMReg xmm[8]; + uint32_t pad[56]; + }; + + // A user area like this no longer exists on FreeBSD + // making this a Linux artifact. Nonetheless, it is safe + // leaving it here while the code is being cleaned up and generalized. + + struct UserArea + { + GPR regs; // General purpose registers. + int32_t fpvalid; // True if FPU is being used. + FPU i387; // FPU registers. + uint32_t tsize; // Text segment size. + uint32_t dsize; // Data segment size. + uint32_t ssize; // Stack segment size. + uint32_t start_code; // VM address of text. + uint32_t start_stack; // VM address of stack bottom (top in rsp). + int32_t signal; // Signal causing core dump. + int32_t reserved; // Unused. + uint32_t ar0; // Location of GPR's. + FPU* fpstate; // Location of FPR's. + uint32_t magic; // Identifier for core dumps. + char u_comm[32]; // Command causing core dump. + uint32_t u_debugreg[8]; // Debug registers (DR0 - DR7). + }; +private: + UserArea user; + + ProcessMonitor &GetMonitor(); + + void LogGPR(const char *title); + + bool ReadGPR(); + bool ReadFPR(); +}; + +#endif // #ifndef liblldb_RegisterContext_i386_h_ diff --git a/source/Plugins/Process/POSIX/RegisterContext_x86.h b/source/Plugins/Process/POSIX/RegisterContext_x86.h new file mode 100644 index 000000000000..61a25c407758 --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContext_x86.h @@ -0,0 +1,110 @@ +//===-- RegisterContext_x86.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContext_x86_H_ +#define liblldb_RegisterContext_x86_H_ + +enum +{ + gcc_eax = 0, + gcc_ecx, + gcc_edx, + gcc_ebx, + gcc_ebp, + gcc_esp, + gcc_esi, + gcc_edi, + gcc_eip, + gcc_eflags +}; + +enum +{ + dwarf_eax = 0, + dwarf_ecx, + dwarf_edx, + dwarf_ebx, + dwarf_esp, + dwarf_ebp, + dwarf_esi, + dwarf_edi, + dwarf_eip, + dwarf_eflags, + dwarf_stmm0 = 11, + dwarf_stmm1, + dwarf_stmm2, + dwarf_stmm3, + dwarf_stmm4, + dwarf_stmm5, + dwarf_stmm6, + dwarf_stmm7, + dwarf_xmm0 = 21, + dwarf_xmm1, + dwarf_xmm2, + dwarf_xmm3, + dwarf_xmm4, + dwarf_xmm5, + dwarf_xmm6, + dwarf_xmm7 +}; + +enum +{ + gdb_eax = 0, + gdb_ecx = 1, + gdb_edx = 2, + gdb_ebx = 3, + gdb_esp = 4, + gdb_ebp = 5, + gdb_esi = 6, + gdb_edi = 7, + gdb_eip = 8, + gdb_eflags = 9, + gdb_cs = 10, + gdb_ss = 11, + gdb_ds = 12, + gdb_es = 13, + gdb_fs = 14, + gdb_gs = 15, + gdb_stmm0 = 16, + gdb_stmm1 = 17, + gdb_stmm2 = 18, + gdb_stmm3 = 19, + gdb_stmm4 = 20, + gdb_stmm5 = 21, + gdb_stmm6 = 22, + gdb_stmm7 = 23, + gdb_fcw = 24, + gdb_fsw = 25, + gdb_ftw = 26, + gdb_fpu_cs = 27, + gdb_ip = 28, + gdb_fpu_ds = 29, + gdb_dp = 30, + gdb_fop = 31, + gdb_xmm0 = 32, + gdb_xmm1 = 33, + gdb_xmm2 = 34, + gdb_xmm3 = 35, + gdb_xmm4 = 36, + gdb_xmm5 = 37, + gdb_xmm6 = 38, + gdb_xmm7 = 39, + gdb_mxcsr = 40, + gdb_mm0 = 41, + gdb_mm1 = 42, + gdb_mm2 = 43, + gdb_mm3 = 44, + gdb_mm4 = 45, + gdb_mm5 = 46, + gdb_mm6 = 47, + gdb_mm7 = 48 +}; + +#endif diff --git a/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp b/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp new file mode 100644 index 000000000000..617b18484e5a --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp @@ -0,0 +1,1563 @@ +//===-- RegisterContext_x86_64.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <cstring> +#include <errno.h> +#include <stdint.h> + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Host/Endian.h" +#include "llvm/Support/Compiler.h" + +#include "ProcessPOSIX.h" +#if defined(__linux__) or defined(__FreeBSD__) +#include "ProcessMonitor.h" +#endif +#include "RegisterContext_i386.h" +#include "RegisterContext_x86.h" +#include "RegisterContext_x86_64.h" +#include "Plugins/Process/elf-core/ProcessElfCore.h" + +using namespace lldb_private; +using namespace lldb; + +// Support ptrace extensions even when compiled without required kernel support +#ifndef NT_X86_XSTATE + #define NT_X86_XSTATE 0x202 +#endif + +enum +{ + gcc_dwarf_gpr_rax = 0, + gcc_dwarf_gpr_rdx, + gcc_dwarf_gpr_rcx, + gcc_dwarf_gpr_rbx, + gcc_dwarf_gpr_rsi, + gcc_dwarf_gpr_rdi, + gcc_dwarf_gpr_rbp, + gcc_dwarf_gpr_rsp, + gcc_dwarf_gpr_r8, + gcc_dwarf_gpr_r9, + gcc_dwarf_gpr_r10, + gcc_dwarf_gpr_r11, + gcc_dwarf_gpr_r12, + gcc_dwarf_gpr_r13, + gcc_dwarf_gpr_r14, + gcc_dwarf_gpr_r15, + gcc_dwarf_gpr_rip, + gcc_dwarf_fpu_xmm0, + gcc_dwarf_fpu_xmm1, + gcc_dwarf_fpu_xmm2, + gcc_dwarf_fpu_xmm3, + gcc_dwarf_fpu_xmm4, + gcc_dwarf_fpu_xmm5, + gcc_dwarf_fpu_xmm6, + gcc_dwarf_fpu_xmm7, + gcc_dwarf_fpu_xmm8, + gcc_dwarf_fpu_xmm9, + gcc_dwarf_fpu_xmm10, + gcc_dwarf_fpu_xmm11, + gcc_dwarf_fpu_xmm12, + gcc_dwarf_fpu_xmm13, + gcc_dwarf_fpu_xmm14, + gcc_dwarf_fpu_xmm15, + gcc_dwarf_fpu_stmm0, + gcc_dwarf_fpu_stmm1, + gcc_dwarf_fpu_stmm2, + gcc_dwarf_fpu_stmm3, + gcc_dwarf_fpu_stmm4, + gcc_dwarf_fpu_stmm5, + gcc_dwarf_fpu_stmm6, + gcc_dwarf_fpu_stmm7, + gcc_dwarf_fpu_ymm0, + gcc_dwarf_fpu_ymm1, + gcc_dwarf_fpu_ymm2, + gcc_dwarf_fpu_ymm3, + gcc_dwarf_fpu_ymm4, + gcc_dwarf_fpu_ymm5, + gcc_dwarf_fpu_ymm6, + gcc_dwarf_fpu_ymm7, + gcc_dwarf_fpu_ymm8, + gcc_dwarf_fpu_ymm9, + gcc_dwarf_fpu_ymm10, + gcc_dwarf_fpu_ymm11, + gcc_dwarf_fpu_ymm12, + gcc_dwarf_fpu_ymm13, + gcc_dwarf_fpu_ymm14, + gcc_dwarf_fpu_ymm15 +}; + +enum +{ + gdb_gpr_rax = 0, + gdb_gpr_rbx = 1, + gdb_gpr_rcx = 2, + gdb_gpr_rdx = 3, + gdb_gpr_rsi = 4, + gdb_gpr_rdi = 5, + gdb_gpr_rbp = 6, + gdb_gpr_rsp = 7, + gdb_gpr_r8 = 8, + gdb_gpr_r9 = 9, + gdb_gpr_r10 = 10, + gdb_gpr_r11 = 11, + gdb_gpr_r12 = 12, + gdb_gpr_r13 = 13, + gdb_gpr_r14 = 14, + gdb_gpr_r15 = 15, + gdb_gpr_rip = 16, + gdb_gpr_rflags = 17, + gdb_gpr_cs = 18, + gdb_gpr_ss = 19, + gdb_gpr_ds = 20, + gdb_gpr_es = 21, + gdb_gpr_fs = 22, + gdb_gpr_gs = 23, + gdb_fpu_stmm0 = 24, + gdb_fpu_stmm1 = 25, + gdb_fpu_stmm2 = 26, + gdb_fpu_stmm3 = 27, + gdb_fpu_stmm4 = 28, + gdb_fpu_stmm5 = 29, + gdb_fpu_stmm6 = 30, + gdb_fpu_stmm7 = 31, + gdb_fpu_fcw = 32, + gdb_fpu_fsw = 33, + gdb_fpu_ftw = 34, + gdb_fpu_cs_64 = 35, + gdb_fpu_ip = 36, + gdb_fpu_ds_64 = 37, + gdb_fpu_dp = 38, + gdb_fpu_fop = 39, + gdb_fpu_xmm0 = 40, + gdb_fpu_xmm1 = 41, + gdb_fpu_xmm2 = 42, + gdb_fpu_xmm3 = 43, + gdb_fpu_xmm4 = 44, + gdb_fpu_xmm5 = 45, + gdb_fpu_xmm6 = 46, + gdb_fpu_xmm7 = 47, + gdb_fpu_xmm8 = 48, + gdb_fpu_xmm9 = 49, + gdb_fpu_xmm10 = 50, + gdb_fpu_xmm11 = 51, + gdb_fpu_xmm12 = 52, + gdb_fpu_xmm13 = 53, + gdb_fpu_xmm14 = 54, + gdb_fpu_xmm15 = 55, + gdb_fpu_mxcsr = 56, + gdb_fpu_ymm0 = 57, + gdb_fpu_ymm1 = 58, + gdb_fpu_ymm2 = 59, + gdb_fpu_ymm3 = 60, + gdb_fpu_ymm4 = 61, + gdb_fpu_ymm5 = 62, + gdb_fpu_ymm6 = 63, + gdb_fpu_ymm7 = 64, + gdb_fpu_ymm8 = 65, + gdb_fpu_ymm9 = 66, + gdb_fpu_ymm10 = 67, + gdb_fpu_ymm11 = 68, + gdb_fpu_ymm12 = 69, + gdb_fpu_ymm13 = 70, + gdb_fpu_ymm14 = 71, + gdb_fpu_ymm15 = 72 +}; + +static const +uint32_t g_gpr_regnums[k_num_gpr_registers] = +{ + gpr_rax, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + gpr_ss, + gpr_ds, + gpr_es, + gpr_eax, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_eip, + gpr_eflags +}; + +static const uint32_t +g_fpu_regnums[k_num_fpr_registers] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15 +}; + +static const uint32_t +g_avx_regnums[k_num_avx_registers] = +{ + fpu_ymm0, + fpu_ymm1, + fpu_ymm2, + fpu_ymm3, + fpu_ymm4, + fpu_ymm5, + fpu_ymm6, + fpu_ymm7, + fpu_ymm8, + fpu_ymm9, + fpu_ymm10, + fpu_ymm11, + fpu_ymm12, + fpu_ymm13, + fpu_ymm14, + fpu_ymm15 +}; + +// Number of register sets provided by this context. +enum +{ + k_num_extended_register_sets = 1, + k_num_register_sets = 3 +}; + +static const RegisterSet +g_reg_sets[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums }, + { "Floating Point Registers", "fpu", k_num_fpr_registers, g_fpu_regnums }, + { "Advanced Vector Extensions", "avx", k_num_avx_registers, g_avx_regnums } +}; + +// Computes the offset of the given FPR in the extended data area. +#define FPR_OFFSET(regname) \ + (offsetof(RegisterContext_x86_64::FPR, xstate) + \ + offsetof(RegisterContext_x86_64::FXSAVE, regname)) + +// Computes the offset of the YMM register assembled from register halves. +#define YMM_OFFSET(regname) \ + (offsetof(RegisterContext_x86_64::YMM, regname)) + +// Number of bytes needed to represent a i386 GPR +#define GPR_i386_SIZE(reg) sizeof(((RegisterContext_i386::GPR*)NULL)->reg) + +// Number of bytes needed to represent a FPR. +#define FPR_SIZE(reg) sizeof(((RegisterContext_x86_64::FXSAVE*)NULL)->reg) + +// Number of bytes needed to represent the i'th FP register. +#define FP_SIZE sizeof(((RegisterContext_x86_64::MMSReg*)NULL)->bytes) + +// Number of bytes needed to represent an XMM register. +#define XMM_SIZE sizeof(RegisterContext_x86_64::XMMReg) + +// Number of bytes needed to represent a YMM register. +#define YMM_SIZE sizeof(RegisterContext_x86_64::YMMReg) + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, 0, 0, eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg }, NULL, NULL } + +// Dummy data for RegisterInfo::value_regs as expected by DumpRegisterSet. +static uint32_t value_regs = LLDB_INVALID_REGNUM; + +#define DEFINE_GPR_i386(reg_i386, reg_x86_64, alt, kind1, kind2, kind3, kind4) \ + { #reg_i386, alt, GPR_i386_SIZE(reg_i386), 0, eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg_i386 }, &value_regs, NULL } + +#define DEFINE_FPR(reg, kind1, kind2, kind3, kind4) \ + { #reg, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4, fpu_##reg }, NULL, NULL } + +#define DEFINE_FP(reg, i) \ + { #reg#i, NULL, FP_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL } + +#define DEFINE_XMM(reg, i) \ + { #reg#i, NULL, XMM_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL } + +#define DEFINE_YMM(reg, i) \ + { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(reg[i]), \ + eEncodingVector, eFormatVectorOfUInt8, \ + { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, \ + LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL } + +#define DEFINE_DR(reg, i) \ + { #reg#i, NULL, 0, 0, eEncodingUint, eFormatHex, \ + { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL } + +#define REG_CONTEXT_SIZE (GetGPRSize() + sizeof(RegisterContext_x86_64::FPR)) + +static RegisterInfo +g_register_infos[k_num_registers] = +{ + // General purpose registers. + DEFINE_GPR(rax, NULL, gcc_dwarf_gpr_rax, gcc_dwarf_gpr_rax, LLDB_INVALID_REGNUM, gdb_gpr_rax), + DEFINE_GPR(rbx, NULL, gcc_dwarf_gpr_rbx, gcc_dwarf_gpr_rbx, LLDB_INVALID_REGNUM, gdb_gpr_rbx), + DEFINE_GPR(rcx, NULL, gcc_dwarf_gpr_rcx, gcc_dwarf_gpr_rcx, LLDB_INVALID_REGNUM, gdb_gpr_rcx), + DEFINE_GPR(rdx, NULL, gcc_dwarf_gpr_rdx, gcc_dwarf_gpr_rdx, LLDB_INVALID_REGNUM, gdb_gpr_rdx), + DEFINE_GPR(rdi, NULL, gcc_dwarf_gpr_rdi, gcc_dwarf_gpr_rdi, LLDB_INVALID_REGNUM, gdb_gpr_rdi), + DEFINE_GPR(rsi, NULL, gcc_dwarf_gpr_rsi, gcc_dwarf_gpr_rsi, LLDB_INVALID_REGNUM, gdb_gpr_rsi), + DEFINE_GPR(rbp, "fp", gcc_dwarf_gpr_rbp, gcc_dwarf_gpr_rbp, LLDB_REGNUM_GENERIC_FP, gdb_gpr_rbp), + DEFINE_GPR(rsp, "sp", gcc_dwarf_gpr_rsp, gcc_dwarf_gpr_rsp, LLDB_REGNUM_GENERIC_SP, gdb_gpr_rsp), + DEFINE_GPR(r8, NULL, gcc_dwarf_gpr_r8, gcc_dwarf_gpr_r8, LLDB_INVALID_REGNUM, gdb_gpr_r8), + DEFINE_GPR(r9, NULL, gcc_dwarf_gpr_r9, gcc_dwarf_gpr_r9, LLDB_INVALID_REGNUM, gdb_gpr_r9), + DEFINE_GPR(r10, NULL, gcc_dwarf_gpr_r10, gcc_dwarf_gpr_r10, LLDB_INVALID_REGNUM, gdb_gpr_r10), + DEFINE_GPR(r11, NULL, gcc_dwarf_gpr_r11, gcc_dwarf_gpr_r11, LLDB_INVALID_REGNUM, gdb_gpr_r11), + DEFINE_GPR(r12, NULL, gcc_dwarf_gpr_r12, gcc_dwarf_gpr_r12, LLDB_INVALID_REGNUM, gdb_gpr_r12), + DEFINE_GPR(r13, NULL, gcc_dwarf_gpr_r13, gcc_dwarf_gpr_r13, LLDB_INVALID_REGNUM, gdb_gpr_r13), + DEFINE_GPR(r14, NULL, gcc_dwarf_gpr_r14, gcc_dwarf_gpr_r14, LLDB_INVALID_REGNUM, gdb_gpr_r14), + DEFINE_GPR(r15, NULL, gcc_dwarf_gpr_r15, gcc_dwarf_gpr_r15, LLDB_INVALID_REGNUM, gdb_gpr_r15), + DEFINE_GPR(rip, "pc", gcc_dwarf_gpr_rip, gcc_dwarf_gpr_rip, LLDB_REGNUM_GENERIC_PC, gdb_gpr_rip), + DEFINE_GPR(rflags, "flags", LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, gdb_gpr_rflags), + DEFINE_GPR(cs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_cs), + DEFINE_GPR(fs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_fs), + DEFINE_GPR(gs, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_gs), + DEFINE_GPR(ss, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_ss), + DEFINE_GPR(ds, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_ds), + DEFINE_GPR(es, NULL, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_gpr_es), + // i386 registers + DEFINE_GPR_i386(eax, rax, NULL, gcc_eax, dwarf_eax, LLDB_INVALID_REGNUM, gdb_eax), + DEFINE_GPR_i386(ebx, rbx, NULL, gcc_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, gdb_ebx), + DEFINE_GPR_i386(ecx, rcx, NULL, gcc_ecx, dwarf_ecx, LLDB_INVALID_REGNUM, gdb_ecx), + DEFINE_GPR_i386(edx, rdx, NULL, gcc_edx, dwarf_edx, LLDB_INVALID_REGNUM, gdb_edx), + DEFINE_GPR_i386(edi, rdi, NULL, gcc_edi, dwarf_edi, LLDB_INVALID_REGNUM, gdb_edi), + DEFINE_GPR_i386(esi, rsi, NULL, gcc_esi, dwarf_esi, LLDB_INVALID_REGNUM, gdb_esi), + DEFINE_GPR_i386(ebp, rbp, "fp", gcc_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, gdb_ebp), + DEFINE_GPR_i386(esp, rsp, "sp", gcc_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, gdb_esp), + DEFINE_GPR_i386(eip, rip, "pc", gcc_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, gdb_eip), + DEFINE_GPR_i386(eflags, rflags, "flags", gcc_eflags, dwarf_eflags, LLDB_REGNUM_GENERIC_FLAGS, gdb_eflags), + // i387 Floating point registers. + DEFINE_FPR(fcw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fcw), + DEFINE_FPR(fsw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fsw), + DEFINE_FPR(ftw, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ftw), + DEFINE_FPR(fop, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_fop), + DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ip), + // FIXME: Extract segment from ip. + DEFINE_FPR(ip, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_cs_64), + DEFINE_FPR(dp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_dp), + // FIXME: Extract segment from dp. + DEFINE_FPR(dp, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_ds_64), + DEFINE_FPR(mxcsr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gdb_fpu_mxcsr), + DEFINE_FPR(mxcsrmask, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + + // FP registers. + DEFINE_FP(stmm, 0), + DEFINE_FP(stmm, 1), + DEFINE_FP(stmm, 2), + DEFINE_FP(stmm, 3), + DEFINE_FP(stmm, 4), + DEFINE_FP(stmm, 5), + DEFINE_FP(stmm, 6), + DEFINE_FP(stmm, 7), + + // XMM registers + DEFINE_XMM(xmm, 0), + DEFINE_XMM(xmm, 1), + DEFINE_XMM(xmm, 2), + DEFINE_XMM(xmm, 3), + DEFINE_XMM(xmm, 4), + DEFINE_XMM(xmm, 5), + DEFINE_XMM(xmm, 6), + DEFINE_XMM(xmm, 7), + DEFINE_XMM(xmm, 8), + DEFINE_XMM(xmm, 9), + DEFINE_XMM(xmm, 10), + DEFINE_XMM(xmm, 11), + DEFINE_XMM(xmm, 12), + DEFINE_XMM(xmm, 13), + DEFINE_XMM(xmm, 14), + DEFINE_XMM(xmm, 15), + + // Copy of YMM registers assembled from xmm and ymmh + DEFINE_YMM(ymm, 0), + DEFINE_YMM(ymm, 1), + DEFINE_YMM(ymm, 2), + DEFINE_YMM(ymm, 3), + DEFINE_YMM(ymm, 4), + DEFINE_YMM(ymm, 5), + DEFINE_YMM(ymm, 6), + DEFINE_YMM(ymm, 7), + DEFINE_YMM(ymm, 8), + DEFINE_YMM(ymm, 9), + DEFINE_YMM(ymm, 10), + DEFINE_YMM(ymm, 11), + DEFINE_YMM(ymm, 12), + DEFINE_YMM(ymm, 13), + DEFINE_YMM(ymm, 14), + DEFINE_YMM(ymm, 15), + + // Debug registers for lldb internal use + DEFINE_DR(dr, 0), + DEFINE_DR(dr, 1), + DEFINE_DR(dr, 2), + DEFINE_DR(dr, 3), + DEFINE_DR(dr, 4), + DEFINE_DR(dr, 5), + DEFINE_DR(dr, 6), + DEFINE_DR(dr, 7) +}; + +static bool IsGPR(unsigned reg) +{ + return reg <= k_last_gpr; // GPR's come first. +} + +static bool IsAVX(unsigned reg) +{ + return (k_first_avx <= reg && reg <= k_last_avx); +} +static bool IsFPR(unsigned reg) +{ + return (k_first_fpr <= reg && reg <= k_last_fpr); +} + + +bool RegisterContext_x86_64::IsFPR(unsigned reg, FPRType fpr_type) +{ + bool generic_fpr = ::IsFPR(reg); + if (fpr_type == eXSAVE) + return generic_fpr || IsAVX(reg); + + return generic_fpr; +} + +RegisterContext_x86_64::RegisterContext_x86_64(Thread &thread, + uint32_t concrete_frame_idx) + : RegisterContextPOSIX(thread, concrete_frame_idx) +{ + // Initialize m_iovec to point to the buffer and buffer size + // using the conventions of Berkeley style UIO structures, as required + // by PTRACE extensions. + m_iovec.iov_base = &m_fpr.xstate.xsave; + m_iovec.iov_len = sizeof(m_fpr.xstate.xsave); + + ::memset(&m_fpr, 0, sizeof(RegisterContext_x86_64::FPR)); + + // elf-core yet to support ReadFPR() + ProcessSP base = CalculateProcess(); + if (base.get()->GetPluginName() == ProcessElfCore::GetPluginNameStatic()) + return; + + // TODO: Use assembly to call cpuid on the inferior and query ebx or ecx + m_fpr_type = eXSAVE; // extended floating-point registers, if available + if (false == ReadFPR()) + m_fpr_type = eFXSAVE; // assume generic floating-point registers +} + +RegisterContext_x86_64::~RegisterContext_x86_64() +{ +} + +void +RegisterContext_x86_64::Invalidate() +{ +} + +void +RegisterContext_x86_64::InvalidateAllRegisters() +{ +} + +unsigned +RegisterContext_x86_64::GetRegisterOffset(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +unsigned +RegisterContext_x86_64::GetRegisterSize(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +size_t +RegisterContext_x86_64::GetRegisterCount() +{ + size_t num_registers = k_num_gpr_registers + k_num_fpr_registers; + if (m_fpr_type == eXSAVE) + return num_registers + k_num_avx_registers; + return num_registers; +} + +const RegisterInfo * +RegisterContext_x86_64::GetRegisterInfo() +{ + // Commonly, this method is overridden and g_register_infos is copied and specialized. + // So, use GetRegisterInfo() rather than g_register_infos in this scope. + return g_register_infos; +} + +const RegisterInfo * +RegisterContext_x86_64::GetRegisterInfoAtIndex(size_t reg) +{ + if (reg < k_num_registers) + return &GetRegisterInfo()[reg]; + else + return NULL; +} + +size_t +RegisterContext_x86_64::GetRegisterSetCount() +{ + size_t sets = 0; + for (size_t set = 0; set < k_num_register_sets; ++set) + if (IsRegisterSetAvailable(set)) + ++sets; + + return sets; +} + +const RegisterSet * +RegisterContext_x86_64::GetRegisterSet(size_t set) +{ + if (IsRegisterSetAvailable(set)) + return &g_reg_sets[set]; + else + return NULL; +} + +unsigned +RegisterContext_x86_64::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + for (reg = 0; reg < k_num_registers; reg++) + { + if (GetRegisterInfo()[reg].byte_offset == offset) + break; + } + assert(reg < k_num_registers && "Invalid register offset."); + return reg; +} + +const char * +RegisterContext_x86_64::GetRegisterName(unsigned reg) +{ + assert(reg < k_num_registers && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +lldb::ByteOrder +RegisterContext_x86_64::GetByteOrder() +{ + // Get the target process whose privileged thread was used for the register read. + lldb::ByteOrder byte_order = eByteOrderInvalid; + Process *process = CalculateProcess().get(); + + if (process) + byte_order = process->GetByteOrder(); + return byte_order; +} + +// Parse ymm registers and into xmm.bytes and ymmh.bytes. +bool RegisterContext_x86_64::CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order) +{ + if (!IsAVX(reg)) + return false; + + if (byte_order == eByteOrderLittle) { + ::memcpy(m_fpr.xstate.fxsave.xmm[reg - fpu_ymm0].bytes, + m_ymm_set.ymm[reg - fpu_ymm0].bytes, + sizeof(RegisterContext_x86_64::XMMReg)); + ::memcpy(m_fpr.xstate.xsave.ymmh[reg - fpu_ymm0].bytes, + m_ymm_set.ymm[reg - fpu_ymm0].bytes + sizeof(RegisterContext_x86_64::XMMReg), + sizeof(RegisterContext_x86_64::YMMHReg)); + return true; + } + + if (byte_order == eByteOrderBig) { + ::memcpy(m_fpr.xstate.fxsave.xmm[reg - fpu_ymm0].bytes, + m_ymm_set.ymm[reg - fpu_ymm0].bytes + sizeof(RegisterContext_x86_64::XMMReg), + sizeof(RegisterContext_x86_64::XMMReg)); + ::memcpy(m_fpr.xstate.xsave.ymmh[reg - fpu_ymm0].bytes, + m_ymm_set.ymm[reg - fpu_ymm0].bytes, + sizeof(RegisterContext_x86_64::YMMHReg)); + return true; + } + return false; // unsupported or invalid byte order +} + +// Concatenate xmm.bytes with ymmh.bytes +bool RegisterContext_x86_64::CopyXSTATEtoYMM(uint32_t reg, lldb::ByteOrder byte_order) +{ + if (!IsAVX(reg)) + return false; + + if (byte_order == eByteOrderLittle) { + ::memcpy(m_ymm_set.ymm[reg - fpu_ymm0].bytes, + m_fpr.xstate.fxsave.xmm[reg - fpu_ymm0].bytes, + sizeof(RegisterContext_x86_64::XMMReg)); + ::memcpy(m_ymm_set.ymm[reg - fpu_ymm0].bytes + sizeof(RegisterContext_x86_64::XMMReg), + m_fpr.xstate.xsave.ymmh[reg - fpu_ymm0].bytes, + sizeof(RegisterContext_x86_64::YMMHReg)); + return true; + } + if (byte_order == eByteOrderBig) { + ::memcpy(m_ymm_set.ymm[reg - fpu_ymm0].bytes + sizeof(RegisterContext_x86_64::XMMReg), + m_fpr.xstate.fxsave.xmm[reg - fpu_ymm0].bytes, + sizeof(RegisterContext_x86_64::XMMReg)); + ::memcpy(m_ymm_set.ymm[reg - fpu_ymm0].bytes, + m_fpr.xstate.xsave.ymmh[reg - fpu_ymm0].bytes, + sizeof(RegisterContext_x86_64::YMMHReg)); + return true; + } + return false; // unsupported or invalid byte order +} + +bool +RegisterContext_x86_64::IsRegisterSetAvailable(size_t set_index) +{ + // Note: Extended register sets are assumed to be at the end of g_reg_sets... + size_t num_sets = k_num_register_sets - k_num_extended_register_sets; + if (m_fpr_type == eXSAVE) // ...and to start with AVX registers. + ++num_sets; + + return (set_index < num_sets); +} + +bool +RegisterContext_x86_64::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + if (!reg_info) + return false; + + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + if (IsFPR(reg, m_fpr_type)) { + if (!ReadFPR()) + return false; + } + else { + bool success = ReadRegister(reg, value); + + // If an i386 register should be parsed from an x86_64 register... + if (success && reg >= k_first_i386 && reg <= k_last_i386) + if (value.GetByteSize() > reg_info->byte_size) + value.SetType(reg_info); // ...use the type specified by reg_info rather than the uint64_t default + return success; + } + + if (reg_info->encoding == eEncodingVector) { + ByteOrder byte_order = GetByteOrder(); + + if (byte_order != ByteOrder::eByteOrderInvalid) { + if (reg >= fpu_stmm0 && reg <= fpu_stmm7) { + value.SetBytes(m_fpr.xstate.fxsave.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, byte_order); + } + if (reg >= fpu_xmm0 && reg <= fpu_xmm15) { + value.SetBytes(m_fpr.xstate.fxsave.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, byte_order); + } + if (reg >= fpu_ymm0 && reg <= fpu_ymm15) { + // Concatenate ymm using the register halves in xmm.bytes and ymmh.bytes + if (m_fpr_type == eXSAVE && CopyXSTATEtoYMM(reg, byte_order)) + value.SetBytes(m_ymm_set.ymm[reg - fpu_ymm0].bytes, reg_info->byte_size, byte_order); + else + return false; + } + return value.GetType() == RegisterValue::eTypeBytes; + } + return false; + } + + // Note that lldb uses slightly different naming conventions from sys/user.h + switch (reg) + { + default: + return false; + case fpu_dp: + value = m_fpr.xstate.fxsave.dp; + break; + case fpu_fcw: + value = m_fpr.xstate.fxsave.fcw; + break; + case fpu_fsw: + value = m_fpr.xstate.fxsave.fsw; + break; + case fpu_ip: + value = m_fpr.xstate.fxsave.ip; + break; + case fpu_fop: + value = m_fpr.xstate.fxsave.fop; + break; + case fpu_ftw: + value = m_fpr.xstate.fxsave.ftw; + break; + case fpu_mxcsr: + value = m_fpr.xstate.fxsave.mxcsr; + break; + case fpu_mxcsrmask: + value = m_fpr.xstate.fxsave.mxcsrmask; + break; + } + return true; +} + +bool +RegisterContext_x86_64::ReadAllRegisterValues(DataBufferSP &data_sp) +{ + bool success = false; + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && ReadGPR () && ReadFPR ()) + { + uint8_t *dst = data_sp->GetBytes(); + success = dst != 0; + + if (success) { + ::memcpy (dst, &m_gpr, GetGPRSize()); + dst += GetGPRSize(); + } + if (m_fpr_type == eFXSAVE) + ::memcpy (dst, &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave)); + + if (m_fpr_type == eXSAVE) { + ByteOrder byte_order = GetByteOrder(); + + // Assemble the YMM register content from the register halves. + for (uint32_t reg = fpu_ymm0; success && reg <= fpu_ymm15; ++reg) + success = CopyXSTATEtoYMM(reg, byte_order); + + if (success) { + // Copy the extended register state including the assembled ymm registers. + ::memcpy (dst, &m_fpr, sizeof(m_fpr)); + } + } + } + return success; +} + +bool +RegisterContext_x86_64::WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + if (IsGPR(reg)) { + return WriteRegister(reg, value); + } + + if (IsFPR(reg, m_fpr_type)) { + switch (reg) + { + default: + if (reg_info->encoding != eEncodingVector) + return false; + + if (reg >= fpu_stmm0 && reg <= fpu_stmm7) + ::memcpy (m_fpr.xstate.fxsave.stmm[reg - fpu_stmm0].bytes, value.GetBytes(), value.GetByteSize()); + + if (reg >= fpu_xmm0 && reg <= fpu_xmm15) + ::memcpy (m_fpr.xstate.fxsave.xmm[reg - fpu_xmm0].bytes, value.GetBytes(), value.GetByteSize()); + + if (reg >= fpu_ymm0 && reg <= fpu_ymm15) { + if (m_fpr_type != eXSAVE) + return false; // the target processor does not support AVX + + // Store ymm register content, and split into the register halves in xmm.bytes and ymmh.bytes + ::memcpy (m_ymm_set.ymm[reg - fpu_ymm0].bytes, value.GetBytes(), value.GetByteSize()); + if (false == CopyYMMtoXSTATE(reg, GetByteOrder())) + return false; + } + break; + case fpu_dp: + m_fpr.xstate.fxsave.dp = value.GetAsUInt64(); + break; + case fpu_fcw: + m_fpr.xstate.fxsave.fcw = value.GetAsUInt16(); + break; + case fpu_fsw: + m_fpr.xstate.fxsave.fsw = value.GetAsUInt16(); + break; + case fpu_ip: + m_fpr.xstate.fxsave.ip = value.GetAsUInt64(); + break; + case fpu_fop: + m_fpr.xstate.fxsave.fop = value.GetAsUInt16(); + break; + case fpu_ftw: + m_fpr.xstate.fxsave.ftw = value.GetAsUInt16(); + break; + case fpu_mxcsr: + m_fpr.xstate.fxsave.mxcsr = value.GetAsUInt32(); + break; + case fpu_mxcsrmask: + m_fpr.xstate.fxsave.mxcsrmask = value.GetAsUInt32(); + break; + } + if (WriteFPR()) { + if (IsAVX(reg)) + return CopyYMMtoXSTATE(reg, GetByteOrder()); + return true; + } + } + return false; +} + +bool +RegisterContext_x86_64::WriteAllRegisterValues(const DataBufferSP &data_sp) +{ + bool success = false; + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + uint8_t *src = data_sp->GetBytes(); + if (src) { + ::memcpy (&m_gpr, src, GetGPRSize()); + + if (WriteGPR()) { + src += GetGPRSize(); + if (m_fpr_type == eFXSAVE) + ::memcpy (&m_fpr.xstate.fxsave, src, sizeof(m_fpr.xstate.fxsave)); + if (m_fpr_type == eXSAVE) + ::memcpy (&m_fpr.xstate.xsave, src, sizeof(m_fpr.xstate.xsave)); + + success = WriteFPR(); + if (success) { + success = true; + + if (m_fpr_type == eXSAVE) { + ByteOrder byte_order = GetByteOrder(); + + // Parse the YMM register content from the register halves. + for (uint32_t reg = fpu_ymm0; success && reg <= fpu_ymm15; ++reg) + success = CopyYMMtoXSTATE(reg, byte_order); + } + } + } + } + } + return success; +} + +bool +RegisterContext_x86_64::UpdateAfterBreakpoint() +{ + // PC points one byte past the int3 responsible for the breakpoint. + lldb::addr_t pc; + + if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + return false; + + SetPC(pc - 1); + return true; +} + +uint32_t +RegisterContext_x86_64::ConvertRegisterKindToRegisterNumber(uint32_t kind, + uint32_t num) +{ + const Process *process = CalculateProcess().get(); + if (process) + { + const ArchSpec arch = process->GetTarget().GetArchitecture();; + switch (arch.GetCore()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + { + if (kind == eRegisterKindGeneric) + { + switch (num) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_eip; + case LLDB_REGNUM_GENERIC_SP: return gpr_esp; + case LLDB_REGNUM_GENERIC_FP: return gpr_ebp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags; + case LLDB_REGNUM_GENERIC_RA: + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (num) + { + case dwarf_eax: return gpr_eax; + case dwarf_edx: return gpr_edx; + case dwarf_ecx: return gpr_ecx; + case dwarf_ebx: return gpr_ebx; + case dwarf_esi: return gpr_esi; + case dwarf_edi: return gpr_edi; + case dwarf_ebp: return gpr_ebp; + case dwarf_esp: return gpr_esp; + case dwarf_eip: return gpr_eip; + case dwarf_xmm0: return fpu_xmm0; + case dwarf_xmm1: return fpu_xmm1; + case dwarf_xmm2: return fpu_xmm2; + case dwarf_xmm3: return fpu_xmm3; + case dwarf_xmm4: return fpu_xmm4; + case dwarf_xmm5: return fpu_xmm5; + case dwarf_xmm6: return fpu_xmm6; + case dwarf_xmm7: return fpu_xmm7; + case dwarf_stmm0: return fpu_stmm0; + case dwarf_stmm1: return fpu_stmm1; + case dwarf_stmm2: return fpu_stmm2; + case dwarf_stmm3: return fpu_stmm3; + case dwarf_stmm4: return fpu_stmm4; + case dwarf_stmm5: return fpu_stmm5; + case dwarf_stmm6: return fpu_stmm6; + case dwarf_stmm7: return fpu_stmm7; + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGDB) + { + switch (num) + { + case gdb_eax : return gpr_eax; + case gdb_ebx : return gpr_ebx; + case gdb_ecx : return gpr_ecx; + case gdb_edx : return gpr_edx; + case gdb_esi : return gpr_esi; + case gdb_edi : return gpr_edi; + case gdb_ebp : return gpr_ebp; + case gdb_esp : return gpr_esp; + case gdb_eip : return gpr_eip; + case gdb_eflags : return gpr_eflags; + case gdb_cs : return gpr_cs; + case gdb_ss : return gpr_ss; + case gdb_ds : return gpr_ds; + case gdb_es : return gpr_es; + case gdb_fs : return gpr_fs; + case gdb_gs : return gpr_gs; + case gdb_stmm0 : return fpu_stmm0; + case gdb_stmm1 : return fpu_stmm1; + case gdb_stmm2 : return fpu_stmm2; + case gdb_stmm3 : return fpu_stmm3; + case gdb_stmm4 : return fpu_stmm4; + case gdb_stmm5 : return fpu_stmm5; + case gdb_stmm6 : return fpu_stmm6; + case gdb_stmm7 : return fpu_stmm7; + case gdb_fcw : return fpu_fcw; + case gdb_fsw : return fpu_fsw; + case gdb_ftw : return fpu_ftw; + case gdb_fpu_cs : return fpu_cs; + case gdb_ip : return fpu_ip; + case gdb_fpu_ds : return fpu_ds; //fpu_fos + case gdb_dp : return fpu_dp; //fpu_foo + case gdb_fop : return fpu_fop; + case gdb_xmm0 : return fpu_xmm0; + case gdb_xmm1 : return fpu_xmm1; + case gdb_xmm2 : return fpu_xmm2; + case gdb_xmm3 : return fpu_xmm3; + case gdb_xmm4 : return fpu_xmm4; + case gdb_xmm5 : return fpu_xmm5; + case gdb_xmm6 : return fpu_xmm6; + case gdb_xmm7 : return fpu_xmm7; + case gdb_mxcsr : return fpu_mxcsr; + default: + return LLDB_INVALID_REGNUM; + } + } + else if (kind == eRegisterKindLLDB) + { + return num; + } + + break; + } + + case ArchSpec::eCore_x86_64_x86_64: + { + if (kind == eRegisterKindGeneric) + { + switch (num) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_rip; + case LLDB_REGNUM_GENERIC_SP: return gpr_rsp; + case LLDB_REGNUM_GENERIC_FP: return gpr_rbp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags; + case LLDB_REGNUM_GENERIC_RA: + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (num) + { + case gcc_dwarf_gpr_rax: return gpr_rax; + case gcc_dwarf_gpr_rdx: return gpr_rdx; + case gcc_dwarf_gpr_rcx: return gpr_rcx; + case gcc_dwarf_gpr_rbx: return gpr_rbx; + case gcc_dwarf_gpr_rsi: return gpr_rsi; + case gcc_dwarf_gpr_rdi: return gpr_rdi; + case gcc_dwarf_gpr_rbp: return gpr_rbp; + case gcc_dwarf_gpr_rsp: return gpr_rsp; + case gcc_dwarf_gpr_r8: return gpr_r8; + case gcc_dwarf_gpr_r9: return gpr_r9; + case gcc_dwarf_gpr_r10: return gpr_r10; + case gcc_dwarf_gpr_r11: return gpr_r11; + case gcc_dwarf_gpr_r12: return gpr_r12; + case gcc_dwarf_gpr_r13: return gpr_r13; + case gcc_dwarf_gpr_r14: return gpr_r14; + case gcc_dwarf_gpr_r15: return gpr_r15; + case gcc_dwarf_gpr_rip: return gpr_rip; + case gcc_dwarf_fpu_xmm0: return fpu_xmm0; + case gcc_dwarf_fpu_xmm1: return fpu_xmm1; + case gcc_dwarf_fpu_xmm2: return fpu_xmm2; + case gcc_dwarf_fpu_xmm3: return fpu_xmm3; + case gcc_dwarf_fpu_xmm4: return fpu_xmm4; + case gcc_dwarf_fpu_xmm5: return fpu_xmm5; + case gcc_dwarf_fpu_xmm6: return fpu_xmm6; + case gcc_dwarf_fpu_xmm7: return fpu_xmm7; + case gcc_dwarf_fpu_xmm8: return fpu_xmm8; + case gcc_dwarf_fpu_xmm9: return fpu_xmm9; + case gcc_dwarf_fpu_xmm10: return fpu_xmm10; + case gcc_dwarf_fpu_xmm11: return fpu_xmm11; + case gcc_dwarf_fpu_xmm12: return fpu_xmm12; + case gcc_dwarf_fpu_xmm13: return fpu_xmm13; + case gcc_dwarf_fpu_xmm14: return fpu_xmm14; + case gcc_dwarf_fpu_xmm15: return fpu_xmm15; + case gcc_dwarf_fpu_stmm0: return fpu_stmm0; + case gcc_dwarf_fpu_stmm1: return fpu_stmm1; + case gcc_dwarf_fpu_stmm2: return fpu_stmm2; + case gcc_dwarf_fpu_stmm3: return fpu_stmm3; + case gcc_dwarf_fpu_stmm4: return fpu_stmm4; + case gcc_dwarf_fpu_stmm5: return fpu_stmm5; + case gcc_dwarf_fpu_stmm6: return fpu_stmm6; + case gcc_dwarf_fpu_stmm7: return fpu_stmm7; + case gcc_dwarf_fpu_ymm0: return fpu_ymm0; + case gcc_dwarf_fpu_ymm1: return fpu_ymm1; + case gcc_dwarf_fpu_ymm2: return fpu_ymm2; + case gcc_dwarf_fpu_ymm3: return fpu_ymm3; + case gcc_dwarf_fpu_ymm4: return fpu_ymm4; + case gcc_dwarf_fpu_ymm5: return fpu_ymm5; + case gcc_dwarf_fpu_ymm6: return fpu_ymm6; + case gcc_dwarf_fpu_ymm7: return fpu_ymm7; + case gcc_dwarf_fpu_ymm8: return fpu_ymm8; + case gcc_dwarf_fpu_ymm9: return fpu_ymm9; + case gcc_dwarf_fpu_ymm10: return fpu_ymm10; + case gcc_dwarf_fpu_ymm11: return fpu_ymm11; + case gcc_dwarf_fpu_ymm12: return fpu_ymm12; + case gcc_dwarf_fpu_ymm13: return fpu_ymm13; + case gcc_dwarf_fpu_ymm14: return fpu_ymm14; + case gcc_dwarf_fpu_ymm15: return fpu_ymm15; + default: + return LLDB_INVALID_REGNUM; + } + } + + if (kind == eRegisterKindGDB) + { + switch (num) + { + case gdb_gpr_rax : return gpr_rax; + case gdb_gpr_rbx : return gpr_rbx; + case gdb_gpr_rcx : return gpr_rcx; + case gdb_gpr_rdx : return gpr_rdx; + case gdb_gpr_rsi : return gpr_rsi; + case gdb_gpr_rdi : return gpr_rdi; + case gdb_gpr_rbp : return gpr_rbp; + case gdb_gpr_rsp : return gpr_rsp; + case gdb_gpr_r8 : return gpr_r8; + case gdb_gpr_r9 : return gpr_r9; + case gdb_gpr_r10 : return gpr_r10; + case gdb_gpr_r11 : return gpr_r11; + case gdb_gpr_r12 : return gpr_r12; + case gdb_gpr_r13 : return gpr_r13; + case gdb_gpr_r14 : return gpr_r14; + case gdb_gpr_r15 : return gpr_r15; + case gdb_gpr_rip : return gpr_rip; + case gdb_gpr_rflags : return gpr_rflags; + case gdb_gpr_cs : return gpr_cs; + case gdb_gpr_ss : return gpr_ss; + case gdb_gpr_ds : return gpr_ds; + case gdb_gpr_es : return gpr_es; + case gdb_gpr_fs : return gpr_fs; + case gdb_gpr_gs : return gpr_gs; + case gdb_fpu_stmm0 : return fpu_stmm0; + case gdb_fpu_stmm1 : return fpu_stmm1; + case gdb_fpu_stmm2 : return fpu_stmm2; + case gdb_fpu_stmm3 : return fpu_stmm3; + case gdb_fpu_stmm4 : return fpu_stmm4; + case gdb_fpu_stmm5 : return fpu_stmm5; + case gdb_fpu_stmm6 : return fpu_stmm6; + case gdb_fpu_stmm7 : return fpu_stmm7; + case gdb_fpu_fcw : return fpu_fcw; + case gdb_fpu_fsw : return fpu_fsw; + case gdb_fpu_ftw : return fpu_ftw; + case gdb_fpu_cs_64 : return fpu_cs; + case gdb_fpu_ip : return fpu_ip; + case gdb_fpu_ds_64 : return fpu_ds; + case gdb_fpu_dp : return fpu_dp; + case gdb_fpu_fop : return fpu_fop; + case gdb_fpu_xmm0 : return fpu_xmm0; + case gdb_fpu_xmm1 : return fpu_xmm1; + case gdb_fpu_xmm2 : return fpu_xmm2; + case gdb_fpu_xmm3 : return fpu_xmm3; + case gdb_fpu_xmm4 : return fpu_xmm4; + case gdb_fpu_xmm5 : return fpu_xmm5; + case gdb_fpu_xmm6 : return fpu_xmm6; + case gdb_fpu_xmm7 : return fpu_xmm7; + case gdb_fpu_xmm8 : return fpu_xmm8; + case gdb_fpu_xmm9 : return fpu_xmm9; + case gdb_fpu_xmm10 : return fpu_xmm10; + case gdb_fpu_xmm11 : return fpu_xmm11; + case gdb_fpu_xmm12 : return fpu_xmm12; + case gdb_fpu_xmm13 : return fpu_xmm13; + case gdb_fpu_xmm14 : return fpu_xmm14; + case gdb_fpu_xmm15 : return fpu_xmm15; + case gdb_fpu_mxcsr : return fpu_mxcsr; + case gdb_fpu_ymm0 : return fpu_ymm0; + case gdb_fpu_ymm1 : return fpu_ymm1; + case gdb_fpu_ymm2 : return fpu_ymm2; + case gdb_fpu_ymm3 : return fpu_ymm3; + case gdb_fpu_ymm4 : return fpu_ymm4; + case gdb_fpu_ymm5 : return fpu_ymm5; + case gdb_fpu_ymm6 : return fpu_ymm6; + case gdb_fpu_ymm7 : return fpu_ymm7; + case gdb_fpu_ymm8 : return fpu_ymm8; + case gdb_fpu_ymm9 : return fpu_ymm9; + case gdb_fpu_ymm10 : return fpu_ymm10; + case gdb_fpu_ymm11 : return fpu_ymm11; + case gdb_fpu_ymm12 : return fpu_ymm12; + case gdb_fpu_ymm13 : return fpu_ymm13; + case gdb_fpu_ymm14 : return fpu_ymm14; + case gdb_fpu_ymm15 : return fpu_ymm15; + default: + return LLDB_INVALID_REGNUM; + } + } + else if (kind == eRegisterKindLLDB) + { + return num; + } + } + } + } + + return LLDB_INVALID_REGNUM; +} + +uint32_t +RegisterContext_x86_64::NumSupportedHardwareWatchpoints() +{ + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} + +bool +RegisterContext_x86_64::IsWatchpointVacant(uint32_t hw_index) +{ + bool is_vacant = false; + RegisterValue value; + + assert(hw_index < NumSupportedHardwareWatchpoints()); + + if (m_watchpoints_initialized == false) + { + // Reset the debug status and debug control registers + RegisterValue zero_bits = RegisterValue(uint64_t(0)); + if (!WriteRegister(dr6, zero_bits) || !WriteRegister(dr7, zero_bits)) + assert(false && "Could not initialize watchpoint registers"); + m_watchpoints_initialized = true; + } + + if (ReadRegister(dr7, value)) + { + uint64_t val = value.GetAsUInt64(); + is_vacant = (val & (3 << 2*hw_index)) == 0; + } + + return is_vacant; +} + +static uint32_t +size_and_rw_bits(size_t size, bool read, bool write) +{ + uint32_t rw; + if (read) { + rw = 0x3; // READ or READ/WRITE + } else if (write) { + rw = 0x1; // WRITE + } else { + assert(0 && "read and write cannot both be false"); + } + + switch (size) { + case 1: + return rw; + case 2: + return (0x1 << 2) | rw; + case 4: + return (0x3 << 2) | rw; + case 8: + return (0x2 << 2) | rw; + default: + assert(0 && "invalid size, must be one of 1, 2, 4, or 8"); + } +} + +uint32_t +RegisterContext_x86_64::SetHardwareWatchpoint(addr_t addr, size_t size, + bool read, bool write) +{ + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + uint32_t hw_index; + + for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) + { + if (IsWatchpointVacant(hw_index)) + return SetHardwareWatchpointWithIndex(addr, size, + read, write, + hw_index); + } + + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContext_x86_64::SetHardwareWatchpointWithIndex(addr_t addr, size_t size, + bool read, bool write, + uint32_t hw_index) +{ + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + if (num_hw_watchpoints == 0 || hw_index >= num_hw_watchpoints) + return false; + + if (!(size == 1 || size == 2 || size == 4 || size == 8)) + return false; + + if (read == false && write == false) + return false; + + if (!IsWatchpointVacant(hw_index)) + return false; + + // Set both dr7 (debug control register) and dri (debug address register). + + // dr7{7-0} encodes the local/gloabl enable bits: + // global enable --. .-- local enable + // | | + // v v + // dr0 -> bits{1-0} + // dr1 -> bits{3-2} + // dr2 -> bits{5-4} + // dr3 -> bits{7-6} + // + // dr7{31-16} encodes the rw/len bits: + // b_x+3, b_x+2, b_x+1, b_x + // where bits{x+1, x} => rw + // 0b00: execute, 0b01: write, 0b11: read-or-write, + // 0b10: io read-or-write (unused) + // and bits{x+3, x+2} => len + // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte + // + // dr0 -> bits{19-16} + // dr1 -> bits{23-20} + // dr2 -> bits{27-24} + // dr3 -> bits{31-28} + if (hw_index < num_hw_watchpoints) + { + RegisterValue current_dr7_bits; + + if (ReadRegister(dr7, current_dr7_bits)) + { + uint64_t new_dr7_bits = current_dr7_bits.GetAsUInt64() | + (1 << (2*hw_index) | + size_and_rw_bits(size, read, write) << + (16+4*hw_index)); + + if (WriteRegister(dr0 + hw_index, RegisterValue(addr)) && + WriteRegister(dr7, RegisterValue(new_dr7_bits))) + return true; + } + } + + return false; +} + +bool +RegisterContext_x86_64::ClearHardwareWatchpoint(uint32_t hw_index) +{ + if (hw_index < NumSupportedHardwareWatchpoints()) + { + RegisterValue current_dr7_bits; + + if (ReadRegister(dr7, current_dr7_bits)) + { + uint64_t new_dr7_bits = current_dr7_bits.GetAsUInt64() & ~(3 << (2*hw_index)); + + if (WriteRegister(dr7, RegisterValue(new_dr7_bits))) + return true; + } + } + + return false; +} + +bool +RegisterContext_x86_64::IsWatchpointHit(uint32_t hw_index) +{ + bool is_hit = false; + + if (m_watchpoints_initialized == false) + { + // Reset the debug status and debug control registers + RegisterValue zero_bits = RegisterValue(uint64_t(0)); + if (!WriteRegister(dr6, zero_bits) || !WriteRegister(dr7, zero_bits)) + assert(false && "Could not initialize watchpoint registers"); + m_watchpoints_initialized = true; + } + + if (hw_index < NumSupportedHardwareWatchpoints()) + { + RegisterValue value; + + if (ReadRegister(dr6, value)) + { + uint64_t val = value.GetAsUInt64(); + is_hit = val & (1 << hw_index); + } + } + + return is_hit; +} + +addr_t +RegisterContext_x86_64::GetWatchpointAddress(uint32_t hw_index) +{ + addr_t wp_monitor_addr = LLDB_INVALID_ADDRESS; + + if (hw_index < NumSupportedHardwareWatchpoints()) + { + if (!IsWatchpointVacant(hw_index)) + { + RegisterValue value; + + if (ReadRegister(dr0 + hw_index, value)) + wp_monitor_addr = value.GetAsUInt64(); + } + } + + return wp_monitor_addr; +} + + +bool +RegisterContext_x86_64::ClearWatchpointHits() +{ + return WriteRegister(dr6, RegisterValue((uint64_t)0)); +} + +bool +RegisterContext_x86_64::HardwareSingleStep(bool enable) +{ + enum { TRACE_BIT = 0x100 }; + uint64_t rflags; + + if ((rflags = ReadRegisterAsUnsigned(gpr_rflags, -1UL)) == -1UL) + return false; + + if (enable) + { + if (rflags & TRACE_BIT) + return true; + + rflags |= TRACE_BIT; + } + else + { + if (!(rflags & TRACE_BIT)) + return false; + + rflags &= ~TRACE_BIT; + } + + return WriteRegisterFromUnsigned(gpr_rflags, rflags); +} + +#if defined(__linux__) or defined(__FreeBSD__) + +ProcessMonitor & +RegisterContext_x86_64::GetMonitor() +{ + ProcessSP base = CalculateProcess(); + ProcessPOSIX *process = static_cast<ProcessPOSIX*>(base.get()); + return process->GetMonitor(); +} + +bool +RegisterContext_x86_64::ReadGPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadGPR(m_thread.GetID(), &m_gpr, GetGPRSize()); +} + +bool +RegisterContext_x86_64::ReadFPR() +{ + ProcessMonitor &monitor = GetMonitor(); + if (m_fpr_type == eFXSAVE) + return monitor.ReadFPR(m_thread.GetID(), &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave)); + + if (m_fpr_type == eXSAVE) + return monitor.ReadRegisterSet(m_thread.GetID(), &m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE); + return false; +} + +bool +RegisterContext_x86_64::WriteGPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteGPR(m_thread.GetID(), &m_gpr, GetGPRSize()); +} + +bool +RegisterContext_x86_64::WriteFPR() +{ + ProcessMonitor &monitor = GetMonitor(); + if (m_fpr_type == eFXSAVE) + return monitor.WriteFPR(m_thread.GetID(), &m_fpr.xstate.fxsave, sizeof(m_fpr.xstate.fxsave)); + + if (m_fpr_type == eXSAVE) + return monitor.WriteRegisterSet(m_thread.GetID(), &m_iovec, sizeof(m_fpr.xstate.xsave), NT_X86_XSTATE); + return false; +} + +bool +RegisterContext_x86_64::ReadRegister(const unsigned reg, + RegisterValue &value) +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadRegisterValue(m_thread.GetID(), + GetRegisterOffset(reg), + GetRegisterName(reg), + GetRegisterSize(reg), + value); +} + +bool +RegisterContext_x86_64::WriteRegister(const unsigned reg, + const RegisterValue &value) +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteRegisterValue(m_thread.GetID(), + GetRegisterOffset(reg), + GetRegisterName(reg), + value); +} + +#else + +bool +RegisterContext_x86_64::ReadGPR() +{ + llvm_unreachable("not implemented"); + return false; +} + +bool +RegisterContext_x86_64::ReadFPR() +{ + llvm_unreachable("not implemented"); + return false; +} + +bool +RegisterContext_x86_64::WriteGPR() +{ + llvm_unreachable("not implemented"); + return false; +} + +bool +RegisterContext_x86_64::WriteFPR() +{ + llvm_unreachable("not implemented"); + return false; +} + +bool +RegisterContext_x86_64::ReadRegister(const unsigned reg, + RegisterValue &value) +{ + llvm_unreachable("not implemented"); + return false; +} + +bool +RegisterContext_x86_64::WriteRegister(const unsigned reg, + const RegisterValue &value) +{ + llvm_unreachable("not implemented"); + return false; +} + +#endif diff --git a/source/Plugins/Process/POSIX/RegisterContext_x86_64.h b/source/Plugins/Process/POSIX/RegisterContext_x86_64.h new file mode 100644 index 000000000000..9d59bd78e547 --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContext_x86_64.h @@ -0,0 +1,347 @@ +//===-- RegisterContext_x86_64.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContext_x86_64_H_ +#define liblldb_RegisterContext_x86_64_H_ + +#include "lldb/Core/Log.h" +#include "RegisterContextPOSIX.h" + +class ProcessMonitor; + +// Internal codes for all x86_64 registers. +enum +{ + k_first_gpr, + gpr_rax = k_first_gpr, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + gpr_ss, + gpr_ds, + gpr_es, + k_first_i386, + gpr_eax = k_first_i386, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_eip, + gpr_eflags, // eRegisterKindLLDB == 33 + k_last_i386 = gpr_eflags, + k_last_gpr = gpr_eflags, + + k_first_fpr, + fpu_fcw = k_first_fpr, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15, + k_last_fpr = fpu_xmm15, + k_first_avx, + fpu_ymm0 = k_first_avx, + fpu_ymm1, + fpu_ymm2, + fpu_ymm3, + fpu_ymm4, + fpu_ymm5, + fpu_ymm6, + fpu_ymm7, + fpu_ymm8, + fpu_ymm9, + fpu_ymm10, + fpu_ymm11, + fpu_ymm12, + fpu_ymm13, + fpu_ymm14, + fpu_ymm15, + k_last_avx = fpu_ymm15, + + dr0, + dr1, + dr2, + dr3, + dr4, + dr5, + dr6, + dr7, + + k_num_registers, + k_num_gpr_registers = k_last_gpr - k_first_gpr + 1, + k_num_fpr_registers = k_last_fpr - k_first_fpr + 1, + k_num_avx_registers = k_last_avx - k_first_avx + 1 +}; + +class RegisterContext_x86_64 + : public RegisterContextPOSIX +{ +public: + RegisterContext_x86_64 (lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + ~RegisterContext_x86_64(); + + void + Invalidate(); + + void + InvalidateAllRegisters(); + + size_t + GetRegisterCount(); + + virtual size_t + GetGPRSize() = 0; + + virtual unsigned + GetRegisterSize(unsigned reg); + + virtual unsigned + GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex(size_t reg); + + size_t + GetRegisterSetCount(); + + const lldb_private::RegisterSet * + GetRegisterSet(size_t set); + + unsigned + GetRegisterIndexFromOffset(unsigned offset); + + const char * + GetRegisterName(unsigned reg); + + virtual bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + uint32_t + ConvertRegisterKindToRegisterNumber(uint32_t kind, uint32_t num); + + uint32_t + NumSupportedHardwareWatchpoints(); + + uint32_t + SetHardwareWatchpoint(lldb::addr_t, size_t size, bool read, bool write); + + bool + SetHardwareWatchpointWithIndex(lldb::addr_t, size_t size, bool read, + bool write, uint32_t hw_index); + + bool + ClearHardwareWatchpoint(uint32_t hw_index); + + bool + HardwareSingleStep(bool enable); + + bool + UpdateAfterBreakpoint(); + + bool + IsWatchpointVacant(uint32_t hw_index); + + bool + IsWatchpointHit (uint32_t hw_index); + + lldb::addr_t + GetWatchpointAddress (uint32_t hw_index); + + bool + ClearWatchpointHits(); + + //--------------------------------------------------------------------------- + // Generic floating-point registers + //--------------------------------------------------------------------------- + + struct MMSReg + { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg + { + uint8_t bytes[16]; // 128-bits for each XMM register + }; + + struct FXSAVE + { + uint16_t fcw; + uint16_t fsw; + uint16_t ftw; + uint16_t fop; + uint64_t ip; + uint64_t dp; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[16]; + uint32_t padding[24]; + }; + + //--------------------------------------------------------------------------- + // Extended floating-point registers + //--------------------------------------------------------------------------- + struct YMMHReg + { + uint8_t bytes[16]; // 16 * 8 bits for the high bytes of each YMM register + }; + + struct YMMReg + { + uint8_t bytes[32]; // 16 * 16 bits for each YMM register + }; + + struct YMM + { + YMMReg ymm[16]; // assembled from ymmh and xmm registers + }; + + struct XSAVE_HDR + { + uint64_t xstate_bv; // OS enabled xstate mask to determine the extended states supported by the processor + uint64_t reserved1[2]; + uint64_t reserved2[5]; + } __attribute__((packed)); + + // x86 extensions to FXSAVE (i.e. for AVX processors) + struct XSAVE + { + FXSAVE i387; // floating point registers typical in i387_fxsave_struct + XSAVE_HDR header; // The xsave_hdr_struct can be used to determine if the following extensions are usable + YMMHReg ymmh[16]; // High 16 bytes of each of 16 YMM registers (the low bytes are in FXSAVE.xmm for compatibility with SSE) + // Slot any extensions to the register file here + } __attribute__((packed, aligned (64))); + + struct IOVEC + { + void *iov_base; // pointer to XSAVE + size_t iov_len; // sizeof(XSAVE) + }; + + //--------------------------------------------------------------------------- + // Note: prefer kernel definitions over user-land + //--------------------------------------------------------------------------- + enum FPRType + { + eNotValid = 0, + eFSAVE, // TODO + eFXSAVE, + eSOFT, // TODO + eXSAVE + }; + + // Floating-point registers + struct FPR + { + // Thread state for the floating-point unit of the processor read by ptrace. + union XSTATE { + FXSAVE fxsave; // Generic floating-point registers. + XSAVE xsave; // x86 extended processor state. + } xstate; + }; + +protected: + // Determines if an extended register set is supported on the processor running the inferior process. + virtual bool + IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfo(); + + virtual bool + ReadRegister(const unsigned reg, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister(const unsigned reg, const lldb_private::RegisterValue &value); + +private: + uint64_t m_gpr[k_num_gpr_registers]; // general purpose registers. + FPRType m_fpr_type; // determines the type of data stored by union FPR, if any. + FPR m_fpr; // floating-point registers including extended register sets. + IOVEC m_iovec; // wrapper for xsave. + YMM m_ymm_set; // copy of ymmh and xmm register halves. + + ProcessMonitor &GetMonitor(); + lldb::ByteOrder GetByteOrder(); + + bool CopyXSTATEtoYMM(uint32_t reg, lldb::ByteOrder byte_order); + bool CopyYMMtoXSTATE(uint32_t reg, lldb::ByteOrder byte_order); + bool IsFPR(unsigned reg, FPRType fpr_type); + + bool ReadGPR(); + bool ReadFPR(); + + bool WriteGPR(); + bool WriteFPR(); +}; + +#endif // #ifndef liblldb_RegisterContext_x86_64_H_ diff --git a/source/Plugins/Process/Utility/ARMDefines.h b/source/Plugins/Process/Utility/ARMDefines.h new file mode 100644 index 000000000000..4b1f06a2f9cd --- /dev/null +++ b/source/Plugins/Process/Utility/ARMDefines.h @@ -0,0 +1,110 @@ +//===-- lldb_ARMDefines.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_ARMDefines_h_ +#define lldb_ARMDefines_h_ + +// Common defintions for the ARM/Thumb Instruction Set Architecture. + +namespace lldb_private { + +// ARM shifter types +typedef enum +{ + SRType_LSL, + SRType_LSR, + SRType_ASR, + SRType_ROR, + SRType_RRX, + SRType_Invalid +} ARM_ShifterType; + +// ARM conditions // Meaning (integer) Meaning (floating-point) Condition flags +#define COND_EQ 0x0 // Equal Equal Z == 1 +#define COND_NE 0x1 // Not equal Not equal, or unordered Z == 0 +#define COND_CS 0x2 // Carry set >, ==, or unordered C == 1 +#define COND_HS 0x2 +#define COND_CC 0x3 // Carry clear Less than C == 0 +#define COND_LO 0x3 +#define COND_MI 0x4 // Minus, negative Less than N == 1 +#define COND_PL 0x5 // Plus, positive or zero >, ==, or unordered N == 0 +#define COND_VS 0x6 // Overflow Unordered V == 1 +#define COND_VC 0x7 // No overflow Not unordered V == 0 +#define COND_HI 0x8 // Unsigned higher Greater than, or unordered C == 1 and Z == 0 +#define COND_LS 0x9 // Unsigned lower or same Less than or equal C == 0 or Z == 1 +#define COND_GE 0xA // Greater than or equal Greater than or equal N == V +#define COND_LT 0xB // Less than Less than, or unordered N != V +#define COND_GT 0xC // Greater than Greater than Z == 0 and N == V +#define COND_LE 0xD // Less than or equal <, ==, or unordered Z == 1 or N != V +#define COND_AL 0xE // Always (unconditional) Always (unconditional) Any +#define COND_UNCOND 0xF + +static inline const char *ARMCondCodeToString(uint32_t CC) +{ + switch (CC) { + default: assert(0 && "Unknown condition code"); + case COND_EQ: return "eq"; + case COND_NE: return "ne"; + case COND_HS: return "hs"; + case COND_LO: return "lo"; + case COND_MI: return "mi"; + case COND_PL: return "pl"; + case COND_VS: return "vs"; + case COND_VC: return "vc"; + case COND_HI: return "hi"; + case COND_LS: return "ls"; + case COND_GE: return "ge"; + case COND_LT: return "lt"; + case COND_GT: return "gt"; + case COND_LE: return "le"; + case COND_AL: return "al"; + } +} + +// Bit positions for CPSR +#define CPSR_T_POS 5 +#define CPSR_F_POS 6 +#define CPSR_I_POS 7 +#define CPSR_A_POS 8 +#define CPSR_E_POS 9 +#define CPSR_J_POS 24 +#define CPSR_Q_POS 27 +#define CPSR_V_POS 28 +#define CPSR_C_POS 29 +#define CPSR_Z_POS 30 +#define CPSR_N_POS 31 + +// CPSR mode definitions +#define CPSR_MODE_USR 0x10u +#define CPSR_MODE_FIQ 0x11u +#define CPSR_MODE_IRQ 0x12u +#define CPSR_MODE_SVC 0x13u +#define CPSR_MODE_ABT 0x17u +#define CPSR_MODE_UND 0x1bu +#define CPSR_MODE_SYS 0x1fu + +// Masks for CPSR +#define MASK_CPSR_MODE_MASK (0x0000001fu) +#define MASK_CPSR_IT_MASK (0x0600fc00u) +#define MASK_CPSR_T (1u << CPSR_T_POS) +#define MASK_CPSR_F (1u << CPSR_F_POS) +#define MASK_CPSR_I (1u << CPSR_I_POS) +#define MASK_CPSR_A (1u << CPSR_A_POS) +#define MASK_CPSR_E (1u << CPSR_E_POS) +#define MASK_CPSR_GE_MASK (0x000f0000u) +#define MASK_CPSR_J (1u << CPSR_J_POS) +#define MASK_CPSR_Q (1u << CPSR_Q_POS) +#define MASK_CPSR_V (1u << CPSR_V_POS) +#define MASK_CPSR_C (1u << CPSR_C_POS) +#define MASK_CPSR_Z (1u << CPSR_Z_POS) +#define MASK_CPSR_N (1u << CPSR_N_POS) + +} // namespace lldb_private + +#endif // lldb_ARMDefines_h_ diff --git a/source/Plugins/Process/Utility/ARMUtils.h b/source/Plugins/Process/Utility/ARMUtils.h new file mode 100644 index 000000000000..76d64e15a53e --- /dev/null +++ b/source/Plugins/Process/Utility/ARMUtils.h @@ -0,0 +1,394 @@ +//===-- ARMUtils.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_ARMUtils_h_ +#define lldb_ARMUtils_h_ + +#include "ARMDefines.h" +#include "InstructionUtils.h" +#include "llvm/Support/MathExtras.h" // for SignExtend64 template function + +// Common utilities for the ARM/Thumb Instruction Set Architecture. + +namespace lldb_private { + +static inline uint32_t Align(uint32_t val, uint32_t alignment) +{ + return alignment * (val / alignment); +} + +static inline uint32_t DecodeImmShift(const uint32_t type, const uint32_t imm5, ARM_ShifterType &shift_t) +{ + switch (type) + { + default: + //assert(0 && "Invalid shift type"); + case 0: + shift_t = SRType_LSL; + return imm5; + case 1: + shift_t = SRType_LSR; + return (imm5 == 0 ? 32 : imm5); + case 2: + shift_t = SRType_ASR; + return (imm5 == 0 ? 32 : imm5); + case 3: + if (imm5 == 0) + { + shift_t = SRType_RRX; + return 1; + } + else + { + shift_t = SRType_ROR; + return imm5; + } + } + shift_t = SRType_Invalid; + return UINT32_MAX; + +} + +// A8.6.35 CMP (register) -- Encoding T3 +// Convenience function. +static inline uint32_t DecodeImmShiftThumb(const uint32_t opcode, ARM_ShifterType &shift_t) +{ + return DecodeImmShift(Bits32(opcode, 5, 4), Bits32(opcode, 14, 12)<<2 | Bits32(opcode, 7, 6), shift_t); +} + +// A8.6.35 CMP (register) -- Encoding A1 +// Convenience function. +static inline uint32_t DecodeImmShiftARM(const uint32_t opcode, ARM_ShifterType &shift_t) +{ + return DecodeImmShift(Bits32(opcode, 6, 5), Bits32(opcode, 11, 7), shift_t); +} + +static inline uint32_t DecodeImmShift(const ARM_ShifterType shift_t, const uint32_t imm5) +{ + ARM_ShifterType dont_care; + return DecodeImmShift(shift_t, imm5, dont_care); +} + +static inline ARM_ShifterType DecodeRegShift(const uint32_t type) +{ + switch (type) { + default: + //assert(0 && "Invalid shift type"); + return SRType_Invalid; + case 0: + return SRType_LSL; + case 1: + return SRType_LSR; + case 2: + return SRType_ASR; + case 3: + return SRType_ROR; + } +} + +static inline uint32_t LSL_C(const uint32_t value, const uint32_t amount, uint32_t &carry_out, bool *success) +{ + if (amount == 0) { + *success = false; + return 0; + } + *success = true; + carry_out = amount <= 32 ? Bit32(value, 32 - amount) : 0; + return value << amount; +} + +static inline uint32_t LSL(const uint32_t value, const uint32_t amount, bool *success) +{ + *success = true; + if (amount == 0) + return value; + uint32_t dont_care; + uint32_t result = LSL_C(value, amount, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t LSR_C(const uint32_t value, const uint32_t amount, uint32_t &carry_out, bool *success) +{ + if (amount == 0) { + *success = false; + return 0; + } + *success = true; + carry_out = amount <= 32 ? Bit32(value, amount - 1) : 0; + return value >> amount; +} + +static inline uint32_t LSR(const uint32_t value, const uint32_t amount, bool *success) +{ + *success = true; + if (amount == 0) + return value; + uint32_t dont_care; + uint32_t result = LSR_C(value, amount, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t ASR_C(const uint32_t value, const uint32_t amount, uint32_t &carry_out, bool *success) +{ + if (amount == 0 || amount > 32) { + *success = false; + return 0; + } + *success = true; + bool negative = BitIsSet(value, 31); + if (amount <= 32) + { + carry_out = Bit32(value, amount - 1); + int64_t extended = llvm::SignExtend64<32>(value); + return UnsignedBits(extended, amount + 31, amount); + } + else + { + carry_out = (negative ? 1 : 0); + return (negative ? 0xffffffff : 0); + } +} + +static inline uint32_t ASR(const uint32_t value, const uint32_t amount, bool *success) +{ + *success = true; + if (amount == 0) + return value; + uint32_t dont_care; + uint32_t result = ASR_C(value, amount, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t ROR_C(const uint32_t value, const uint32_t amount, uint32_t &carry_out, bool *success) +{ + if (amount == 0) { + *success = false; + return 0; + } + *success = true; + uint32_t amt = amount % 32; + uint32_t result = Rotr32(value, amt); + carry_out = Bit32(value, 31); + return result; +} + +static inline uint32_t ROR(const uint32_t value, const uint32_t amount, bool *success) +{ + *success = true; + if (amount == 0) + return value; + uint32_t dont_care; + uint32_t result = ROR_C(value, amount, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t RRX_C(const uint32_t value, const uint32_t carry_in, uint32_t &carry_out, bool *success) +{ + *success = true; + carry_out = Bit32(value, 0); + return Bit32(carry_in, 0) << 31 | Bits32(value, 31, 1); +} + +static inline uint32_t RRX(const uint32_t value, const uint32_t carry_in, bool *success) +{ + *success = true; + uint32_t dont_care; + uint32_t result = RRX_C(value, carry_in, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t Shift_C(const uint32_t value, ARM_ShifterType type, const uint32_t amount, + const uint32_t carry_in, uint32_t &carry_out, bool *success) +{ + if (type == SRType_RRX && amount != 1) { + *success = false; + return 0; + } + *success = true; + + if (amount == 0) { + carry_out = carry_in; + return value; + } + uint32_t result; + switch (type) { + case SRType_LSL: + result = LSL_C(value, amount, carry_out, success); + break; + case SRType_LSR: + result = LSR_C(value, amount, carry_out, success); + break; + case SRType_ASR: + result = ASR_C(value, amount, carry_out, success); + break; + case SRType_ROR: + result = ROR_C(value, amount, carry_out, success); + break; + case SRType_RRX: + result = RRX_C(value, carry_in, carry_out, success); + break; + default: + *success = false; + break; + } + if (*success) + return result; + else + return 0; +} + +static inline uint32_t Shift(const uint32_t value, ARM_ShifterType type, const uint32_t amount, + const uint32_t carry_in, bool *success) +{ + // Don't care about carry out in this case. + uint32_t dont_care; + uint32_t result = Shift_C(value, type, amount, carry_in, dont_care, success); + if (*success) + return result; + else + return 0; +} + +static inline uint32_t bits(const uint32_t val, const uint32_t msbit, const uint32_t lsbit) +{ + return Bits32(val, msbit, lsbit); +} + +static inline uint32_t bit(const uint32_t val, const uint32_t msbit) +{ + return bits(val, msbit, msbit); +} + +static uint32_t ror(uint32_t val, uint32_t N, uint32_t shift) +{ + uint32_t m = shift % N; + return (val >> m) | (val << (N - m)); +} + +// (imm32, carry_out) = ARMExpandImm_C(imm12, carry_in) +static inline uint32_t ARMExpandImm_C(uint32_t opcode, uint32_t carry_in, uint32_t &carry_out) +{ + uint32_t imm32; // the expanded result + uint32_t imm = bits(opcode, 7, 0); // immediate value + uint32_t amt = 2 * bits(opcode, 11, 8); // rotate amount + if (amt == 0) + { + imm32 = imm; + carry_out = carry_in; + } + else + { + imm32 = ror(imm, 32, amt); + carry_out = Bit32(imm32, 31); + } + return imm32; +} + +static inline uint32_t ARMExpandImm(uint32_t opcode) +{ + // 'carry_in' argument to following function call does not affect the imm32 result. + uint32_t carry_in = 0; + uint32_t carry_out; + return ARMExpandImm_C(opcode, carry_in, carry_out); +} + +// (imm32, carry_out) = ThumbExpandImm_C(imm12, carry_in) +static inline uint32_t ThumbExpandImm_C(uint32_t opcode, uint32_t carry_in, uint32_t &carry_out) +{ + uint32_t imm32; // the expaned result + const uint32_t i = bit(opcode, 26); + const uint32_t imm3 = bits(opcode, 14, 12); + const uint32_t abcdefgh = bits(opcode, 7, 0); + const uint32_t imm12 = i << 11 | imm3 << 8 | abcdefgh; + + if (bits(imm12, 11, 10) == 0) + { + switch (bits(imm12, 9, 8)) { + default: // Keep static analyzer happy with a default case + case 0: + imm32 = abcdefgh; + break; + + case 1: + imm32 = abcdefgh << 16 | abcdefgh; + break; + + case 2: + imm32 = abcdefgh << 24 | abcdefgh << 8; + break; + + case 3: + imm32 = abcdefgh << 24 | abcdefgh << 16 | abcdefgh << 8 | abcdefgh; + break; + } + carry_out = carry_in; + } + else + { + const uint32_t unrotated_value = 0x80 | bits(imm12, 6, 0); + imm32 = ror(unrotated_value, 32, bits(imm12, 11, 7)); + carry_out = Bit32(imm32, 31); + } + return imm32; +} + +static inline uint32_t ThumbExpandImm(uint32_t opcode) +{ + // 'carry_in' argument to following function call does not affect the imm32 result. + uint32_t carry_in = 0; + uint32_t carry_out; + return ThumbExpandImm_C(opcode, carry_in, carry_out); +} + +// imm32 = ZeroExtend(i:imm3:imm8, 32) +static inline uint32_t ThumbImm12(uint32_t opcode) +{ + const uint32_t i = bit(opcode, 26); + const uint32_t imm3 = bits(opcode, 14, 12); + const uint32_t imm8 = bits(opcode, 7, 0); + const uint32_t imm12 = i << 11 | imm3 << 8 | imm8; + return imm12; +} + +// imm32 = ZeroExtend(imm7:'00', 32) +static inline uint32_t ThumbImm7Scaled(uint32_t opcode) +{ + const uint32_t imm7 = bits(opcode, 6, 0); + return imm7 * 4; +} + +// imm32 = ZeroExtend(imm8:'00', 32) +static inline uint32_t ThumbImm8Scaled(uint32_t opcode) +{ + const uint32_t imm8 = bits(opcode, 7, 0); + return imm8 * 4; +} + +// This function performs the check for the register numbers 13 and 15 that are +// not permitted for many Thumb register specifiers. +static inline bool BadReg(uint32_t n) { return n == 13 || n == 15; } + +} // namespace lldb_private + +#endif // lldb_ARMUtils_h_ diff --git a/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp b/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp new file mode 100644 index 000000000000..0c95d66cef94 --- /dev/null +++ b/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp @@ -0,0 +1,279 @@ +//===-- DynamicRegisterInfo.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "DynamicRegisterInfo.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" + +#ifndef LLDB_DISABLE_PYTHON +#include "lldb/Interpreter/PythonDataObjects.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +DynamicRegisterInfo::DynamicRegisterInfo () : + m_regs (), + m_sets (), + m_set_reg_nums (), + m_set_names (), + m_reg_data_byte_size (0) +{ +} + +DynamicRegisterInfo::DynamicRegisterInfo (const lldb_private::PythonDictionary &dict) : + m_regs (), + m_sets (), + m_set_reg_nums (), + m_set_names (), + m_reg_data_byte_size (0) +{ + SetRegisterInfo (dict); +} + +DynamicRegisterInfo::~DynamicRegisterInfo () +{ +} + + +size_t +DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict) +{ +#ifndef LLDB_DISABLE_PYTHON + PythonList sets (dict.GetItemForKey("sets")); + if (sets) + { + const uint32_t num_sets = sets.GetSize(); + for (uint32_t i=0; i<num_sets; ++i) + { + PythonString py_set_name(sets.GetItemAtIndex(i)); + ConstString set_name; + if (py_set_name) + set_name.SetCString(py_set_name.GetString()); + if (set_name) + { + RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL }; + m_sets.push_back (new_set); + } + else + { + Clear(); + return 0; + } + } + m_set_reg_nums.resize(m_sets.size()); + } + PythonList regs (dict.GetItemForKey("registers")); + if (regs) + { + const uint32_t num_regs = regs.GetSize(); + PythonString name_pystr("name"); + PythonString altname_pystr("alt-name"); + PythonString bitsize_pystr("bitsize"); + PythonString offset_pystr("offset"); + PythonString encoding_pystr("encoding"); + PythonString format_pystr("format"); + PythonString set_pystr("set"); + PythonString gcc_pystr("gcc"); + PythonString dwarf_pystr("dwarf"); + PythonString generic_pystr("generic"); + for (uint32_t i=0; i<num_regs; ++i) + { + PythonDictionary reg_info_dict(regs.GetItemAtIndex(i)); + if (reg_info_dict) + { + // { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 2, 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, + RegisterInfo reg_info; + bzero (®_info, sizeof(reg_info)); + + reg_info.name = ConstString (reg_info_dict.GetItemForKeyAsString(name_pystr)).GetCString(); + if (reg_info.name == NULL) + { + Clear(); + return 0; + } + + reg_info.alt_name = ConstString (reg_info_dict.GetItemForKeyAsString(altname_pystr)).GetCString(); + + reg_info.byte_offset = reg_info_dict.GetItemForKeyAsInteger(offset_pystr, UINT32_MAX); + + if (reg_info.byte_offset == UINT32_MAX) + { + Clear(); + return 0; + } + reg_info.byte_size = reg_info_dict.GetItemForKeyAsInteger(bitsize_pystr, 0) / 8; + + if (reg_info.byte_size == 0) + { + Clear(); + return 0; + } + + const char *format_cstr = reg_info_dict.GetItemForKeyAsString(format_pystr); + if (format_cstr) + { + if (Args::StringToFormat(format_cstr, reg_info.format, NULL).Fail()) + { + Clear(); + return 0; + } + } + else + reg_info.format = eFormatHex; + + const char *encoding_cstr = reg_info_dict.GetItemForKeyAsString(encoding_pystr); + if (encoding_cstr) + reg_info.encoding = Args::StringToEncoding (encoding_cstr, eEncodingUint); + else + reg_info.encoding = eEncodingUint; + + const int64_t set = reg_info_dict.GetItemForKeyAsInteger(set_pystr, -1); + if (set >= m_sets.size()) + { + Clear(); + return 0; + } + + reg_info.kinds[lldb::eRegisterKindLLDB] = i; + reg_info.kinds[lldb::eRegisterKindGDB] = i; + reg_info.kinds[lldb::eRegisterKindGCC] = reg_info_dict.GetItemForKeyAsInteger(gcc_pystr, LLDB_INVALID_REGNUM); + reg_info.kinds[lldb::eRegisterKindDWARF] = reg_info_dict.GetItemForKeyAsInteger(dwarf_pystr, LLDB_INVALID_REGNUM); + reg_info.kinds[lldb::eRegisterKindGeneric] = Args::StringToGenericRegister (reg_info_dict.GetItemForKeyAsString(generic_pystr)); + const size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; + if (m_reg_data_byte_size < end_reg_offset) + m_reg_data_byte_size = end_reg_offset; + + m_regs.push_back (reg_info); + m_set_reg_nums[set].push_back(i); + + } + else + { + Clear(); + return 0; + } + } + Finalize (); + } +#endif + return 0; +} + + +void +DynamicRegisterInfo::AddRegister (RegisterInfo ®_info, + ConstString ®_name, + ConstString ®_alt_name, + ConstString &set_name) +{ + const uint32_t reg_num = m_regs.size(); + reg_info.name = reg_name.AsCString(); + assert (reg_info.name); + reg_info.alt_name = reg_alt_name.AsCString(NULL); + m_regs.push_back (reg_info); + uint32_t set = GetRegisterSetIndexByName (set_name, true); + assert (set < m_sets.size()); + assert (set < m_set_reg_nums.size()); + assert (set < m_set_names.size()); + m_set_reg_nums[set].push_back(reg_num); + size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; + if (m_reg_data_byte_size < end_reg_offset) + m_reg_data_byte_size = end_reg_offset; +} + +void +DynamicRegisterInfo::Finalize () +{ + for (uint32_t set = 0; set < m_sets.size(); ++set) + { + assert (m_sets.size() == m_set_reg_nums.size()); + m_sets[set].num_registers = m_set_reg_nums[set].size(); + m_sets[set].registers = &m_set_reg_nums[set][0]; + } +} + +size_t +DynamicRegisterInfo::GetNumRegisters() const +{ + return m_regs.size(); +} + +size_t +DynamicRegisterInfo::GetNumRegisterSets() const +{ + return m_sets.size(); +} + +size_t +DynamicRegisterInfo::GetRegisterDataByteSize() const +{ + return m_reg_data_byte_size; +} + +const RegisterInfo * +DynamicRegisterInfo::GetRegisterInfoAtIndex (uint32_t i) const +{ + if (i < m_regs.size()) + return &m_regs[i]; + return NULL; +} + +const RegisterSet * +DynamicRegisterInfo::GetRegisterSet (uint32_t i) const +{ + if (i < m_sets.size()) + return &m_sets[i]; + return NULL; +} + +uint32_t +DynamicRegisterInfo::GetRegisterSetIndexByName (ConstString &set_name, bool can_create) +{ + name_collection::iterator pos, end = m_set_names.end(); + for (pos = m_set_names.begin(); pos != end; ++pos) + { + if (*pos == set_name) + return std::distance (m_set_names.begin(), pos); + } + + m_set_names.push_back(set_name); + m_set_reg_nums.resize(m_set_reg_nums.size()+1); + RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL }; + m_sets.push_back (new_set); + return m_sets.size() - 1; +} + +uint32_t +DynamicRegisterInfo::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const +{ + reg_collection::const_iterator pos, end = m_regs.end(); + for (pos = m_regs.begin(); pos != end; ++pos) + { + if (pos->kinds[kind] == num) + return std::distance (m_regs.begin(), pos); + } + + return LLDB_INVALID_REGNUM; +} + +void +DynamicRegisterInfo::Clear() +{ + m_regs.clear(); + m_sets.clear(); + m_set_reg_nums.clear(); + m_set_names.clear(); +} diff --git a/source/Plugins/Process/Utility/DynamicRegisterInfo.h b/source/Plugins/Process/Utility/DynamicRegisterInfo.h new file mode 100644 index 000000000000..a11cd333545f --- /dev/null +++ b/source/Plugins/Process/Utility/DynamicRegisterInfo.h @@ -0,0 +1,85 @@ +//===-- DynamicRegisterInfo.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_DynamicRegisterInfo_h_ +#define lldb_DynamicRegisterInfo_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" + +class DynamicRegisterInfo +{ +public: + DynamicRegisterInfo (); + + DynamicRegisterInfo (const lldb_private::PythonDictionary &dict); + + virtual + ~DynamicRegisterInfo (); + + size_t + SetRegisterInfo (const lldb_private::PythonDictionary &dict); + + void + AddRegister (lldb_private::RegisterInfo ®_info, + lldb_private::ConstString ®_name, + lldb_private::ConstString ®_alt_name, + lldb_private::ConstString &set_name); + + void + Finalize (); + + size_t + GetNumRegisters() const; + + size_t + GetNumRegisterSets() const; + + size_t + GetRegisterDataByteSize() const; + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t i) const; + + const lldb_private::RegisterSet * + GetRegisterSet (uint32_t i) const; + + uint32_t + GetRegisterSetIndexByName (lldb_private::ConstString &set_name, bool can_create); + + uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const; + + void + Clear(); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from DynamicRegisterInfo can see and modify these + //------------------------------------------------------------------ + typedef std::vector <lldb_private::RegisterInfo> reg_collection; + typedef std::vector <lldb_private::RegisterSet> set_collection; + typedef std::vector <uint32_t> reg_num_collection; + typedef std::vector <reg_num_collection> set_reg_num_collection; + typedef std::vector <lldb_private::ConstString> name_collection; + + reg_collection m_regs; + set_collection m_sets; + set_reg_num_collection m_set_reg_nums; + name_collection m_set_names; + size_t m_reg_data_byte_size; // The number of bytes required to store all registers +}; + +#endif // lldb_DynamicRegisterInfo_h_ diff --git a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp new file mode 100644 index 000000000000..499d6d766150 --- /dev/null +++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -0,0 +1,274 @@ +//===-- InferiorCallPOSIX.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InferiorCallPOSIX.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlanCallFunction.h" + +#include <sys/mman.h> + +using namespace lldb; +using namespace lldb_private; + +bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr, + addr_t addr, addr_t length, unsigned prot, + unsigned flags, addr_t fd, addr_t offset) { + Thread *thread = process->GetThreadList().GetSelectedThread().get(); + if (thread == NULL) + return false; + + const bool append = true; + const bool include_symbols = true; + const bool include_inlines = false; + SymbolContextList sc_list; + const uint32_t count + = process->GetTarget().GetImages().FindFunctions (ConstString ("mmap"), + eFunctionNameTypeFull, + include_symbols, + include_inlines, + append, + sc_list); + if (count > 0) + { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) + { + const uint32_t range_scope = eSymbolContextFunction | eSymbolContextSymbol; + const bool use_inline_block_range = false; + const bool stop_other_threads = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + const bool try_all_threads = true; + const uint32_t timeout_usec = 500000; + + addr_t prot_arg, flags_arg = 0; + if (prot == eMmapProtNone) + prot_arg = PROT_NONE; + else { + prot_arg = 0; + if (prot & eMmapProtExec) + prot_arg |= PROT_EXEC; + if (prot & eMmapProtRead) + prot_arg |= PROT_READ; + if (prot & eMmapProtWrite) + prot_arg |= PROT_WRITE; + } + + if (flags & eMmapFlagsPrivate) + flags_arg |= MAP_PRIVATE; + if (flags & eMmapFlagsAnon) + flags_arg |= MAP_ANON; + + AddressRange mmap_range; + if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, mmap_range)) + { + ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); + ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + ThreadPlanCallFunction *call_function_thread_plan + = new ThreadPlanCallFunction (*thread, + mmap_range.GetBaseAddress(), + clang_void_ptr_type, + stop_other_threads, + unwind_on_error, + ignore_breakpoints, + &addr, + &length, + &prot_arg, + &flags_arg, + &fd, + &offset); + lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan); + if (call_plan_sp) + { + StreamFile error_strm; + // This plan is a utility plan, so set it to discard itself when done. + call_plan_sp->SetIsMasterPlan (true); + call_plan_sp->SetOkayToDiscard(true); + + StackFrame *frame = thread->GetStackFrameAtIndex (0).get(); + if (frame) + { + ExecutionContext exe_ctx; + frame->CalculateExecutionContext (exe_ctx); + ExecutionResults result = process->RunThreadPlan (exe_ctx, + call_plan_sp, + stop_other_threads, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + error_strm); + if (result == eExecutionCompleted) + { + + allocated_addr = call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + if (process->GetAddressByteSize() == 4) + { + if (allocated_addr == UINT32_MAX) + return false; + } + else if (process->GetAddressByteSize() == 8) + { + if (allocated_addr == UINT64_MAX) + return false; + } + return true; + } + } + } + } + } + } + + return false; +} + +bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr, + addr_t length) { + Thread *thread = process->GetThreadList().GetSelectedThread().get(); + if (thread == NULL) + return false; + + const bool append = true; + const bool include_symbols = true; + const bool include_inlines = false; + SymbolContextList sc_list; + const uint32_t count + = process->GetTarget().GetImages().FindFunctions (ConstString ("munmap"), + eFunctionNameTypeFull, + include_symbols, + include_inlines, + append, + sc_list); + if (count > 0) + { + SymbolContext sc; + if (sc_list.GetContextAtIndex(0, sc)) + { + const uint32_t range_scope = eSymbolContextFunction | eSymbolContextSymbol; + const bool use_inline_block_range = false; + const bool stop_other_threads = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + const bool try_all_threads = true; + const uint32_t timeout_usec = 500000; + + AddressRange munmap_range; + if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, munmap_range)) + { + lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread, + munmap_range.GetBaseAddress(), + ClangASTType(), + stop_other_threads, + unwind_on_error, + ignore_breakpoints, + &addr, + &length)); + if (call_plan_sp) + { + StreamFile error_strm; + // This plan is a utility plan, so set it to discard itself when done. + call_plan_sp->SetIsMasterPlan (true); + call_plan_sp->SetOkayToDiscard(true); + + StackFrame *frame = thread->GetStackFrameAtIndex (0).get(); + if (frame) + { + ExecutionContext exe_ctx; + frame->CalculateExecutionContext (exe_ctx); + ExecutionResults result = process->RunThreadPlan (exe_ctx, + call_plan_sp, + stop_other_threads, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + error_strm); + if (result == eExecutionCompleted) + { + return true; + } + } + } + } + } + } + + return false; +} + +bool lldb_private::InferiorCall(Process *process, const Address *address, addr_t &returned_func) { + Thread *thread = process->GetThreadList().GetSelectedThread().get(); + if (thread == NULL || address == NULL) + return false; + + const bool stop_other_threads = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + const bool try_all_threads = true; + const uint32_t timeout_usec = 500000; + + ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); + ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); + ThreadPlanCallFunction *call_function_thread_plan + = new ThreadPlanCallFunction (*thread, + *address, + clang_void_ptr_type, + stop_other_threads, + unwind_on_error, + ignore_breakpoints); + lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan); + if (call_plan_sp) + { + StreamFile error_strm; + // This plan is a utility plan, so set it to discard itself when done. + call_plan_sp->SetIsMasterPlan (true); + call_plan_sp->SetOkayToDiscard(true); + + StackFrame *frame = thread->GetStackFrameAtIndex (0).get(); + if (frame) + { + ExecutionContext exe_ctx; + frame->CalculateExecutionContext (exe_ctx); + ExecutionResults result = process->RunThreadPlan (exe_ctx, + call_plan_sp, + stop_other_threads, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + error_strm); + if (result == eExecutionCompleted) + { + returned_func = call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + + if (process->GetAddressByteSize() == 4) + { + if (returned_func == UINT32_MAX) + return false; + } + else if (process->GetAddressByteSize() == 8) + { + if (returned_func == UINT64_MAX) + return false; + } + return true; + } + } + } + + return false; +} diff --git a/source/Plugins/Process/Utility/InferiorCallPOSIX.h b/source/Plugins/Process/Utility/InferiorCallPOSIX.h new file mode 100644 index 000000000000..d8b6d0ed57fd --- /dev/null +++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.h @@ -0,0 +1,43 @@ +//===-- InferiorCallPOSIX.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_InferiorCallPOSIX_h_ +#define lldb_InferiorCallPOSIX_h_ + +// Inferior execution of POSIX functions. + +#include "lldb/lldb-types.h" + +namespace lldb_private { + +class Process; + +enum MmapProt { + eMmapProtNone = 0, + eMmapProtExec = 1, + eMmapProtRead = 2, + eMmapProtWrite = 4 +}; + +enum MmapFlags { + eMmapFlagsPrivate = 1, + eMmapFlagsAnon = 2 +}; + +bool InferiorCallMmap(Process *proc, lldb::addr_t &allocated_addr, + lldb::addr_t addr, lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, lldb::addr_t offset); + +bool InferiorCallMunmap(Process *proc, lldb::addr_t addr, lldb::addr_t length); + +bool InferiorCall(Process *proc, const Address *address, lldb::addr_t &returned_func); + +} // namespace lldb_private + +#endif // lldb_InferiorCallPOSIX_h_ diff --git a/source/Plugins/Process/Utility/InstructionUtils.h b/source/Plugins/Process/Utility/InstructionUtils.h new file mode 100644 index 000000000000..4bb644e6efe6 --- /dev/null +++ b/source/Plugins/Process/Utility/InstructionUtils.h @@ -0,0 +1,136 @@ +//===-- InstructionUtils.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_InstructionUtils_h_ +#define lldb_InstructionUtils_h_ + +// Common utilities for manipulating instruction bit fields. + +namespace lldb_private { + +// Return the bit field(s) from the most significant bit (msbit) to the +// least significant bit (lsbit) of a 64-bit unsigned value. +static inline uint64_t +Bits64 (const uint64_t bits, const uint32_t msbit, const uint32_t lsbit) +{ + assert(msbit < 64 && lsbit <= msbit); + return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1); +} + +// Return the bit field(s) from the most significant bit (msbit) to the +// least significant bit (lsbit) of a 32-bit unsigned value. +static inline uint32_t +Bits32 (const uint32_t bits, const uint32_t msbit, const uint32_t lsbit) +{ + assert(msbit < 32 && lsbit <= msbit); + return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1); +} + +// Return the bit value from the 'bit' position of a 32-bit unsigned value. +static inline uint32_t +Bit32 (const uint32_t bits, const uint32_t bit) +{ + return (bits >> bit) & 1u; +} + +static inline uint64_t +Bit64 (const uint64_t bits, const uint32_t bit) +{ + return (bits >> bit) & 1ull; +} + +// Set the bit field(s) from the most significant bit (msbit) to the +// least significant bit (lsbit) of a 32-bit unsigned value to 'val'. +static inline void +SetBits32(uint32_t &bits, const uint32_t msbit, const uint32_t lsbit, const uint32_t val) +{ + assert(msbit < 32 && lsbit < 32 && msbit >= lsbit); + uint32_t mask = ((1u << (msbit - lsbit + 1)) - 1); + bits &= ~(mask << lsbit); + bits |= (val & mask) << lsbit; +} + +// Set the 'bit' position of a 32-bit unsigned value to 'val'. +static inline void +SetBit32(uint32_t &bits, const uint32_t bit, const uint32_t val) +{ + SetBits32(bits, bit, bit, val); +} + +// Rotate a 32-bit unsigned value right by the specified amount. +static inline uint32_t +Rotr32 (uint32_t bits, uint32_t amt) +{ + assert(amt < 32 && "Invalid rotate amount"); + return (bits >> amt) | (bits << ((32-amt)&31)); +} + +// Rotate a 32-bit unsigned value left by the specified amount. +static inline uint32_t +Rotl32 (uint32_t bits, uint32_t amt) +{ + assert(amt < 32 && "Invalid rotate amount"); + return (bits << amt) | (bits >> ((32-amt)&31)); +} + +// Create a mask that starts at bit zero and includes "bit" +static inline uint64_t +MaskUpToBit (const uint64_t bit) +{ + return (1ull << (bit + 1ull)) - 1ull; +} + +// Return an integer result equal to the number of bits of x that are ones. +static inline uint32_t +BitCount (uint64_t x) +{ + // c accumulates the total bits set in x + uint32_t c; + for (c = 0; x; ++c) + { + x &= x - 1; // clear the least significant bit set + } + return c; +} + +static inline bool +BitIsSet (const uint64_t value, const uint64_t bit) +{ + return (value & (1ull << bit)) != 0; +} + +static inline bool +BitIsClear (const uint64_t value, const uint64_t bit) +{ + return (value & (1ull << bit)) == 0; +} + +static inline uint64_t +UnsignedBits (const uint64_t value, const uint64_t msbit, const uint64_t lsbit) +{ + uint64_t result = value >> lsbit; + result &= MaskUpToBit (msbit - lsbit); + return result; +} + +static inline int64_t +SignedBits (const uint64_t value, const uint64_t msbit, const uint64_t lsbit) +{ + uint64_t result = UnsignedBits (value, msbit, lsbit); + if (BitIsSet(value, msbit)) + { + // Sign extend + result |= ~MaskUpToBit (msbit - lsbit); + } + return result; +} + +} // namespace lldb_private + +#endif // lldb_InstructionUtils_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp new file mode 100644 index 000000000000..4d77b6f20fdc --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp @@ -0,0 +1,1226 @@ +//===-- RegisterContextDarwin_arm.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) + +#include "RegisterContextDarwin_arm.h" + +// C Includes +#include <mach/mach_types.h> +#include <mach/thread_act.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Host/Endian.h" +#include "llvm/Support/Compiler.h" + +#include "Plugins/Process/Utility/InstructionUtils.h" + +// Support building against older versions of LLVM, this macro was added +// recently. +#ifndef LLVM_EXTENSION +#define LLVM_EXTENSION +#endif + +// Project includes +#include "ARM_GCC_Registers.h" +#include "ARM_DWARF_Registers.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_r0 = 0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, gpr_sp = gpr_r13, + gpr_r14, gpr_lr = gpr_r14, + gpr_r15, gpr_pc = gpr_r15, + gpr_cpsr, + + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, + + exc_exception, + exc_fsr, + exc_far, + + dbg_bvr0, + dbg_bvr1, + dbg_bvr2, + dbg_bvr3, + dbg_bvr4, + dbg_bvr5, + dbg_bvr6, + dbg_bvr7, + dbg_bvr8, + dbg_bvr9, + dbg_bvr10, + dbg_bvr11, + dbg_bvr12, + dbg_bvr13, + dbg_bvr14, + dbg_bvr15, + + dbg_bcr0, + dbg_bcr1, + dbg_bcr2, + dbg_bcr3, + dbg_bcr4, + dbg_bcr5, + dbg_bcr6, + dbg_bcr7, + dbg_bcr8, + dbg_bcr9, + dbg_bcr10, + dbg_bcr11, + dbg_bcr12, + dbg_bcr13, + dbg_bcr14, + dbg_bcr15, + + dbg_wvr0, + dbg_wvr1, + dbg_wvr2, + dbg_wvr3, + dbg_wvr4, + dbg_wvr5, + dbg_wvr6, + dbg_wvr7, + dbg_wvr8, + dbg_wvr9, + dbg_wvr10, + dbg_wvr11, + dbg_wvr12, + dbg_wvr13, + dbg_wvr14, + dbg_wvr15, + + dbg_wcr0, + dbg_wcr1, + dbg_wcr2, + dbg_wcr3, + dbg_wcr4, + dbg_wcr5, + dbg_wcr6, + dbg_wcr7, + dbg_wcr8, + dbg_wcr9, + dbg_wcr10, + dbg_wcr11, + dbg_wcr12, + dbg_wcr13, + dbg_wcr14, + dbg_wcr15, + + k_num_registers +}; + + +RegisterContextDarwin_arm::RegisterContextDarwin_arm(Thread &thread, uint32_t concrete_frame_idx) : + RegisterContext(thread, concrete_frame_idx), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; i<kNumErrors; i++) + { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextDarwin_arm::~RegisterContextDarwin_arm() +{ +} + + +#define GPR_OFFSET(idx) ((idx) * 4) +#define FPU_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextDarwin_arm::GPR)) +#define EXC_OFFSET(idx) ((idx) * 4 + sizeof (RegisterContextDarwin_arm::GPR) + sizeof (RegisterContextDarwin_arm::FPU)) +#define DBG_OFFSET(reg) ((LLVM_EXTENSION offsetof (RegisterContextDarwin_arm::DBG, reg) + sizeof (RegisterContextDarwin_arm::GPR) + sizeof (RegisterContextDarwin_arm::FPU) + sizeof (RegisterContextDarwin_arm::EXC))) + +#define DEFINE_DBG(reg, i) #reg, NULL, sizeof(((RegisterContextDarwin_arm::DBG *)NULL)->reg[i]), DBG_OFFSET(reg[i]), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, dbg_##reg##i }, NULL, NULL +#define REG_CONTEXT_SIZE (sizeof (RegisterContextDarwin_arm::GPR) + sizeof (RegisterContextDarwin_arm::FPU) + sizeof (RegisterContextDarwin_arm::EXC)) + +static RegisterInfo g_register_infos[] = { +// General purpose registers +// NAME ALT SZ OFFSET ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS +// ====== ======= == ============= ============= ============ =============== =============== ========================= ===================== ============= ========== =============== +{ "r0", NULL, 4, GPR_OFFSET(0), eEncodingUint, eFormatHex, { gcc_r0, dwarf_r0, LLDB_INVALID_REGNUM, gdb_arm_r0, gpr_r0 }, NULL, NULL}, +{ "r1", NULL, 4, GPR_OFFSET(1), eEncodingUint, eFormatHex, { gcc_r1, dwarf_r1, LLDB_INVALID_REGNUM, gdb_arm_r1, gpr_r1 }, NULL, NULL}, +{ "r2", NULL, 4, GPR_OFFSET(2), eEncodingUint, eFormatHex, { gcc_r2, dwarf_r2, LLDB_INVALID_REGNUM, gdb_arm_r2, gpr_r2 }, NULL, NULL}, +{ "r3", NULL, 4, GPR_OFFSET(3), eEncodingUint, eFormatHex, { gcc_r3, dwarf_r3, LLDB_INVALID_REGNUM, gdb_arm_r3, gpr_r3 }, NULL, NULL}, +{ "r4", NULL, 4, GPR_OFFSET(4), eEncodingUint, eFormatHex, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM, gdb_arm_r4, gpr_r4 }, NULL, NULL}, +{ "r5", NULL, 4, GPR_OFFSET(5), eEncodingUint, eFormatHex, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM, gdb_arm_r5, gpr_r5 }, NULL, NULL}, +{ "r6", NULL, 4, GPR_OFFSET(6), eEncodingUint, eFormatHex, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM, gdb_arm_r6, gpr_r6 }, NULL, NULL}, +{ "r7", NULL, 4, GPR_OFFSET(7), eEncodingUint, eFormatHex, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, gdb_arm_r7, gpr_r7 }, NULL, NULL}, +{ "r8", NULL, 4, GPR_OFFSET(8), eEncodingUint, eFormatHex, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM, gdb_arm_r8, gpr_r8 }, NULL, NULL}, +{ "r9", NULL, 4, GPR_OFFSET(9), eEncodingUint, eFormatHex, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM, gdb_arm_r9, gpr_r9 }, NULL, NULL}, +{ "r10", NULL, 4, GPR_OFFSET(10), eEncodingUint, eFormatHex, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM, gdb_arm_r10, gpr_r10 }, NULL, NULL}, +{ "r11", NULL, 4, GPR_OFFSET(11), eEncodingUint, eFormatHex, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM, gdb_arm_r11, gpr_r11 }, NULL, NULL}, +{ "r12", NULL, 4, GPR_OFFSET(12), eEncodingUint, eFormatHex, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM, gdb_arm_r12, gpr_r12 }, NULL, NULL}, +{ "sp", "r13", 4, GPR_OFFSET(13), eEncodingUint, eFormatHex, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, gdb_arm_sp, gpr_sp }, NULL, NULL}, +{ "lr", "r14", 4, GPR_OFFSET(14), eEncodingUint, eFormatHex, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, gdb_arm_lr, gpr_lr }, NULL, NULL}, +{ "pc", "r15", 4, GPR_OFFSET(15), eEncodingUint, eFormatHex, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, gdb_arm_pc, gpr_pc }, NULL, NULL}, +{ "cpsr", "psr", 4, GPR_OFFSET(16), eEncodingUint, eFormatHex, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, gdb_arm_cpsr, gpr_cpsr }, NULL, NULL}, + +{ "s0", NULL, 4, FPU_OFFSET(0), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, gdb_arm_s0, fpu_s0 }, NULL, NULL}, +{ "s1", NULL, 4, FPU_OFFSET(1), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, gdb_arm_s1, fpu_s1 }, NULL, NULL}, +{ "s2", NULL, 4, FPU_OFFSET(2), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, gdb_arm_s2, fpu_s2 }, NULL, NULL}, +{ "s3", NULL, 4, FPU_OFFSET(3), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, gdb_arm_s3, fpu_s3 }, NULL, NULL}, +{ "s4", NULL, 4, FPU_OFFSET(4), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, gdb_arm_s4, fpu_s4 }, NULL, NULL}, +{ "s5", NULL, 4, FPU_OFFSET(5), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, gdb_arm_s5, fpu_s5 }, NULL, NULL}, +{ "s6", NULL, 4, FPU_OFFSET(6), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, gdb_arm_s6, fpu_s6 }, NULL, NULL}, +{ "s7", NULL, 4, FPU_OFFSET(7), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, gdb_arm_s7, fpu_s7 }, NULL, NULL}, +{ "s8", NULL, 4, FPU_OFFSET(8), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, gdb_arm_s8, fpu_s8 }, NULL, NULL}, +{ "s9", NULL, 4, FPU_OFFSET(9), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, gdb_arm_s9, fpu_s9 }, NULL, NULL}, +{ "s10", NULL, 4, FPU_OFFSET(10), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, gdb_arm_s10, fpu_s10 }, NULL, NULL}, +{ "s11", NULL, 4, FPU_OFFSET(11), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, gdb_arm_s11, fpu_s11 }, NULL, NULL}, +{ "s12", NULL, 4, FPU_OFFSET(12), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, gdb_arm_s12, fpu_s12 }, NULL, NULL}, +{ "s13", NULL, 4, FPU_OFFSET(13), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, gdb_arm_s13, fpu_s13 }, NULL, NULL}, +{ "s14", NULL, 4, FPU_OFFSET(14), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, gdb_arm_s14, fpu_s14 }, NULL, NULL}, +{ "s15", NULL, 4, FPU_OFFSET(15), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, gdb_arm_s15, fpu_s15 }, NULL, NULL}, +{ "s16", NULL, 4, FPU_OFFSET(16), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, gdb_arm_s16, fpu_s16 }, NULL, NULL}, +{ "s17", NULL, 4, FPU_OFFSET(17), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, gdb_arm_s17, fpu_s17 }, NULL, NULL}, +{ "s18", NULL, 4, FPU_OFFSET(18), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, gdb_arm_s18, fpu_s18 }, NULL, NULL}, +{ "s19", NULL, 4, FPU_OFFSET(19), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, gdb_arm_s19, fpu_s19 }, NULL, NULL}, +{ "s20", NULL, 4, FPU_OFFSET(20), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, gdb_arm_s20, fpu_s20 }, NULL, NULL}, +{ "s21", NULL, 4, FPU_OFFSET(21), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, gdb_arm_s21, fpu_s21 }, NULL, NULL}, +{ "s22", NULL, 4, FPU_OFFSET(22), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, gdb_arm_s22, fpu_s22 }, NULL, NULL}, +{ "s23", NULL, 4, FPU_OFFSET(23), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, gdb_arm_s23, fpu_s23 }, NULL, NULL}, +{ "s24", NULL, 4, FPU_OFFSET(24), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, gdb_arm_s24, fpu_s24 }, NULL, NULL}, +{ "s25", NULL, 4, FPU_OFFSET(25), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, gdb_arm_s25, fpu_s25 }, NULL, NULL}, +{ "s26", NULL, 4, FPU_OFFSET(26), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, gdb_arm_s26, fpu_s26 }, NULL, NULL}, +{ "s27", NULL, 4, FPU_OFFSET(27), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, gdb_arm_s27, fpu_s27 }, NULL, NULL}, +{ "s28", NULL, 4, FPU_OFFSET(28), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, gdb_arm_s28, fpu_s28 }, NULL, NULL}, +{ "s29", NULL, 4, FPU_OFFSET(29), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, gdb_arm_s29, fpu_s29 }, NULL, NULL}, +{ "s30", NULL, 4, FPU_OFFSET(30), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, gdb_arm_s30, fpu_s30 }, NULL, NULL}, +{ "s31", NULL, 4, FPU_OFFSET(31), eEncodingIEEE754,eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, gdb_arm_s31, fpu_s31 }, NULL, NULL}, +{ "fpscr", NULL, 4, FPU_OFFSET(32), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, gdb_arm_fpscr, fpu_fpscr }, NULL, NULL}, + +{ "exception",NULL, 4, EXC_OFFSET(0), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_exception }, NULL, NULL}, +{ "fsr", NULL, 4, EXC_OFFSET(1), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_fsr }, NULL, NULL}, +{ "far", NULL, 4, EXC_OFFSET(2), eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, exc_far }, NULL, NULL}, + +{ DEFINE_DBG (bvr, 0) }, +{ DEFINE_DBG (bvr, 1) }, +{ DEFINE_DBG (bvr, 2) }, +{ DEFINE_DBG (bvr, 3) }, +{ DEFINE_DBG (bvr, 4) }, +{ DEFINE_DBG (bvr, 5) }, +{ DEFINE_DBG (bvr, 6) }, +{ DEFINE_DBG (bvr, 7) }, +{ DEFINE_DBG (bvr, 8) }, +{ DEFINE_DBG (bvr, 9) }, +{ DEFINE_DBG (bvr, 10) }, +{ DEFINE_DBG (bvr, 11) }, +{ DEFINE_DBG (bvr, 12) }, +{ DEFINE_DBG (bvr, 13) }, +{ DEFINE_DBG (bvr, 14) }, +{ DEFINE_DBG (bvr, 15) }, + +{ DEFINE_DBG (bcr, 0) }, +{ DEFINE_DBG (bcr, 1) }, +{ DEFINE_DBG (bcr, 2) }, +{ DEFINE_DBG (bcr, 3) }, +{ DEFINE_DBG (bcr, 4) }, +{ DEFINE_DBG (bcr, 5) }, +{ DEFINE_DBG (bcr, 6) }, +{ DEFINE_DBG (bcr, 7) }, +{ DEFINE_DBG (bcr, 8) }, +{ DEFINE_DBG (bcr, 9) }, +{ DEFINE_DBG (bcr, 10) }, +{ DEFINE_DBG (bcr, 11) }, +{ DEFINE_DBG (bcr, 12) }, +{ DEFINE_DBG (bcr, 13) }, +{ DEFINE_DBG (bcr, 14) }, +{ DEFINE_DBG (bcr, 15) }, + +{ DEFINE_DBG (wvr, 0) }, +{ DEFINE_DBG (wvr, 1) }, +{ DEFINE_DBG (wvr, 2) }, +{ DEFINE_DBG (wvr, 3) }, +{ DEFINE_DBG (wvr, 4) }, +{ DEFINE_DBG (wvr, 5) }, +{ DEFINE_DBG (wvr, 6) }, +{ DEFINE_DBG (wvr, 7) }, +{ DEFINE_DBG (wvr, 8) }, +{ DEFINE_DBG (wvr, 9) }, +{ DEFINE_DBG (wvr, 10) }, +{ DEFINE_DBG (wvr, 11) }, +{ DEFINE_DBG (wvr, 12) }, +{ DEFINE_DBG (wvr, 13) }, +{ DEFINE_DBG (wvr, 14) }, +{ DEFINE_DBG (wvr, 15) }, + +{ DEFINE_DBG (wcr, 0) }, +{ DEFINE_DBG (wcr, 1) }, +{ DEFINE_DBG (wcr, 2) }, +{ DEFINE_DBG (wcr, 3) }, +{ DEFINE_DBG (wcr, 4) }, +{ DEFINE_DBG (wcr, 5) }, +{ DEFINE_DBG (wcr, 6) }, +{ DEFINE_DBG (wcr, 7) }, +{ DEFINE_DBG (wcr, 8) }, +{ DEFINE_DBG (wcr, 9) }, +{ DEFINE_DBG (wcr, 10) }, +{ DEFINE_DBG (wcr, 11) }, +{ DEFINE_DBG (wcr, 12) }, +{ DEFINE_DBG (wcr, 13) }, +{ DEFINE_DBG (wcr, 14) }, +{ DEFINE_DBG (wcr, 15) } +}; + +// General purpose registers +static uint32_t +g_gpr_regnums[] = +{ + gpr_r0, + gpr_r1, + gpr_r2, + gpr_r3, + gpr_r4, + gpr_r5, + gpr_r6, + gpr_r7, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_sp, + gpr_lr, + gpr_pc, + gpr_cpsr +}; + +// Floating point registers +static uint32_t +g_fpu_regnums[] = +{ + fpu_s0, + fpu_s1, + fpu_s2, + fpu_s3, + fpu_s4, + fpu_s5, + fpu_s6, + fpu_s7, + fpu_s8, + fpu_s9, + fpu_s10, + fpu_s11, + fpu_s12, + fpu_s13, + fpu_s14, + fpu_s15, + fpu_s16, + fpu_s17, + fpu_s18, + fpu_s19, + fpu_s20, + fpu_s21, + fpu_s22, + fpu_s23, + fpu_s24, + fpu_s25, + fpu_s26, + fpu_s27, + fpu_s28, + fpu_s29, + fpu_s30, + fpu_s31, + fpu_fpscr, +}; + +// Exception registers + +static uint32_t +g_exc_regnums[] = +{ + exc_exception, + exc_fsr, + exc_far, +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + +void +RegisterContextDarwin_arm::InvalidateAllRegisters () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextDarwin_arm::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextDarwin_arm::GetRegisterInfoAtIndex (size_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + +size_t +RegisterContextDarwin_arm::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const RegisterInfo * +RegisterContextDarwin_arm::GetRegisterInfos () +{ + return g_register_infos; +} + + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextDarwin_arm::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextDarwin_arm::GetRegisterSet (size_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + + +//---------------------------------------------------------------------- +// Register information defintions for 32 bit i386. +//---------------------------------------------------------------------- +int +RegisterContextDarwin_arm::GetSetForNativeRegNum (int reg) +{ + if (reg < fpu_s0) + return GPRRegSet; + else if (reg < exc_exception) + return FPURegSet; + else if (reg < k_num_registers) + return EXCRegSet; + return -1; +} + +int +RegisterContextDarwin_arm::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr)); + } + return GetError(GPRRegSet, Read); +} + +int +RegisterContextDarwin_arm::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu)); + } + return GetError(FPURegSet, Read); +} + +int +RegisterContextDarwin_arm::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadEXC(GetThreadID(), set, exc)); + } + return GetError(EXCRegSet, Read); +} + +int +RegisterContextDarwin_arm::ReadDBG (bool force) +{ + int set = DBGRegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadDBG(GetThreadID(), set, dbg)); + } + return GetError(DBGRegSet, Read); +} + +int +RegisterContextDarwin_arm::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, DoWriteGPR(GetThreadID(), set, gpr)); + SetError (set, Read, -1); + return GetError(GPRRegSet, Write); +} + +int +RegisterContextDarwin_arm::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, DoWriteFPU(GetThreadID(), set, fpu)); + SetError (set, Read, -1); + return GetError(FPURegSet, Write); +} + +int +RegisterContextDarwin_arm::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, DoWriteEXC(GetThreadID(), set, exc)); + SetError (set, Read, -1); + return GetError(EXCRegSet, Write); +} + +int +RegisterContextDarwin_arm::WriteDBG () +{ + int set = DBGRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return KERN_INVALID_ARGUMENT; + } + SetError (set, Write, DoWriteDBG(GetThreadID(), set, dbg)); + SetError (set, Read, -1); + return GetError(DBGRegSet, Write); +} + + +int +RegisterContextDarwin_arm::ReadRegisterSet (uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR(force); + case FPURegSet: return ReadFPU(force); + case EXCRegSet: return ReadEXC(force); + case DBGRegSet: return ReadDBG(force); + default: break; + } + return KERN_INVALID_ARGUMENT; +} + +int +RegisterContextDarwin_arm::WriteRegisterSet (uint32_t set) +{ + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) + { + switch (set) + { + case GPRRegSet: return WriteGPR(); + case FPURegSet: return WriteFPU(); + case EXCRegSet: return WriteEXC(); + case DBGRegSet: return WriteDBG(); + default: break; + } + } + return KERN_INVALID_ARGUMENT; +} + +void +RegisterContextDarwin_arm::LogDBGRegisters (Log *log, const DBG& dbg) +{ + if (log) + { + for (uint32_t i=0; i<16; i++) + log->Printf("BVR%-2u/BCR%-2u = { 0x%8.8x, 0x%8.8x } WVR%-2u/WCR%-2u = { 0x%8.8x, 0x%8.8x }", + i, i, dbg.bvr[i], dbg.bcr[i], + i, i, dbg.wvr[i], dbg.wcr[i]); + } +} + + +bool +RegisterContextDarwin_arm::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_arm::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + value.SetUInt32 (gpr.r[reg - gpr_r0]); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + value.SetUInt32 (fpu.floats.s[reg], RegisterValue::eTypeFloat); + break; + + case fpu_fpscr: + value.SetUInt32 (fpu.fpscr); + break; + + case exc_exception: + value.SetUInt32 (exc.exception); + break; + case exc_fsr: + value.SetUInt32 (exc.fsr); + break; + case exc_far: + value.SetUInt32 (exc.far); + break; + + default: + value.SetValueToInvalid(); + return false; + + } + return true; +} + + +bool +RegisterContextDarwin_arm::WriteRegister (const RegisterInfo *reg_info, + const RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != KERN_SUCCESS) + return false; + + switch (reg) + { + case gpr_r0: + case gpr_r1: + case gpr_r2: + case gpr_r3: + case gpr_r4: + case gpr_r5: + case gpr_r6: + case gpr_r7: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_sp: + case gpr_lr: + case gpr_pc: + case gpr_cpsr: + gpr.r[reg - gpr_r0] = value.GetAsUInt32(); + break; + + case fpu_s0: + case fpu_s1: + case fpu_s2: + case fpu_s3: + case fpu_s4: + case fpu_s5: + case fpu_s6: + case fpu_s7: + case fpu_s8: + case fpu_s9: + case fpu_s10: + case fpu_s11: + case fpu_s12: + case fpu_s13: + case fpu_s14: + case fpu_s15: + case fpu_s16: + case fpu_s17: + case fpu_s18: + case fpu_s19: + case fpu_s20: + case fpu_s21: + case fpu_s22: + case fpu_s23: + case fpu_s24: + case fpu_s25: + case fpu_s26: + case fpu_s27: + case fpu_s28: + case fpu_s29: + case fpu_s30: + case fpu_s31: + fpu.floats.s[reg] = value.GetAsUInt32(); + break; + + case fpu_fpscr: + fpu.fpscr = value.GetAsUInt32(); + break; + + case exc_exception: + exc.exception = value.GetAsUInt32(); + break; + case exc_fsr: + exc.fsr = value.GetAsUInt32(); + break; + case exc_far: + exc.far = value.GetAsUInt32(); + break; + + default: + return false; + + } + return WriteRegisterSet(set) == KERN_SUCCESS; +} + +bool +RegisterContextDarwin_arm::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == KERN_SUCCESS && + ReadFPU (false) == KERN_SUCCESS && + ReadEXC (false) == KERN_SUCCESS) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextDarwin_arm::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == KERN_SUCCESS) + ++success_count; + if (WriteFPU() == KERN_SUCCESS) + ++success_count; + if (WriteEXC() == KERN_SUCCESS) + ++success_count; + return success_count == 3; + } + return false; +} + +uint32_t +RegisterContextDarwin_arm::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_pc; + case LLDB_REGNUM_GENERIC_SP: return gpr_sp; + case LLDB_REGNUM_GENERIC_FP: return gpr_r7; + case LLDB_REGNUM_GENERIC_RA: return gpr_lr; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_cpsr; + default: + break; + } + } + else if (kind == eRegisterKindDWARF) + { + switch (reg) + { + case dwarf_r0: return gpr_r0; + case dwarf_r1: return gpr_r1; + case dwarf_r2: return gpr_r2; + case dwarf_r3: return gpr_r3; + case dwarf_r4: return gpr_r4; + case dwarf_r5: return gpr_r5; + case dwarf_r6: return gpr_r6; + case dwarf_r7: return gpr_r7; + case dwarf_r8: return gpr_r8; + case dwarf_r9: return gpr_r9; + case dwarf_r10: return gpr_r10; + case dwarf_r11: return gpr_r11; + case dwarf_r12: return gpr_r12; + case dwarf_sp: return gpr_sp; + case dwarf_lr: return gpr_lr; + case dwarf_pc: return gpr_pc; + case dwarf_spsr: return gpr_cpsr; + + case dwarf_s0: return fpu_s0; + case dwarf_s1: return fpu_s1; + case dwarf_s2: return fpu_s2; + case dwarf_s3: return fpu_s3; + case dwarf_s4: return fpu_s4; + case dwarf_s5: return fpu_s5; + case dwarf_s6: return fpu_s6; + case dwarf_s7: return fpu_s7; + case dwarf_s8: return fpu_s8; + case dwarf_s9: return fpu_s9; + case dwarf_s10: return fpu_s10; + case dwarf_s11: return fpu_s11; + case dwarf_s12: return fpu_s12; + case dwarf_s13: return fpu_s13; + case dwarf_s14: return fpu_s14; + case dwarf_s15: return fpu_s15; + case dwarf_s16: return fpu_s16; + case dwarf_s17: return fpu_s17; + case dwarf_s18: return fpu_s18; + case dwarf_s19: return fpu_s19; + case dwarf_s20: return fpu_s20; + case dwarf_s21: return fpu_s21; + case dwarf_s22: return fpu_s22; + case dwarf_s23: return fpu_s23; + case dwarf_s24: return fpu_s24; + case dwarf_s25: return fpu_s25; + case dwarf_s26: return fpu_s26; + case dwarf_s27: return fpu_s27; + case dwarf_s28: return fpu_s28; + case dwarf_s29: return fpu_s29; + case dwarf_s30: return fpu_s30; + case dwarf_s31: return fpu_s31; + + default: + break; + } + } + else if (kind == eRegisterKindGCC) + { + switch (reg) + { + case gcc_r0: return gpr_r0; + case gcc_r1: return gpr_r1; + case gcc_r2: return gpr_r2; + case gcc_r3: return gpr_r3; + case gcc_r4: return gpr_r4; + case gcc_r5: return gpr_r5; + case gcc_r6: return gpr_r6; + case gcc_r7: return gpr_r7; + case gcc_r8: return gpr_r8; + case gcc_r9: return gpr_r9; + case gcc_r10: return gpr_r10; + case gcc_r11: return gpr_r11; + case gcc_r12: return gpr_r12; + case gcc_sp: return gpr_sp; + case gcc_lr: return gpr_lr; + case gcc_pc: return gpr_pc; + case gcc_cpsr: return gpr_cpsr; + } + } + else if (kind == eRegisterKindLLDB) + { + return reg; + } + return LLDB_INVALID_REGNUM; +} + + +uint32_t +RegisterContextDarwin_arm::NumSupportedHardwareBreakpoints () +{ +#if defined (__arm__) + // Set the init value to something that will let us know that we need to + // autodetect how many breakpoints are supported dynamically... + static uint32_t g_num_supported_hw_breakpoints = UINT32_MAX; + if (g_num_supported_hw_breakpoints == UINT32_MAX) + { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_breakpoints = 0; + + uint32_t register_DBGDIDR; + + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + g_num_supported_hw_breakpoints = Bits32 (register_DBGDIDR, 27, 24); + // Zero is reserved for the BRP count, so don't increment it if it is zero + if (g_num_supported_hw_breakpoints > 0) + g_num_supported_hw_breakpoints++; +// if (log) log->Printf ("DBGDIDR=0x%8.8x (number BRP pairs = %u)", register_DBGDIDR, g_num_supported_hw_breakpoints); + + } + return g_num_supported_hw_breakpoints; +#else + // TODO: figure out remote case here! + return 6; +#endif +} + +uint32_t +RegisterContextDarwin_arm::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + // Make sure our address isn't bogus + if (addr & 1) + return LLDB_INVALID_INDEX32; + + int kret = ReadDBG (false); + + if (kret == KERN_SUCCESS) + { + const uint32_t num_hw_breakpoints = NumSupportedHardwareBreakpoints(); + uint32_t i; + for (i=0; i<num_hw_breakpoints; ++i) + { + if ((dbg.bcr[i] & BCR_ENABLE) == 0) + break; // We found an available hw breakpoint slot (in i) + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_breakpoints) + { + // Make sure bits 1:0 are clear in our address + dbg.bvr[i] = addr & ~((lldb::addr_t)3); + + if (size == 2 || addr & 2) + { + uint32_t byte_addr_select = (addr & 2) ? BAS_IMVA_2_3 : BAS_IMVA_0_1; + + // We have a thumb breakpoint + // We have an ARM breakpoint + dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch + byte_addr_select | // Set the correct byte address select so we only trigger on the correct opcode + S_USER | // Which modes should this breakpoint stop in? + BCR_ENABLE; // Enable this hardware breakpoint +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (Thumb)", +// addr, +// size, +// i, +// i, +// dbg.bvr[i], +// dbg.bcr[i]); + } + else if (size == 4) + { + // We have an ARM breakpoint + dbg.bcr[i] = BCR_M_IMVA_MATCH | // Stop on address mismatch + BAS_IMVA_ALL | // Stop on any of the four bytes following the IMVA + S_USER | // Which modes should this breakpoint stop in? + BCR_ENABLE; // Enable this hardware breakpoint +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareBreakpoint( addr = %8.8p, size = %u ) - BVR%u/BCR%u = 0x%8.8x / 0x%8.8x (ARM)", +// addr, +// size, +// i, +// i, +// dbg.bvr[i], +// dbg.bcr[i]); + } + + kret = WriteDBG(); +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareBreakpoint() WriteDBG() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } +// else +// { +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareBreakpoint(addr = %8.8p, size = %u) => all hardware breakpoint resources are being used.", addr, size); +// } + } + + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContextDarwin_arm::ClearHardwareBreakpoint (uint32_t hw_index) +{ + int kret = ReadDBG (false); + + const uint32_t num_hw_points = NumSupportedHardwareBreakpoints(); + if (kret == KERN_SUCCESS) + { + if (hw_index < num_hw_points) + { + dbg.bcr[hw_index] = 0; +// if (log) log->Printf ("RegisterContextDarwin_arm::SetHardwareBreakpoint( %u ) - BVR%u = 0x%8.8x BCR%u = 0x%8.8x", +// hw_index, +// hw_index, +// dbg.bvr[hw_index], +// hw_index, +// dbg.bcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + +uint32_t +RegisterContextDarwin_arm::NumSupportedHardwareWatchpoints () +{ +#if defined (__arm__) + // Set the init value to something that will let us know that we need to + // autodetect how many watchpoints are supported dynamically... + static uint32_t g_num_supported_hw_watchpoints = UINT32_MAX; + if (g_num_supported_hw_watchpoints == UINT32_MAX) + { + // Set this to zero in case we can't tell if there are any HW breakpoints + g_num_supported_hw_watchpoints = 0; + + uint32_t register_DBGDIDR; + asm("mrc p14, 0, %0, c0, c0, 0" : "=r" (register_DBGDIDR)); + g_num_supported_hw_watchpoints = Bits32 (register_DBGDIDR, 31, 28) + 1; +// if (log) log->Printf ("DBGDIDR=0x%8.8x (number WRP pairs = %u)", register_DBGDIDR, g_num_supported_hw_watchpoints); + } + return g_num_supported_hw_watchpoints; +#else + // TODO: figure out remote case here! + return 2; +#endif +} + + +uint32_t +RegisterContextDarwin_arm::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write) +{ +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(addr = %8.8p, size = %u, read = %u, write = %u)", addr, size, read, write); + + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + // Can't watch zero bytes + if (size == 0) + return LLDB_INVALID_INDEX32; + + // We must watch for either read or write + if (read == false && write == false) + return LLDB_INVALID_INDEX32; + + // Can't watch more than 4 bytes per WVR/WCR pair + if (size > 4) + return LLDB_INVALID_INDEX32; + + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair. Since we have at most so we can only watch + // until the next 4 byte boundary and we need to make sure we can properly + // encode this. + uint32_t addr_word_offset = addr % 4; +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() - addr_word_offset = 0x%8.8x", addr_word_offset); + + uint32_t byte_mask = ((1u << size) - 1u) << addr_word_offset; +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() - byte_mask = 0x%8.8x", byte_mask); + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Read the debug state + int kret = ReadDBG (false); + + if (kret == KERN_SUCCESS) + { + // Check to make sure we have the needed hardware support + uint32_t i = 0; + + for (i=0; i<num_hw_watchpoints; ++i) + { + if ((dbg.wcr[i] & WCR_ENABLE) == 0) + break; // We found an available hw breakpoint slot (in i) + } + + // See if we found an available hw breakpoint slot above + if (i < num_hw_watchpoints) + { + // Make the byte_mask into a valid Byte Address Select mask + uint32_t byte_address_select = byte_mask << 5; + // Make sure bits 1:0 are clear in our address + dbg.wvr[i] = addr & ~((lldb::addr_t)3); + dbg.wcr[i] = byte_address_select | // Which bytes that follow the IMVA that we will watch + S_USER | // Stop only in user mode + (read ? WCR_LOAD : 0) | // Stop on read access? + (write ? WCR_STORE : 0) | // Stop on write access? + WCR_ENABLE; // Enable this watchpoint; + + kret = WriteDBG(); +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint() WriteDBG() => 0x%8.8x.", kret); + + if (kret == KERN_SUCCESS) + return i; + } + else + { +// if (log) log->Printf ("RegisterContextDarwin_arm::EnableHardwareWatchpoint(): All hardware resources (%u) are in use.", num_hw_watchpoints); + } + } + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContextDarwin_arm::ClearHardwareWatchpoint (uint32_t hw_index) +{ + int kret = ReadDBG (false); + + const uint32_t num_hw_points = NumSupportedHardwareWatchpoints(); + if (kret == KERN_SUCCESS) + { + if (hw_index < num_hw_points) + { + dbg.wcr[hw_index] = 0; +// if (log) log->Printf ("RegisterContextDarwin_arm::ClearHardwareWatchpoint( %u ) - WVR%u = 0x%8.8x WCR%u = 0x%8.8x", +// hw_index, +// hw_index, +// dbg.wvr[hw_index], +// hw_index, +// dbg.wcr[hw_index]); + + kret = WriteDBG(); + + if (kret == KERN_SUCCESS) + return true; + } + } + return false; +} + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h new file mode 100644 index 000000000000..0bf204f57c80 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextDarwin_arm.h @@ -0,0 +1,333 @@ +//===-- RegisterContextDarwin_arm.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextDarwin_arm_h_ +#define liblldb_RegisterContextDarwin_arm_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +// BCR address match type +#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21)) +#define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21)) +#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21)) +#define BCR_M_RESERVED ((uint32_t)(3u << 21)) + +// Link a BVR/BCR or WVR/WCR pair to another +#define E_ENABLE_LINKING ((uint32_t)(1u << 20)) + +// Byte Address Select +#define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5)) +#define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6)) +#define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7)) +#define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8)) +#define BAS_IMVA_0_1 ((uint32_t)(3u << 5)) +#define BAS_IMVA_2_3 ((uint32_t)(3u << 7)) +#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5)) + +// Break only in privileged or user mode +#define S_RSVD ((uint32_t)(0u << 1)) +#define S_PRIV ((uint32_t)(1u << 1)) +#define S_USER ((uint32_t)(2u << 1)) +#define S_PRIV_USER ((S_PRIV) | (S_USER)) + +#define BCR_ENABLE ((uint32_t)(1u)) +#define WCR_ENABLE ((uint32_t)(1u)) + +// Watchpoint load/store +#define WCR_LOAD ((uint32_t)(1u << 3)) +#define WCR_STORE ((uint32_t)(1u << 4)) + +class RegisterContextDarwin_arm : public lldb_private::RegisterContext +{ +public: + + RegisterContextDarwin_arm(lldb_private::Thread &thread, uint32_t concrete_frame_idx); + + virtual + ~RegisterContextDarwin_arm(); + + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb_private::RegisterSet * + GetRegisterSet (size_t set); + + virtual bool + ReadRegister (const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value); + + virtual bool + WriteRegister (const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + virtual uint32_t + NumSupportedHardwareBreakpoints (); + + virtual uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size); + + virtual bool + ClearHardwareBreakpoint (uint32_t hw_idx); + + virtual uint32_t + NumSupportedHardwareWatchpoints (); + + virtual uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write); + + virtual bool + ClearHardwareWatchpoint (uint32_t hw_index); + + struct GPR + { + uint32_t r[16]; // R0-R15 + uint32_t cpsr; // CPSR + }; + + + struct QReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + union { + uint32_t s[32]; + uint64_t d[32]; + QReg q[16]; // the 128-bit NEON registers + } floats; + uint32_t fpscr; + }; + +// struct NeonReg +// { +// uint8_t bytes[16]; +// }; +// +// struct VFPv3 +// { +// union { +// uint32_t s[32]; +// uint64_t d[32]; +// NeonReg q[16]; +// } v3; +// uint32_t fpscr; +// }; + + struct EXC + { + uint32_t exception; + uint32_t fsr; /* Fault status */ + uint32_t far; /* Virtual Fault Address */ + }; + + struct DBG + { + uint32_t bvr[16]; + uint32_t bcr[16]; + uint32_t wvr[16]; + uint32_t wcr[16]; + }; + + static void + LogDBGRegisters (lldb_private::Log *log, const DBG& dbg); + +protected: + + enum + { + GPRRegSet = 1, // ARM_THREAD_STATE + FPURegSet = 2, // ARM_VFP_STATE + EXCRegSet = 3, // ARM_EXCEPTION_STATE + DBGRegSet = 4 // ARM_DEBUG_STATE + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t), + DBGWordCount = sizeof(DBG)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + DBG dbg; + int gpr_errs[2]; // Read/Write errors + int fpu_errs[2]; // Read/Write errors + int exc_errs[2]; // Read/Write errors + int dbg_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + int + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + case DBGRegSet: return dbg_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, int err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + case DBGRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == 0; + } + + int + ReadGPR (bool force); + + int + ReadFPU (bool force); + + int + ReadEXC (bool force); + + int + ReadDBG (bool force); + + int + WriteGPR (); + + int + WriteFPU (); + + int + WriteEXC (); + + int + WriteDBG (); + + + // Subclasses override these to do the actual reading. + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) + { + return -1; + } + + virtual int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) = 0; + + virtual int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) = 0; + + virtual int + DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) = 0; + + virtual int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) = 0; + + virtual int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) = 0; + + virtual int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) = 0; + + virtual int + DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) = 0; + + int + ReadRegisterSet (uint32_t set, bool force); + + int + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb_private::RegisterInfo * + GetRegisterInfos (); +}; + +#endif // liblldb_RegisterContextDarwin_arm_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp b/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp new file mode 100644 index 000000000000..a94d1f538a28 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp @@ -0,0 +1,980 @@ +//===-- RegisterContextDarwin_i386.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +#include <stddef.h> // offsetof + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Host/Endian.h" +#include "llvm/Support/Compiler.h" + +// Support building against older versions of LLVM, this macro was added +// recently. +#ifndef LLVM_EXTENSION +#define LLVM_EXTENSION +#endif + +// Project includes +#include "RegisterContextDarwin_i386.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_eax = 0, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs, + + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + + exc_trapno, + exc_err, + exc_faultvaddr, + + k_num_registers, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp +}; + +enum +{ + gcc_eax = 0, + gcc_ecx, + gcc_edx, + gcc_ebx, + gcc_ebp, + gcc_esp, + gcc_esi, + gcc_edi, + gcc_eip, + gcc_eflags +}; + +enum +{ + dwarf_eax = 0, + dwarf_ecx, + dwarf_edx, + dwarf_ebx, + dwarf_esp, + dwarf_ebp, + dwarf_esi, + dwarf_edi, + dwarf_eip, + dwarf_eflags, + dwarf_stmm0 = 11, + dwarf_stmm1, + dwarf_stmm2, + dwarf_stmm3, + dwarf_stmm4, + dwarf_stmm5, + dwarf_stmm6, + dwarf_stmm7, + dwarf_xmm0 = 21, + dwarf_xmm1, + dwarf_xmm2, + dwarf_xmm3, + dwarf_xmm4, + dwarf_xmm5, + dwarf_xmm6, + dwarf_xmm7 +}; + +enum +{ + gdb_eax = 0, + gdb_ecx = 1, + gdb_edx = 2, + gdb_ebx = 3, + gdb_esp = 4, + gdb_ebp = 5, + gdb_esi = 6, + gdb_edi = 7, + gdb_eip = 8, + gdb_eflags = 9, + gdb_cs = 10, + gdb_ss = 11, + gdb_ds = 12, + gdb_es = 13, + gdb_fs = 14, + gdb_gs = 15, + gdb_stmm0 = 16, + gdb_stmm1 = 17, + gdb_stmm2 = 18, + gdb_stmm3 = 19, + gdb_stmm4 = 20, + gdb_stmm5 = 21, + gdb_stmm6 = 22, + gdb_stmm7 = 23, + gdb_fctrl = 24, gdb_fcw = gdb_fctrl, + gdb_fstat = 25, gdb_fsw = gdb_fstat, + gdb_ftag = 26, gdb_ftw = gdb_ftag, + gdb_fiseg = 27, gdb_fpu_cs = gdb_fiseg, + gdb_fioff = 28, gdb_ip = gdb_fioff, + gdb_foseg = 29, gdb_fpu_ds = gdb_foseg, + gdb_fooff = 30, gdb_dp = gdb_fooff, + gdb_fop = 31, + gdb_xmm0 = 32, + gdb_xmm1 = 33, + gdb_xmm2 = 34, + gdb_xmm3 = 35, + gdb_xmm4 = 36, + gdb_xmm5 = 37, + gdb_xmm6 = 38, + gdb_xmm7 = 39, + gdb_mxcsr = 40, + gdb_mm0 = 41, + gdb_mm1 = 42, + gdb_mm2 = 43, + gdb_mm3 = 44, + gdb_mm4 = 45, + gdb_mm5 = 46, + gdb_mm6 = 47, + gdb_mm7 = 48 +}; + +RegisterContextDarwin_i386::RegisterContextDarwin_i386 (Thread &thread, uint32_t concrete_frame_idx) : + RegisterContext(thread, concrete_frame_idx), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; i<kNumErrors; i++) + { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextDarwin_i386::~RegisterContextDarwin_i386() +{ +} + + + +#define GPR_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_i386::GPR, reg)) +#define FPU_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_i386::FPU, reg) + sizeof (RegisterContextDarwin_i386::GPR)) +#define EXC_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_i386::EXC, reg) + sizeof (RegisterContextDarwin_i386::GPR) + sizeof (RegisterContextDarwin_i386::FPU)) + +// These macros will auto define the register name, alt name, register size, +// register offset, encoding, format and native register. This ensures that +// the register state structures are defined correctly and have the correct +// sizes and offsets. +#define DEFINE_GPR(reg, alt) #reg, alt, sizeof(((RegisterContextDarwin_i386::GPR *)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex +#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextDarwin_i386::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex +#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextDarwin_i386::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_##reg##i, LLDB_INVALID_REGNUM, gdb_##reg##i, fpu_##reg##i }, NULL, NULL + +#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextDarwin_i386::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex +#define REG_CONTEXT_SIZE (sizeof (RegisterContextDarwin_i386::GPR) + sizeof (RegisterContextDarwin_i386::FPU) + sizeof (RegisterContextDarwin_i386::EXC)) + +static RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff GCC DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS +// =============================== ======================= =================== ========================= ================== ================= ========== =============== + { DEFINE_GPR(eax , NULL) , { gcc_eax , dwarf_eax , LLDB_INVALID_REGNUM , gdb_eax , gpr_eax }, NULL, NULL}, + { DEFINE_GPR(ebx , NULL) , { gcc_ebx , dwarf_ebx , LLDB_INVALID_REGNUM , gdb_ebx , gpr_ebx }, NULL, NULL}, + { DEFINE_GPR(ecx , NULL) , { gcc_ecx , dwarf_ecx , LLDB_INVALID_REGNUM , gdb_ecx , gpr_ecx }, NULL, NULL}, + { DEFINE_GPR(edx , NULL) , { gcc_edx , dwarf_edx , LLDB_INVALID_REGNUM , gdb_edx , gpr_edx }, NULL, NULL}, + { DEFINE_GPR(edi , NULL) , { gcc_edi , dwarf_edi , LLDB_INVALID_REGNUM , gdb_edi , gpr_edi }, NULL, NULL}, + { DEFINE_GPR(esi , NULL) , { gcc_esi , dwarf_esi , LLDB_INVALID_REGNUM , gdb_esi , gpr_esi }, NULL, NULL}, + { DEFINE_GPR(ebp , "fp") , { gcc_ebp , dwarf_ebp , LLDB_REGNUM_GENERIC_FP , gdb_ebp , gpr_ebp }, NULL, NULL}, + { DEFINE_GPR(esp , "sp") , { gcc_esp , dwarf_esp , LLDB_REGNUM_GENERIC_SP , gdb_esp , gpr_esp }, NULL, NULL}, + { DEFINE_GPR(ss , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ss , gpr_ss }, NULL, NULL}, + { DEFINE_GPR(eflags , "flags") , { gcc_eflags , dwarf_eflags , LLDB_REGNUM_GENERIC_FLAGS , gdb_eflags , gpr_eflags }, NULL, NULL}, + { DEFINE_GPR(eip , "pc") , { gcc_eip , dwarf_eip , LLDB_REGNUM_GENERIC_PC , gdb_eip , gpr_eip }, NULL, NULL}, + { DEFINE_GPR(cs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_cs , gpr_cs }, NULL, NULL}, + { DEFINE_GPR(ds , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ds , gpr_ds }, NULL, NULL}, + { DEFINE_GPR(es , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_es , gpr_es }, NULL, NULL}, + { DEFINE_GPR(fs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fs , gpr_fs }, NULL, NULL}, + { DEFINE_GPR(gs , NULL) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_gs , gpr_gs }, NULL, NULL}, + + { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fcw , fpu_fcw }, NULL, NULL}, + { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fsw , fpu_fsw }, NULL, NULL}, + { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ftw , fpu_ftw }, NULL, NULL}, + { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fop , fpu_fop }, NULL, NULL}, + { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ip , fpu_ip }, NULL, NULL}, + { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_cs , fpu_cs }, NULL, NULL}, + { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_dp , fpu_dp }, NULL, NULL}, + { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_ds , fpu_ds }, NULL, NULL}, + { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_mxcsr , fpu_mxcsr }, NULL, NULL}, + { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, fpu_mxcsrmask}, NULL, NULL}, + { DEFINE_FPU_VECT(stmm,0) }, + { DEFINE_FPU_VECT(stmm,1) }, + { DEFINE_FPU_VECT(stmm,2) }, + { DEFINE_FPU_VECT(stmm,3) }, + { DEFINE_FPU_VECT(stmm,4) }, + { DEFINE_FPU_VECT(stmm,5) }, + { DEFINE_FPU_VECT(stmm,6) }, + { DEFINE_FPU_VECT(stmm,7) }, + { DEFINE_FPU_VECT(xmm,0) }, + { DEFINE_FPU_VECT(xmm,1) }, + { DEFINE_FPU_VECT(xmm,2) }, + { DEFINE_FPU_VECT(xmm,3) }, + { DEFINE_FPU_VECT(xmm,4) }, + { DEFINE_FPU_VECT(xmm,5) }, + { DEFINE_FPU_VECT(xmm,6) }, + { DEFINE_FPU_VECT(xmm,7) }, + + { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_trapno }, NULL, NULL}, + { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_err }, NULL, NULL}, + { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_faultvaddr }, NULL, NULL} +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + +void +RegisterContextDarwin_i386::InvalidateAllRegisters () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextDarwin_i386::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + +const RegisterInfo * +RegisterContextDarwin_i386::GetRegisterInfoAtIndex (size_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + +size_t +RegisterContextDarwin_i386::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const RegisterInfo * +RegisterContextDarwin_i386::GetRegisterInfos () +{ + return g_register_infos; +} + + +// General purpose registers +static uint32_t +g_gpr_regnums[] = +{ + gpr_eax, + gpr_ebx, + gpr_ecx, + gpr_edx, + gpr_edi, + gpr_esi, + gpr_ebp, + gpr_esp, + gpr_ss, + gpr_eflags, + gpr_eip, + gpr_cs, + gpr_ds, + gpr_es, + gpr_fs, + gpr_gs +}; + +// Floating point registers +static uint32_t +g_fpu_regnums[] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7 +}; + +// Exception registers + +static uint32_t +g_exc_regnums[] = +{ + exc_trapno, + exc_err, + exc_faultvaddr +}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextDarwin_i386::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextDarwin_i386::GetRegisterSet (size_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + + +//---------------------------------------------------------------------- +// Register information definitions for 32 bit i386. +//---------------------------------------------------------------------- +int +RegisterContextDarwin_i386::GetSetForNativeRegNum (int reg_num) +{ + if (reg_num < fpu_fcw) + return GPRRegSet; + else if (reg_num < exc_trapno) + return FPURegSet; + else if (reg_num < k_num_registers) + return EXCRegSet; + return -1; +} + + +void +RegisterContextDarwin_i386::LogGPR(Log *log, const char *title) +{ + if (log) + { + if (title) + log->Printf ("%s", title); + for (uint32_t i=0; i<k_num_gpr_registers; i++) + { + uint32_t reg = gpr_eax + i; + log->Printf("%12s = 0x%8.8x", g_register_infos[reg].name, (&gpr.eax)[reg]); + } + } +} + + + +int +RegisterContextDarwin_i386::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr)); + } + return GetError(set, Read); +} + +int +RegisterContextDarwin_i386::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu)); + } + return GetError(set, Read); +} + +int +RegisterContextDarwin_i386::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadEXC(GetThreadID(), set, exc)); + } + return GetError(set, Read); +} + +int +RegisterContextDarwin_i386::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return -1; + } + SetError (set, Write, DoWriteGPR(GetThreadID(), set, gpr)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +int +RegisterContextDarwin_i386::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return -1; + } + SetError (set, Write, DoWriteFPU(GetThreadID(), set, fpu)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +int +RegisterContextDarwin_i386::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return -1; + } + SetError (set, Write, DoWriteEXC(GetThreadID(), set, exc)); + SetError (set, Read, -1); + return GetError(set, Write); +} + +int +RegisterContextDarwin_i386::ReadRegisterSet (uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR(force); + case FPURegSet: return ReadFPU(force); + case EXCRegSet: return ReadEXC(force); + default: break; + } + return -1; +} + +int +RegisterContextDarwin_i386::WriteRegisterSet (uint32_t set) +{ + // Make sure we have a valid context to set. + if (RegisterSetIsCached(set)) + { + switch (set) + { + case GPRRegSet: return WriteGPR(); + case FPURegSet: return WriteFPU(); + case EXCRegSet: return WriteEXC(); + default: break; + } + } + return -1; +} + +bool +RegisterContextDarwin_i386::ReadRegister (const RegisterInfo *reg_info, + RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_i386::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != 0) + return false; + + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + value = (&gpr.eax)[reg - gpr_eax]; + break; + + case fpu_fcw: + value = fpu.fcw; + break; + + case fpu_fsw: + value = fpu.fsw; + break; + + case fpu_ftw: + value = fpu.ftw; + break; + + case fpu_fop: + value = fpu.fop; + break; + + case fpu_ip: + value = fpu.ip; + break; + + case fpu_cs: + value = fpu.cs; + break; + + case fpu_dp: + value = fpu.dp; + break; + + case fpu_ds: + value = fpu.ds; + break; + + case fpu_mxcsr: + value = fpu.mxcsr; + break; + + case fpu_mxcsrmask: + value = fpu.mxcsrmask; + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, + // RegisterContext::ReadRegisterBytes() must be used for these + // registers + //::memcpy (reg_value.value.vector.uint8, fpu.stmm[reg - fpu_stmm0].bytes, 10); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + //::memcpy (reg_value.value.vector.uint8, fpu.xmm[reg - fpu_xmm0].bytes, 16); + return false; + + case exc_trapno: + value = exc.trapno; + break; + + case exc_err: + value = exc.err; + break; + + case exc_faultvaddr: + value = exc.faultvaddr; + break; + + default: + return false; + } + return true; +} + + +bool +RegisterContextDarwin_i386::WriteRegister (const RegisterInfo *reg_info, + const RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != 0) + return false; + + switch (reg) + { + case gpr_eax: + case gpr_ebx: + case gpr_ecx: + case gpr_edx: + case gpr_edi: + case gpr_esi: + case gpr_ebp: + case gpr_esp: + case gpr_ss: + case gpr_eflags: + case gpr_eip: + case gpr_cs: + case gpr_ds: + case gpr_es: + case gpr_fs: + case gpr_gs: + (&gpr.eax)[reg - gpr_eax] = value.GetAsUInt32(); + break; + + case fpu_fcw: + fpu.fcw = value.GetAsUInt16(); + break; + + case fpu_fsw: + fpu.fsw = value.GetAsUInt16(); + break; + + case fpu_ftw: + fpu.ftw = value.GetAsUInt8(); + break; + + case fpu_fop: + fpu.fop = value.GetAsUInt16(); + break; + + case fpu_ip: + fpu.ip = value.GetAsUInt32(); + break; + + case fpu_cs: + fpu.cs = value.GetAsUInt16(); + break; + + case fpu_dp: + fpu.dp = value.GetAsUInt32(); + break; + + case fpu_ds: + fpu.ds = value.GetAsUInt16(); + break; + + case fpu_mxcsr: + fpu.mxcsr = value.GetAsUInt32(); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = value.GetAsUInt32(); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(), value.GetByteSize()); + return false; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + // These values don't fit into scalar types, RegisterContext::ReadRegisterBytes() + // must be used for these registers + ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(), value.GetByteSize()); + return false; + + case exc_trapno: + exc.trapno = value.GetAsUInt32(); + break; + + case exc_err: + exc.err = value.GetAsUInt32(); + break; + + case exc_faultvaddr: + exc.faultvaddr = value.GetAsUInt32(); + break; + + default: + return false; + } + return WriteRegisterSet(set) == 0; +} + +bool +RegisterContextDarwin_i386::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == 0 && + ReadFPU (false) == 0 && + ReadEXC (false) == 0) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextDarwin_i386::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == 0) + ++success_count; + if (WriteFPU() == 0) + ++success_count; + if (WriteEXC() == 0) + ++success_count; + return success_count == 3; + } + return false; +} + + +uint32_t +RegisterContextDarwin_i386::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_eip; + case LLDB_REGNUM_GENERIC_SP: return gpr_esp; + case LLDB_REGNUM_GENERIC_FP: return gpr_ebp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_eflags; + case LLDB_REGNUM_GENERIC_RA: + default: + break; + } + } + else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (reg) + { + case dwarf_eax: return gpr_eax; + case dwarf_ecx: return gpr_ecx; + case dwarf_edx: return gpr_edx; + case dwarf_ebx: return gpr_ebx; + case dwarf_esp: return gpr_esp; + case dwarf_ebp: return gpr_ebp; + case dwarf_esi: return gpr_esi; + case dwarf_edi: return gpr_edi; + case dwarf_eip: return gpr_eip; + case dwarf_eflags: return gpr_eflags; + case dwarf_stmm0: return fpu_stmm0; + case dwarf_stmm1: return fpu_stmm1; + case dwarf_stmm2: return fpu_stmm2; + case dwarf_stmm3: return fpu_stmm3; + case dwarf_stmm4: return fpu_stmm4; + case dwarf_stmm5: return fpu_stmm5; + case dwarf_stmm6: return fpu_stmm6; + case dwarf_stmm7: return fpu_stmm7; + case dwarf_xmm0: return fpu_xmm0; + case dwarf_xmm1: return fpu_xmm1; + case dwarf_xmm2: return fpu_xmm2; + case dwarf_xmm3: return fpu_xmm3; + case dwarf_xmm4: return fpu_xmm4; + case dwarf_xmm5: return fpu_xmm5; + case dwarf_xmm6: return fpu_xmm6; + case dwarf_xmm7: return fpu_xmm7; + default: + break; + } + } + else if (kind == eRegisterKindGDB) + { + switch (reg) + { + case gdb_eax : return gpr_eax; + case gdb_ebx : return gpr_ebx; + case gdb_ecx : return gpr_ecx; + case gdb_edx : return gpr_edx; + case gdb_esi : return gpr_esi; + case gdb_edi : return gpr_edi; + case gdb_ebp : return gpr_ebp; + case gdb_esp : return gpr_esp; + case gdb_eip : return gpr_eip; + case gdb_eflags : return gpr_eflags; + case gdb_cs : return gpr_cs; + case gdb_ss : return gpr_ss; + case gdb_ds : return gpr_ds; + case gdb_es : return gpr_es; + case gdb_fs : return gpr_fs; + case gdb_gs : return gpr_gs; + case gdb_stmm0 : return fpu_stmm0; + case gdb_stmm1 : return fpu_stmm1; + case gdb_stmm2 : return fpu_stmm2; + case gdb_stmm3 : return fpu_stmm3; + case gdb_stmm4 : return fpu_stmm4; + case gdb_stmm5 : return fpu_stmm5; + case gdb_stmm6 : return fpu_stmm6; + case gdb_stmm7 : return fpu_stmm7; + case gdb_fctrl : return fpu_fctrl; + case gdb_fstat : return fpu_fstat; + case gdb_ftag : return fpu_ftag; + case gdb_fiseg : return fpu_fiseg; + case gdb_fioff : return fpu_fioff; + case gdb_foseg : return fpu_foseg; + case gdb_fooff : return fpu_fooff; + case gdb_fop : return fpu_fop; + case gdb_xmm0 : return fpu_xmm0; + case gdb_xmm1 : return fpu_xmm1; + case gdb_xmm2 : return fpu_xmm2; + case gdb_xmm3 : return fpu_xmm3; + case gdb_xmm4 : return fpu_xmm4; + case gdb_xmm5 : return fpu_xmm5; + case gdb_xmm6 : return fpu_xmm6; + case gdb_xmm7 : return fpu_xmm7; + case gdb_mxcsr : return fpu_mxcsr; + default: + break; + } + } + else if (kind == eRegisterKindLLDB) + { + return reg; + } + return LLDB_INVALID_REGNUM; +} + + +bool +RegisterContextDarwin_i386::HardwareSingleStep (bool enable) +{ + if (ReadGPR(false) != 0) + return false; + + const uint32_t trace_bit = 0x100u; + if (enable) + { + // If the trace bit is already set, there is nothing to do + if (gpr.eflags & trace_bit) + return true; + else + gpr.eflags |= trace_bit; + } + else + { + // If the trace bit is already cleared, there is nothing to do + if (gpr.eflags & trace_bit) + gpr.eflags &= ~trace_bit; + else + return true; + } + + return WriteGPR() == 0; +} + + + diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h b/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h new file mode 100644 index 000000000000..a588494f9dcf --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextDarwin_i386.h @@ -0,0 +1,269 @@ +//===-- RegisterContextDarwin_i386.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextDarwin_i386_h_ +#define liblldb_RegisterContextDarwin_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +class RegisterContextDarwin_i386 : public lldb_private::RegisterContext +{ +public: + + RegisterContextDarwin_i386(lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextDarwin_i386(); + + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb_private::RegisterSet * + GetRegisterSet (size_t set); + + virtual bool + ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + virtual bool + HardwareSingleStep (bool enable); + + struct GPR + { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ss; + uint32_t eflags; + uint32_t eip; + uint32_t cs; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t gs; + }; + + struct MMSReg + { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint32_t pad[2]; + uint16_t fcw; + uint16_t fsw; + uint8_t ftw; + uint8_t pad1; + uint16_t fop; + uint32_t ip; + uint16_t cs; + uint16_t pad2; + uint32_t dp; + uint16_t ds; + uint16_t pad3; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[8]; + uint8_t pad4[14*16]; + int pad5; + }; + + struct EXC + { + uint32_t trapno; + uint32_t err; + uint32_t faultvaddr; + }; + +protected: + + enum + { + GPRRegSet = 1, + FPURegSet = 2, + EXCRegSet = 3 + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + int gpr_errs[2]; // Read/Write errors + int fpu_errs[2]; // Read/Write errors + int exc_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + int + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, int err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == 0; + } + + void + LogGPR (lldb_private::Log *log, const char *title); + + int + ReadGPR (bool force); + + int + ReadFPU (bool force); + + int + ReadEXC (bool force); + + int + WriteGPR (); + + int + WriteFPU (); + + int + WriteEXC (); + + // Subclasses override these to do the actual reading. + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) = 0; + + virtual int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) = 0; + + virtual int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) = 0; + + virtual int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) = 0; + + virtual int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) = 0; + + virtual int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) = 0; + + int + ReadRegisterSet (uint32_t set, bool force); + + int + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb_private::RegisterInfo * + GetRegisterInfos (); +}; + +#endif // liblldb_RegisterContextDarwin_i386_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp b/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp new file mode 100644 index 000000000000..433782fe20c0 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp @@ -0,0 +1,1066 @@ +//===-- RegisterContextDarwin_x86_64.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +#include <stdarg.h> +#include <stddef.h> // offsetof + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Host/Endian.h" +#include "llvm/Support/Compiler.h" + +// Support building against older versions of LLVM, this macro was added +// recently. +#ifndef LLVM_EXTENSION +#define LLVM_EXTENSION +#endif + +// Project includes +#include "RegisterContextDarwin_x86_64.h" + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gpr_rax = 0, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs, + + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15, + + exc_trapno, + exc_err, + exc_faultvaddr, + + k_num_registers, + + // Aliases + fpu_fctrl = fpu_fcw, + fpu_fstat = fpu_fsw, + fpu_ftag = fpu_ftw, + fpu_fiseg = fpu_cs, + fpu_fioff = fpu_ip, + fpu_foseg = fpu_ds, + fpu_fooff = fpu_dp +}; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_gpr_rax = 0, + gcc_dwarf_gpr_rdx, + gcc_dwarf_gpr_rcx, + gcc_dwarf_gpr_rbx, + gcc_dwarf_gpr_rsi, + gcc_dwarf_gpr_rdi, + gcc_dwarf_gpr_rbp, + gcc_dwarf_gpr_rsp, + gcc_dwarf_gpr_r8, + gcc_dwarf_gpr_r9, + gcc_dwarf_gpr_r10, + gcc_dwarf_gpr_r11, + gcc_dwarf_gpr_r12, + gcc_dwarf_gpr_r13, + gcc_dwarf_gpr_r14, + gcc_dwarf_gpr_r15, + gcc_dwarf_gpr_rip, + gcc_dwarf_fpu_xmm0, + gcc_dwarf_fpu_xmm1, + gcc_dwarf_fpu_xmm2, + gcc_dwarf_fpu_xmm3, + gcc_dwarf_fpu_xmm4, + gcc_dwarf_fpu_xmm5, + gcc_dwarf_fpu_xmm6, + gcc_dwarf_fpu_xmm7, + gcc_dwarf_fpu_xmm8, + gcc_dwarf_fpu_xmm9, + gcc_dwarf_fpu_xmm10, + gcc_dwarf_fpu_xmm11, + gcc_dwarf_fpu_xmm12, + gcc_dwarf_fpu_xmm13, + gcc_dwarf_fpu_xmm14, + gcc_dwarf_fpu_xmm15, + gcc_dwarf_fpu_stmm0, + gcc_dwarf_fpu_stmm1, + gcc_dwarf_fpu_stmm2, + gcc_dwarf_fpu_stmm3, + gcc_dwarf_fpu_stmm4, + gcc_dwarf_fpu_stmm5, + gcc_dwarf_fpu_stmm6, + gcc_dwarf_fpu_stmm7 + +}; + +enum gdb_regnums +{ + gdb_gpr_rax = 0, + gdb_gpr_rbx = 1, + gdb_gpr_rcx = 2, + gdb_gpr_rdx = 3, + gdb_gpr_rsi = 4, + gdb_gpr_rdi = 5, + gdb_gpr_rbp = 6, + gdb_gpr_rsp = 7, + gdb_gpr_r8 = 8, + gdb_gpr_r9 = 9, + gdb_gpr_r10 = 10, + gdb_gpr_r11 = 11, + gdb_gpr_r12 = 12, + gdb_gpr_r13 = 13, + gdb_gpr_r14 = 14, + gdb_gpr_r15 = 15, + gdb_gpr_rip = 16, + gdb_gpr_rflags = 17, + gdb_gpr_cs = 18, + gdb_gpr_ss = 19, + gdb_gpr_ds = 20, + gdb_gpr_es = 21, + gdb_gpr_fs = 22, + gdb_gpr_gs = 23, + gdb_fpu_stmm0 = 24, + gdb_fpu_stmm1 = 25, + gdb_fpu_stmm2 = 26, + gdb_fpu_stmm3 = 27, + gdb_fpu_stmm4 = 28, + gdb_fpu_stmm5 = 29, + gdb_fpu_stmm6 = 30, + gdb_fpu_stmm7 = 31, + gdb_fpu_fctrl = 32, gdb_fpu_fcw = gdb_fpu_fctrl, + gdb_fpu_fstat = 33, gdb_fpu_fsw = gdb_fpu_fstat, + gdb_fpu_ftag = 34, gdb_fpu_ftw = gdb_fpu_ftag, + gdb_fpu_fiseg = 35, gdb_fpu_cs = gdb_fpu_fiseg, + gdb_fpu_fioff = 36, gdb_fpu_ip = gdb_fpu_fioff, + gdb_fpu_foseg = 37, gdb_fpu_ds = gdb_fpu_foseg, + gdb_fpu_fooff = 38, gdb_fpu_dp = gdb_fpu_fooff, + gdb_fpu_fop = 39, + gdb_fpu_xmm0 = 40, + gdb_fpu_xmm1 = 41, + gdb_fpu_xmm2 = 42, + gdb_fpu_xmm3 = 43, + gdb_fpu_xmm4 = 44, + gdb_fpu_xmm5 = 45, + gdb_fpu_xmm6 = 46, + gdb_fpu_xmm7 = 47, + gdb_fpu_xmm8 = 48, + gdb_fpu_xmm9 = 49, + gdb_fpu_xmm10 = 50, + gdb_fpu_xmm11 = 51, + gdb_fpu_xmm12 = 52, + gdb_fpu_xmm13 = 53, + gdb_fpu_xmm14 = 54, + gdb_fpu_xmm15 = 55, + gdb_fpu_mxcsr = 56 +}; + +RegisterContextDarwin_x86_64::RegisterContextDarwin_x86_64 (Thread &thread, uint32_t concrete_frame_idx) : + RegisterContext (thread, concrete_frame_idx), + gpr(), + fpu(), + exc() +{ + uint32_t i; + for (i=0; i<kNumErrors; i++) + { + gpr_errs[i] = -1; + fpu_errs[i] = -1; + exc_errs[i] = -1; + } +} + +RegisterContextDarwin_x86_64::~RegisterContextDarwin_x86_64() +{ +} + +#define GPR_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_x86_64::GPR, reg)) +#define FPU_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_x86_64::FPU, reg) + sizeof (RegisterContextDarwin_x86_64::GPR)) +#define EXC_OFFSET(reg) (LLVM_EXTENSION offsetof (RegisterContextDarwin_x86_64::EXC, reg) + sizeof (RegisterContextDarwin_x86_64::GPR) + sizeof (RegisterContextDarwin_x86_64::FPU)) + +// These macros will auto define the register name, alt name, register size, +// register offset, encoding, format and native register. This ensures that +// the register state structures are defined correctly and have the correct +// sizes and offsets. +#define DEFINE_GPR(reg, alt) #reg, alt, sizeof(((RegisterContextDarwin_x86_64::GPR *)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, eFormatHex +#define DEFINE_FPU_UINT(reg) #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::FPU *)NULL)->reg), FPU_OFFSET(reg), eEncodingUint, eFormatHex +#define DEFINE_FPU_VECT(reg, i) #reg#i, NULL, sizeof(((RegisterContextDarwin_x86_64::FPU *)NULL)->reg[i].bytes), FPU_OFFSET(reg[i]), eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_fpu_##reg##i, gcc_dwarf_fpu_##reg##i, LLDB_INVALID_REGNUM, gdb_fpu_##reg##i, fpu_##reg##i }, NULL, NULL +#define DEFINE_EXC(reg) #reg, NULL, sizeof(((RegisterContextDarwin_x86_64::EXC *)NULL)->reg), EXC_OFFSET(reg), eEncodingUint, eFormatHex + +#define REG_CONTEXT_SIZE (sizeof (RegisterContextDarwin_x86_64::GPR) + sizeof (RegisterContextDarwin_x86_64::FPU) + sizeof (RegisterContextDarwin_x86_64::EXC)) + +// General purpose registers for 64 bit +static RegisterInfo g_register_infos[] = +{ +// Macro auto defines most stuff GCC DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS +// =============================== ====================== =================== ========================== ==================== =================== ========== =============== + { DEFINE_GPR (rax , NULL) , { gcc_dwarf_gpr_rax , gcc_dwarf_gpr_rax , LLDB_INVALID_REGNUM , gdb_gpr_rax , gpr_rax }, NULL, NULL}, + { DEFINE_GPR (rbx , NULL) , { gcc_dwarf_gpr_rbx , gcc_dwarf_gpr_rbx , LLDB_INVALID_REGNUM , gdb_gpr_rbx , gpr_rbx }, NULL, NULL}, + { DEFINE_GPR (rcx , NULL) , { gcc_dwarf_gpr_rcx , gcc_dwarf_gpr_rcx , LLDB_INVALID_REGNUM , gdb_gpr_rcx , gpr_rcx }, NULL, NULL}, + { DEFINE_GPR (rdx , NULL) , { gcc_dwarf_gpr_rdx , gcc_dwarf_gpr_rdx , LLDB_INVALID_REGNUM , gdb_gpr_rdx , gpr_rdx }, NULL, NULL}, + { DEFINE_GPR (rdi , NULL) , { gcc_dwarf_gpr_rdi , gcc_dwarf_gpr_rdi , LLDB_INVALID_REGNUM , gdb_gpr_rdi , gpr_rdi }, NULL, NULL}, + { DEFINE_GPR (rsi , NULL) , { gcc_dwarf_gpr_rsi , gcc_dwarf_gpr_rsi , LLDB_INVALID_REGNUM , gdb_gpr_rsi , gpr_rsi }, NULL, NULL}, + { DEFINE_GPR (rbp , "fp") , { gcc_dwarf_gpr_rbp , gcc_dwarf_gpr_rbp , LLDB_REGNUM_GENERIC_FP , gdb_gpr_rbp , gpr_rbp }, NULL, NULL}, + { DEFINE_GPR (rsp , "sp") , { gcc_dwarf_gpr_rsp , gcc_dwarf_gpr_rsp , LLDB_REGNUM_GENERIC_SP , gdb_gpr_rsp , gpr_rsp }, NULL, NULL}, + { DEFINE_GPR (r8 , NULL) , { gcc_dwarf_gpr_r8 , gcc_dwarf_gpr_r8 , LLDB_INVALID_REGNUM , gdb_gpr_r8 , gpr_r8 }, NULL, NULL}, + { DEFINE_GPR (r9 , NULL) , { gcc_dwarf_gpr_r9 , gcc_dwarf_gpr_r9 , LLDB_INVALID_REGNUM , gdb_gpr_r9 , gpr_r9 }, NULL, NULL}, + { DEFINE_GPR (r10 , NULL) , { gcc_dwarf_gpr_r10 , gcc_dwarf_gpr_r10 , LLDB_INVALID_REGNUM , gdb_gpr_r10 , gpr_r10 }, NULL, NULL}, + { DEFINE_GPR (r11 , NULL) , { gcc_dwarf_gpr_r11 , gcc_dwarf_gpr_r11 , LLDB_INVALID_REGNUM , gdb_gpr_r11 , gpr_r11 }, NULL, NULL}, + { DEFINE_GPR (r12 , NULL) , { gcc_dwarf_gpr_r12 , gcc_dwarf_gpr_r12 , LLDB_INVALID_REGNUM , gdb_gpr_r12 , gpr_r12 }, NULL, NULL}, + { DEFINE_GPR (r13 , NULL) , { gcc_dwarf_gpr_r13 , gcc_dwarf_gpr_r13 , LLDB_INVALID_REGNUM , gdb_gpr_r13 , gpr_r13 }, NULL, NULL}, + { DEFINE_GPR (r14 , NULL) , { gcc_dwarf_gpr_r14 , gcc_dwarf_gpr_r14 , LLDB_INVALID_REGNUM , gdb_gpr_r14 , gpr_r14 }, NULL, NULL}, + { DEFINE_GPR (r15 , NULL) , { gcc_dwarf_gpr_r15 , gcc_dwarf_gpr_r15 , LLDB_INVALID_REGNUM , gdb_gpr_r15 , gpr_r15 }, NULL, NULL}, + { DEFINE_GPR (rip , "pc") , { gcc_dwarf_gpr_rip , gcc_dwarf_gpr_rip , LLDB_REGNUM_GENERIC_PC , gdb_gpr_rip , gpr_rip }, NULL, NULL}, + { DEFINE_GPR (rflags, "flags") , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, gdb_gpr_rflags , gpr_rflags }, NULL, NULL}, + { DEFINE_GPR (cs , NULL) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_gpr_cs , gpr_cs }, NULL, NULL}, + { DEFINE_GPR (fs , NULL) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_gpr_fs , gpr_fs }, NULL, NULL}, + { DEFINE_GPR (gs , NULL) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_gpr_gs , gpr_gs }, NULL, NULL}, + + { DEFINE_FPU_UINT(fcw) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_fcw , fpu_fcw }, NULL, NULL}, + { DEFINE_FPU_UINT(fsw) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_fsw , fpu_fsw }, NULL, NULL}, + { DEFINE_FPU_UINT(ftw) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_ftw , fpu_ftw }, NULL, NULL}, + { DEFINE_FPU_UINT(fop) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_fop , fpu_fop }, NULL, NULL}, + { DEFINE_FPU_UINT(ip) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_ip , fpu_ip }, NULL, NULL}, + { DEFINE_FPU_UINT(cs) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_cs , fpu_cs }, NULL, NULL}, + { DEFINE_FPU_UINT(dp) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_dp , fpu_dp }, NULL, NULL}, + { DEFINE_FPU_UINT(ds) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_ds , fpu_ds }, NULL, NULL}, + { DEFINE_FPU_UINT(mxcsr) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , gdb_fpu_mxcsr , fpu_mxcsr }, NULL, NULL}, + { DEFINE_FPU_UINT(mxcsrmask) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, fpu_mxcsrmask }, NULL, NULL}, + { DEFINE_FPU_VECT(stmm,0) }, + { DEFINE_FPU_VECT(stmm,1) }, + { DEFINE_FPU_VECT(stmm,2) }, + { DEFINE_FPU_VECT(stmm,3) }, + { DEFINE_FPU_VECT(stmm,4) }, + { DEFINE_FPU_VECT(stmm,5) }, + { DEFINE_FPU_VECT(stmm,6) }, + { DEFINE_FPU_VECT(stmm,7) }, + { DEFINE_FPU_VECT(xmm,0) }, + { DEFINE_FPU_VECT(xmm,1) }, + { DEFINE_FPU_VECT(xmm,2) }, + { DEFINE_FPU_VECT(xmm,3) }, + { DEFINE_FPU_VECT(xmm,4) }, + { DEFINE_FPU_VECT(xmm,5) }, + { DEFINE_FPU_VECT(xmm,6) }, + { DEFINE_FPU_VECT(xmm,7) }, + { DEFINE_FPU_VECT(xmm,8) }, + { DEFINE_FPU_VECT(xmm,9) }, + { DEFINE_FPU_VECT(xmm,10) }, + { DEFINE_FPU_VECT(xmm,11) }, + { DEFINE_FPU_VECT(xmm,12) }, + { DEFINE_FPU_VECT(xmm,13) }, + { DEFINE_FPU_VECT(xmm,14) }, + { DEFINE_FPU_VECT(xmm,15) }, + + { DEFINE_EXC(trapno) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_trapno }, NULL, NULL}, + { DEFINE_EXC(err) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_err }, NULL, NULL}, + { DEFINE_EXC(faultvaddr) , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM, exc_faultvaddr }, NULL, NULL} +}; + +static size_t k_num_register_infos = (sizeof(g_register_infos)/sizeof(RegisterInfo)); + + +void +RegisterContextDarwin_x86_64::InvalidateAllRegisters () +{ + InvalidateAllRegisterStates(); +} + + +size_t +RegisterContextDarwin_x86_64::GetRegisterCount () +{ + assert(k_num_register_infos == k_num_registers); + return k_num_registers; +} + + +const RegisterInfo * +RegisterContextDarwin_x86_64::GetRegisterInfoAtIndex (size_t reg) +{ + assert(k_num_register_infos == k_num_registers); + if (reg < k_num_registers) + return &g_register_infos[reg]; + return NULL; +} + + +size_t +RegisterContextDarwin_x86_64::GetRegisterInfosCount () +{ + return k_num_register_infos; +} + +const lldb_private::RegisterInfo * +RegisterContextDarwin_x86_64::GetRegisterInfos () +{ + return g_register_infos; +} + + + +static uint32_t g_gpr_regnums[] = +{ + gpr_rax, + gpr_rbx, + gpr_rcx, + gpr_rdx, + gpr_rdi, + gpr_rsi, + gpr_rbp, + gpr_rsp, + gpr_r8, + gpr_r9, + gpr_r10, + gpr_r11, + gpr_r12, + gpr_r13, + gpr_r14, + gpr_r15, + gpr_rip, + gpr_rflags, + gpr_cs, + gpr_fs, + gpr_gs +}; + +static uint32_t g_fpu_regnums[] = +{ + fpu_fcw, + fpu_fsw, + fpu_ftw, + fpu_fop, + fpu_ip, + fpu_cs, + fpu_dp, + fpu_ds, + fpu_mxcsr, + fpu_mxcsrmask, + fpu_stmm0, + fpu_stmm1, + fpu_stmm2, + fpu_stmm3, + fpu_stmm4, + fpu_stmm5, + fpu_stmm6, + fpu_stmm7, + fpu_xmm0, + fpu_xmm1, + fpu_xmm2, + fpu_xmm3, + fpu_xmm4, + fpu_xmm5, + fpu_xmm6, + fpu_xmm7, + fpu_xmm8, + fpu_xmm9, + fpu_xmm10, + fpu_xmm11, + fpu_xmm12, + fpu_xmm13, + fpu_xmm14, + fpu_xmm15 +}; + +static uint32_t +g_exc_regnums[] = +{ + exc_trapno, + exc_err, + exc_faultvaddr +}; + +// Number of registers in each register set +const size_t k_num_gpr_registers = sizeof(g_gpr_regnums) / sizeof(uint32_t); +const size_t k_num_fpu_registers = sizeof(g_fpu_regnums) / sizeof(uint32_t); +const size_t k_num_exc_registers = sizeof(g_exc_regnums) / sizeof(uint32_t); + +//---------------------------------------------------------------------- +// Register set definitions. The first definitions at register set index +// of zero is for all registers, followed by other registers sets. The +// register information for the all register set need not be filled in. +//---------------------------------------------------------------------- +static const RegisterSet g_reg_sets[] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers, g_gpr_regnums, }, + { "Floating Point Registers", "fpu", k_num_fpu_registers, g_fpu_regnums }, + { "Exception State Registers", "exc", k_num_exc_registers, g_exc_regnums } +}; + +const size_t k_num_regsets = sizeof(g_reg_sets) / sizeof(RegisterSet); + + +size_t +RegisterContextDarwin_x86_64::GetRegisterSetCount () +{ + return k_num_regsets; +} + +const RegisterSet * +RegisterContextDarwin_x86_64::GetRegisterSet (size_t reg_set) +{ + if (reg_set < k_num_regsets) + return &g_reg_sets[reg_set]; + return NULL; +} + +int +RegisterContextDarwin_x86_64::GetSetForNativeRegNum (int reg_num) +{ + if (reg_num < fpu_fcw) + return GPRRegSet; + else if (reg_num < exc_trapno) + return FPURegSet; + else if (reg_num < k_num_registers) + return EXCRegSet; + return -1; +} + +void +RegisterContextDarwin_x86_64::LogGPR(Log *log, const char *format, ...) +{ + if (log) + { + if (format) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } + for (uint32_t i=0; i<k_num_gpr_registers; i++) + { + uint32_t reg = gpr_rax + i; + log->Printf("%12s = 0x%16.16" PRIx64, g_register_infos[reg].name, (&gpr.rax)[reg]); + } + } +} + +int +RegisterContextDarwin_x86_64::ReadGPR (bool force) +{ + int set = GPRRegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadGPR(GetThreadID(), set, gpr)); + } + return GetError(GPRRegSet, Read); +} + +int +RegisterContextDarwin_x86_64::ReadFPU (bool force) +{ + int set = FPURegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadFPU(GetThreadID(), set, fpu)); + } + return GetError(FPURegSet, Read); +} + +int +RegisterContextDarwin_x86_64::ReadEXC (bool force) +{ + int set = EXCRegSet; + if (force || !RegisterSetIsCached(set)) + { + SetError(set, Read, DoReadEXC(GetThreadID(), set, exc)); + } + return GetError(EXCRegSet, Read); +} + +int +RegisterContextDarwin_x86_64::WriteGPR () +{ + int set = GPRRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return -1; + } + SetError (set, Write, DoWriteGPR(GetThreadID(), set, gpr)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +int +RegisterContextDarwin_x86_64::WriteFPU () +{ + int set = FPURegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return -1; + } + SetError (set, Write, DoWriteFPU(GetThreadID(), set, fpu)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +int +RegisterContextDarwin_x86_64::WriteEXC () +{ + int set = EXCRegSet; + if (!RegisterSetIsCached(set)) + { + SetError (set, Write, -1); + return -1; + } + SetError (set, Write, DoWriteEXC(GetThreadID(), set, exc)); + SetError (set, Read, -1); + return GetError (set, Write); +} + +int +RegisterContextDarwin_x86_64::ReadRegisterSet(uint32_t set, bool force) +{ + switch (set) + { + case GPRRegSet: return ReadGPR (force); + case FPURegSet: return ReadFPU (force); + case EXCRegSet: return ReadEXC (force); + default: break; + } + return -1; +} + +int +RegisterContextDarwin_x86_64::WriteRegisterSet(uint32_t set) +{ + // Make sure we have a valid context to set. + switch (set) + { + case GPRRegSet: return WriteGPR (); + case FPURegSet: return WriteFPU (); + case EXCRegSet: return WriteEXC (); + default: break; + } + return -1; +} + + +bool +RegisterContextDarwin_x86_64::ReadRegister (const RegisterInfo *reg_info, + RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum (reg); + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != 0) + return false; + + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + value = (&gpr.rax)[reg - gpr_rax]; + break; + + case fpu_fcw: + value = fpu.fcw; + break; + + case fpu_fsw: + value = fpu.fsw; + break; + + case fpu_ftw: + value = fpu.ftw; + break; + + case fpu_fop: + value = fpu.fop; + break; + + case fpu_ip: + value = fpu.ip; + break; + + case fpu_cs: + value = fpu.cs; + break; + + case fpu_dp: + value = fpu.dp; + break; + + case fpu_ds: + value = fpu.ds; + break; + + case fpu_mxcsr: + value = fpu.mxcsr; + break; + + case fpu_mxcsrmask: + value = fpu.mxcsrmask; + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + value.SetBytes(fpu.stmm[reg - fpu_stmm0].bytes, reg_info->byte_size, lldb::endian::InlHostByteOrder()); + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + value.SetBytes(fpu.xmm[reg - fpu_xmm0].bytes, reg_info->byte_size, lldb::endian::InlHostByteOrder()); + break; + + case exc_trapno: + value = exc.trapno; + break; + + case exc_err: + value = exc.err; + break; + + case exc_faultvaddr: + value = exc.faultvaddr; + break; + + default: + return false; + } + return true; +} + + +bool +RegisterContextDarwin_x86_64::WriteRegister (const RegisterInfo *reg_info, + const RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + int set = RegisterContextDarwin_x86_64::GetSetForNativeRegNum (reg); + + if (set == -1) + return false; + + if (ReadRegisterSet(set, false) != 0) + return false; + + switch (reg) + { + case gpr_rax: + case gpr_rbx: + case gpr_rcx: + case gpr_rdx: + case gpr_rdi: + case gpr_rsi: + case gpr_rbp: + case gpr_rsp: + case gpr_r8: + case gpr_r9: + case gpr_r10: + case gpr_r11: + case gpr_r12: + case gpr_r13: + case gpr_r14: + case gpr_r15: + case gpr_rip: + case gpr_rflags: + case gpr_cs: + case gpr_fs: + case gpr_gs: + (&gpr.rax)[reg - gpr_rax] = value.GetAsUInt64(); + break; + + case fpu_fcw: + fpu.fcw = value.GetAsUInt16(); + break; + + case fpu_fsw: + fpu.fsw = value.GetAsUInt16(); + break; + + case fpu_ftw: + fpu.ftw = value.GetAsUInt8(); + break; + + case fpu_fop: + fpu.fop = value.GetAsUInt16(); + break; + + case fpu_ip: + fpu.ip = value.GetAsUInt32(); + break; + + case fpu_cs: + fpu.cs = value.GetAsUInt16(); + break; + + case fpu_dp: + fpu.dp = value.GetAsUInt32(); + break; + + case fpu_ds: + fpu.ds = value.GetAsUInt16(); + break; + + case fpu_mxcsr: + fpu.mxcsr = value.GetAsUInt32(); + break; + + case fpu_mxcsrmask: + fpu.mxcsrmask = value.GetAsUInt32(); + break; + + case fpu_stmm0: + case fpu_stmm1: + case fpu_stmm2: + case fpu_stmm3: + case fpu_stmm4: + case fpu_stmm5: + case fpu_stmm6: + case fpu_stmm7: + ::memcpy (fpu.stmm[reg - fpu_stmm0].bytes, value.GetBytes(), value.GetByteSize()); + break; + + case fpu_xmm0: + case fpu_xmm1: + case fpu_xmm2: + case fpu_xmm3: + case fpu_xmm4: + case fpu_xmm5: + case fpu_xmm6: + case fpu_xmm7: + case fpu_xmm8: + case fpu_xmm9: + case fpu_xmm10: + case fpu_xmm11: + case fpu_xmm12: + case fpu_xmm13: + case fpu_xmm14: + case fpu_xmm15: + ::memcpy (fpu.xmm[reg - fpu_xmm0].bytes, value.GetBytes(), value.GetByteSize()); + return false; + + case exc_trapno: + exc.trapno = value.GetAsUInt32(); + break; + + case exc_err: + exc.err = value.GetAsUInt32(); + break; + + case exc_faultvaddr: + exc.faultvaddr = value.GetAsUInt64(); + break; + + default: + return false; + } + return WriteRegisterSet(set) == 0; +} + +bool +RegisterContextDarwin_x86_64::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && + ReadGPR (false) == 0 && + ReadFPU (false) == 0 && + ReadEXC (false) == 0) + { + uint8_t *dst = data_sp->GetBytes(); + ::memcpy (dst, &gpr, sizeof(gpr)); + dst += sizeof(gpr); + + ::memcpy (dst, &fpu, sizeof(fpu)); + dst += sizeof(gpr); + + ::memcpy (dst, &exc, sizeof(exc)); + return true; + } + return false; +} + +bool +RegisterContextDarwin_x86_64::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + const uint8_t *src = data_sp->GetBytes(); + ::memcpy (&gpr, src, sizeof(gpr)); + src += sizeof(gpr); + + ::memcpy (&fpu, src, sizeof(fpu)); + src += sizeof(gpr); + + ::memcpy (&exc, src, sizeof(exc)); + uint32_t success_count = 0; + if (WriteGPR() == 0) + ++success_count; + if (WriteFPU() == 0) + ++success_count; + if (WriteEXC() == 0) + ++success_count; + return success_count == 3; + } + return false; +} + + +uint32_t +RegisterContextDarwin_x86_64::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t reg) +{ + if (kind == eRegisterKindGeneric) + { + switch (reg) + { + case LLDB_REGNUM_GENERIC_PC: return gpr_rip; + case LLDB_REGNUM_GENERIC_SP: return gpr_rsp; + case LLDB_REGNUM_GENERIC_FP: return gpr_rbp; + case LLDB_REGNUM_GENERIC_FLAGS: return gpr_rflags; + case LLDB_REGNUM_GENERIC_RA: + default: + break; + } + } + else if (kind == eRegisterKindGCC || kind == eRegisterKindDWARF) + { + switch (reg) + { + case gcc_dwarf_gpr_rax: return gpr_rax; + case gcc_dwarf_gpr_rdx: return gpr_rdx; + case gcc_dwarf_gpr_rcx: return gpr_rcx; + case gcc_dwarf_gpr_rbx: return gpr_rbx; + case gcc_dwarf_gpr_rsi: return gpr_rsi; + case gcc_dwarf_gpr_rdi: return gpr_rdi; + case gcc_dwarf_gpr_rbp: return gpr_rbp; + case gcc_dwarf_gpr_rsp: return gpr_rsp; + case gcc_dwarf_gpr_r8: return gpr_r8; + case gcc_dwarf_gpr_r9: return gpr_r9; + case gcc_dwarf_gpr_r10: return gpr_r10; + case gcc_dwarf_gpr_r11: return gpr_r11; + case gcc_dwarf_gpr_r12: return gpr_r12; + case gcc_dwarf_gpr_r13: return gpr_r13; + case gcc_dwarf_gpr_r14: return gpr_r14; + case gcc_dwarf_gpr_r15: return gpr_r15; + case gcc_dwarf_gpr_rip: return gpr_rip; + case gcc_dwarf_fpu_xmm0: return fpu_xmm0; + case gcc_dwarf_fpu_xmm1: return fpu_xmm1; + case gcc_dwarf_fpu_xmm2: return fpu_xmm2; + case gcc_dwarf_fpu_xmm3: return fpu_xmm3; + case gcc_dwarf_fpu_xmm4: return fpu_xmm4; + case gcc_dwarf_fpu_xmm5: return fpu_xmm5; + case gcc_dwarf_fpu_xmm6: return fpu_xmm6; + case gcc_dwarf_fpu_xmm7: return fpu_xmm7; + case gcc_dwarf_fpu_xmm8: return fpu_xmm8; + case gcc_dwarf_fpu_xmm9: return fpu_xmm9; + case gcc_dwarf_fpu_xmm10: return fpu_xmm10; + case gcc_dwarf_fpu_xmm11: return fpu_xmm11; + case gcc_dwarf_fpu_xmm12: return fpu_xmm12; + case gcc_dwarf_fpu_xmm13: return fpu_xmm13; + case gcc_dwarf_fpu_xmm14: return fpu_xmm14; + case gcc_dwarf_fpu_xmm15: return fpu_xmm15; + case gcc_dwarf_fpu_stmm0: return fpu_stmm0; + case gcc_dwarf_fpu_stmm1: return fpu_stmm1; + case gcc_dwarf_fpu_stmm2: return fpu_stmm2; + case gcc_dwarf_fpu_stmm3: return fpu_stmm3; + case gcc_dwarf_fpu_stmm4: return fpu_stmm4; + case gcc_dwarf_fpu_stmm5: return fpu_stmm5; + case gcc_dwarf_fpu_stmm6: return fpu_stmm6; + case gcc_dwarf_fpu_stmm7: return fpu_stmm7; + default: + break; + } + } + else if (kind == eRegisterKindGDB) + { + switch (reg) + { + case gdb_gpr_rax : return gpr_rax; + case gdb_gpr_rbx : return gpr_rbx; + case gdb_gpr_rcx : return gpr_rcx; + case gdb_gpr_rdx : return gpr_rdx; + case gdb_gpr_rsi : return gpr_rsi; + case gdb_gpr_rdi : return gpr_rdi; + case gdb_gpr_rbp : return gpr_rbp; + case gdb_gpr_rsp : return gpr_rsp; + case gdb_gpr_r8 : return gpr_r8; + case gdb_gpr_r9 : return gpr_r9; + case gdb_gpr_r10 : return gpr_r10; + case gdb_gpr_r11 : return gpr_r11; + case gdb_gpr_r12 : return gpr_r12; + case gdb_gpr_r13 : return gpr_r13; + case gdb_gpr_r14 : return gpr_r14; + case gdb_gpr_r15 : return gpr_r15; + case gdb_gpr_rip : return gpr_rip; + case gdb_gpr_rflags : return gpr_rflags; + case gdb_gpr_cs : return gpr_cs; + case gdb_gpr_ss : return gpr_gs; // HACK: For now for "ss", just copy what is in "gs" + case gdb_gpr_ds : return gpr_gs; // HACK: For now for "ds", just copy what is in "gs" + case gdb_gpr_es : return gpr_gs; // HACK: For now for "es", just copy what is in "gs" + case gdb_gpr_fs : return gpr_fs; + case gdb_gpr_gs : return gpr_gs; + case gdb_fpu_stmm0 : return fpu_stmm0; + case gdb_fpu_stmm1 : return fpu_stmm1; + case gdb_fpu_stmm2 : return fpu_stmm2; + case gdb_fpu_stmm3 : return fpu_stmm3; + case gdb_fpu_stmm4 : return fpu_stmm4; + case gdb_fpu_stmm5 : return fpu_stmm5; + case gdb_fpu_stmm6 : return fpu_stmm6; + case gdb_fpu_stmm7 : return fpu_stmm7; + case gdb_fpu_fctrl : return fpu_fctrl; + case gdb_fpu_fstat : return fpu_fstat; + case gdb_fpu_ftag : return fpu_ftag; + case gdb_fpu_fiseg : return fpu_fiseg; + case gdb_fpu_fioff : return fpu_fioff; + case gdb_fpu_foseg : return fpu_foseg; + case gdb_fpu_fooff : return fpu_fooff; + case gdb_fpu_fop : return fpu_fop; + case gdb_fpu_xmm0 : return fpu_xmm0; + case gdb_fpu_xmm1 : return fpu_xmm1; + case gdb_fpu_xmm2 : return fpu_xmm2; + case gdb_fpu_xmm3 : return fpu_xmm3; + case gdb_fpu_xmm4 : return fpu_xmm4; + case gdb_fpu_xmm5 : return fpu_xmm5; + case gdb_fpu_xmm6 : return fpu_xmm6; + case gdb_fpu_xmm7 : return fpu_xmm7; + case gdb_fpu_xmm8 : return fpu_xmm8; + case gdb_fpu_xmm9 : return fpu_xmm9; + case gdb_fpu_xmm10 : return fpu_xmm10; + case gdb_fpu_xmm11 : return fpu_xmm11; + case gdb_fpu_xmm12 : return fpu_xmm12; + case gdb_fpu_xmm13 : return fpu_xmm13; + case gdb_fpu_xmm14 : return fpu_xmm14; + case gdb_fpu_xmm15 : return fpu_xmm15; + case gdb_fpu_mxcsr : return fpu_mxcsr; + default: + break; + } + } + else if (kind == eRegisterKindLLDB) + { + return reg; + } + return LLDB_INVALID_REGNUM; +} + +bool +RegisterContextDarwin_x86_64::HardwareSingleStep (bool enable) +{ + if (ReadGPR(true) != 0) + return false; + + const uint64_t trace_bit = 0x100ull; + if (enable) + { + + if (gpr.rflags & trace_bit) + return true; // trace bit is already set, there is nothing to do + else + gpr.rflags |= trace_bit; + } + else + { + if (gpr.rflags & trace_bit) + gpr.rflags &= ~trace_bit; + else + return true; // trace bit is clear, there is nothing to do + } + + return WriteGPR() == 0; +} + diff --git a/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h b/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h new file mode 100644 index 000000000000..4b8127af997c --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h @@ -0,0 +1,274 @@ +//===-- RegisterContextDarwin_x86_64.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextDarwin_x86_64_h_ +#define liblldb_RegisterContextDarwin_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +class RegisterContextDarwin_x86_64 : public lldb_private::RegisterContext +{ +public: + RegisterContextDarwin_x86_64 (lldb_private::Thread &thread, + uint32_t concrete_frame_idx); + + virtual + ~RegisterContextDarwin_x86_64(); + + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb_private::RegisterSet * + GetRegisterSet (size_t set); + + virtual bool + ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + virtual bool + HardwareSingleStep (bool enable); + + struct GPR + { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; + }; + + struct MMSReg + { + uint8_t bytes[10]; + uint8_t pad[6]; + }; + + struct XMMReg + { + uint8_t bytes[16]; + }; + + struct FPU + { + uint32_t pad[2]; + uint16_t fcw; // "fctrl" + uint16_t fsw; // "fstat" + uint8_t ftw; // "ftag" + uint8_t pad1; + uint16_t fop; // "fop" + uint32_t ip; // "fioff" + uint16_t cs; // "fiseg" + uint16_t pad2; + uint32_t dp; // "fooff" + uint16_t ds; // "foseg" + uint16_t pad3; + uint32_t mxcsr; + uint32_t mxcsrmask; + MMSReg stmm[8]; + XMMReg xmm[16]; + uint8_t pad4[6*16]; + int pad5; + }; + + struct EXC + { + uint32_t trapno; + uint32_t err; + uint64_t faultvaddr; + }; + +protected: + + enum + { + GPRRegSet = 4, + FPURegSet = 5, + EXCRegSet = 6 + }; + + enum + { + GPRWordCount = sizeof(GPR)/sizeof(uint32_t), + FPUWordCount = sizeof(FPU)/sizeof(uint32_t), + EXCWordCount = sizeof(EXC)/sizeof(uint32_t) + }; + + enum + { + Read = 0, + Write = 1, + kNumErrors = 2 + }; + + GPR gpr; + FPU fpu; + EXC exc; + int gpr_errs[2]; // Read/Write errors + int fpu_errs[2]; // Read/Write errors + int exc_errs[2]; // Read/Write errors + + void + InvalidateAllRegisterStates() + { + SetError (GPRRegSet, Read, -1); + SetError (FPURegSet, Read, -1); + SetError (EXCRegSet, Read, -1); + } + + int + GetError (int flavor, uint32_t err_idx) const + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + // When getting all errors, just OR all values together to see if + // we got any kind of error. + case GPRRegSet: return gpr_errs[err_idx]; + case FPURegSet: return fpu_errs[err_idx]; + case EXCRegSet: return exc_errs[err_idx]; + default: break; + } + } + return -1; + } + + bool + SetError (int flavor, uint32_t err_idx, int err) + { + if (err_idx < kNumErrors) + { + switch (flavor) + { + case GPRRegSet: + gpr_errs[err_idx] = err; + return true; + + case FPURegSet: + fpu_errs[err_idx] = err; + return true; + + case EXCRegSet: + exc_errs[err_idx] = err; + return true; + + default: break; + } + } + return false; + } + + bool + RegisterSetIsCached (int set) const + { + return GetError(set, Read) == 0; + } + + void + LogGPR (lldb_private::Log *log, const char *format, ...); + + int + ReadGPR (bool force); + + int + ReadFPU (bool force); + + int + ReadEXC (bool force); + + int + WriteGPR (); + + int + WriteFPU (); + + int + WriteEXC (); + + // Subclasses override these to do the actual reading. + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) = 0; + + virtual int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) = 0; + + virtual int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) = 0; + + virtual int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) = 0; + + virtual int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) = 0; + + virtual int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) = 0; + + int + ReadRegisterSet (uint32_t set, bool force); + + int + WriteRegisterSet (uint32_t set); + + static uint32_t + GetRegisterNumber (uint32_t reg_kind, uint32_t reg_num); + + static int + GetSetForNativeRegNum (int reg_num); + + static size_t + GetRegisterInfosCount (); + + static const lldb_private::RegisterInfo * + GetRegisterInfos (); + +}; + +#endif // liblldb_RegisterContextDarwin_x86_64_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextDummy.cpp b/source/Plugins/Process/Utility/RegisterContextDummy.cpp new file mode 100644 index 000000000000..1e282ce74f2e --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextDummy.cpp @@ -0,0 +1,137 @@ +//===-- RegisterContextDummy.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/DynamicLoader.h" + +#include "RegisterContextDummy.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextDummy::RegisterContextDummy (Thread &thread, uint32_t concrete_frame_idx, uint32_t address_byte_size) : +RegisterContext (thread, concrete_frame_idx) +{ + m_reg_set0.name = "General Purpose Registers"; + m_reg_set0.short_name = "GPR"; + m_reg_set0.num_registers = 1; + m_reg_set0.registers = new uint32_t(0); + + m_pc_reg_info.name = "pc"; + m_pc_reg_info.alt_name = "pc"; + m_pc_reg_info.byte_offset = 0; + m_pc_reg_info.byte_size = address_byte_size; + m_pc_reg_info.encoding = eEncodingUint; + m_pc_reg_info.format = eFormatPointer; + m_pc_reg_info.invalidate_regs = NULL; + m_pc_reg_info.value_regs = NULL; + m_pc_reg_info.kinds[eRegisterKindGCC] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + m_pc_reg_info.kinds[eRegisterKindGDB] = LLDB_INVALID_REGNUM; + m_pc_reg_info.kinds[eRegisterKindLLDB] = LLDB_INVALID_REGNUM; +} + +RegisterContextDummy::~RegisterContextDummy () +{ + delete m_reg_set0.registers; + delete m_pc_reg_info.invalidate_regs; + delete m_pc_reg_info.value_regs; +} + +void +RegisterContextDummy::InvalidateAllRegisters () {} + +size_t +RegisterContextDummy::GetRegisterCount () +{ + return 1; +} + +const lldb_private::RegisterInfo * +RegisterContextDummy::GetRegisterInfoAtIndex (size_t reg) +{ + if (reg) + return NULL; + return &m_pc_reg_info; +} + +size_t +RegisterContextDummy::GetRegisterSetCount () +{ + return 1; +} + +const lldb_private::RegisterSet * +RegisterContextDummy::GetRegisterSet (size_t reg_set) +{ + if (reg_set) + return NULL; + return &m_reg_set0; +} + +bool +RegisterContextDummy::ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value) +{ + if (!reg_info) + return false; + uint32_t reg_number = reg_info->kinds[eRegisterKindGeneric]; + if (reg_number == LLDB_REGNUM_GENERIC_PC) + { + value.SetUInt(LLDB_INVALID_ADDRESS, reg_info->byte_size); + return true; + } + return false; +} + +bool +RegisterContextDummy::WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value) +{ + return false; +} + +bool +RegisterContextDummy::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextDummy::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + return false; +} + +uint32_t +RegisterContextDummy::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + if (kind == eRegisterKindGeneric && num == LLDB_REGNUM_GENERIC_PC) + return 0; + return LLDB_INVALID_REGNUM; +} diff --git a/source/Plugins/Process/Utility/RegisterContextDummy.h b/source/Plugins/Process/Utility/RegisterContextDummy.h new file mode 100644 index 000000000000..ee8d5a134bbc --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextDummy.h @@ -0,0 +1,77 @@ +//===-- RegisterContextDummy.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_RegisterContextDummy_h_ +#define lldb_RegisterContextDummy_h_ + +#include <vector> + +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Symbol/SymbolContext.h" + +namespace lldb_private { + +class RegisterContextDummy : public lldb_private::RegisterContext +{ +public: + typedef std::shared_ptr<RegisterContextDummy> SharedPtr; + + RegisterContextDummy (Thread &thread, uint32_t concrete_frame_idx, uint32_t address_byte_size); + + /// + // pure virtual functions from the base class that we must implement + /// + + virtual + ~RegisterContextDummy (); + + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb_private::RegisterSet * + GetRegisterSet (size_t reg_set); + + virtual bool + ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + +private: + //------------------------------------------------------------------ + // For RegisterContextLLDB only + //------------------------------------------------------------------ + + lldb_private::RegisterSet m_reg_set0; // register set 0 (PC only) + lldb_private::RegisterInfo m_pc_reg_info; + + DISALLOW_COPY_AND_ASSIGN (RegisterContextDummy); +}; +} // namespace lldb_private + +#endif // lldb_RegisterContextDummy_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp new file mode 100644 index 000000000000..1ffc30da5762 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -0,0 +1,1541 @@ +//===-- RegisterContextLLDB.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/DynamicLoader.h" + +#include "RegisterContextLLDB.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextLLDB::RegisterContextLLDB +( + Thread& thread, + const SharedPtr &next_frame, + SymbolContext& sym_ctx, + uint32_t frame_number, + UnwindLLDB& unwind_lldb +) : + RegisterContext (thread, frame_number), + m_thread(thread), + m_fast_unwind_plan_sp (), + m_full_unwind_plan_sp (), + m_all_registers_available(false), + m_frame_type (-1), + m_cfa (LLDB_INVALID_ADDRESS), + m_start_pc (), + m_current_pc (), + m_current_offset (0), + m_current_offset_backed_up_one (0), + m_sym_ctx(sym_ctx), + m_sym_ctx_valid (false), + m_frame_number (frame_number), + m_registers(), + m_parent_unwind (unwind_lldb) +{ + m_sym_ctx.Clear(false); + m_sym_ctx_valid = false; + + if (IsFrameZero ()) + { + InitializeZerothFrame (); + } + else + { + InitializeNonZerothFrame (); + } + + // This same code exists over in the GetFullUnwindPlanForFrame() but it may not have been executed yet + if (IsFrameZero() + || next_frame->m_frame_type == eSigtrampFrame + || next_frame->m_frame_type == eDebuggerFrame) + { + m_all_registers_available = true; + } +} + +// Initialize a RegisterContextLLDB which is the first frame of a stack -- the zeroth frame or currently +// executing frame. + +void +RegisterContextLLDB::InitializeZerothFrame() +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + ExecutionContext exe_ctx(m_thread.shared_from_this()); + RegisterContextSP reg_ctx_sp = m_thread.GetRegisterContext(); + + if (reg_ctx_sp.get() == NULL) + { + m_frame_type = eNotAValidFrame; + return; + } + + addr_t current_pc = reg_ctx_sp->GetPC(); + + if (current_pc == LLDB_INVALID_ADDRESS) + { + m_frame_type = eNotAValidFrame; + return; + } + + Process *process = exe_ctx.GetProcessPtr(); + + // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs + // this will strip bit zero in case we read a PC from memory or from the LR. + // (which would be a no-op in frame 0 where we get it from the register set, + // but still a good idea to make the call here for other ABIs that may exist.) + ABI *abi = process->GetABI().get(); + if (abi) + current_pc = abi->FixCodeAddress(current_pc); + + // Initialize m_current_pc, an Address object, based on current_pc, an addr_t. + process->GetTarget().GetSectionLoadList().ResolveLoadAddress (current_pc, m_current_pc); + + // If we don't have a Module for some reason, we're not going to find symbol/function information - just + // stick in some reasonable defaults and hope we can unwind past this frame. + ModuleSP pc_module_sp (m_current_pc.GetModule()); + if (!m_current_pc.IsValid() || !pc_module_sp) + { + UnwindLogMsg ("using architectural default unwind method"); + } + + // We require that eSymbolContextSymbol be successfully filled in or this context is of no use to us. + if (pc_module_sp.get() + && (pc_module_sp->ResolveSymbolContextForAddress (m_current_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol) + { + m_sym_ctx_valid = true; + } + + AddressRange addr_range; + m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range); + + static ConstString g_sigtramp_name ("_sigtramp"); + if ((m_sym_ctx.function && m_sym_ctx.function->GetName() == g_sigtramp_name) || + (m_sym_ctx.symbol && m_sym_ctx.symbol->GetName() == g_sigtramp_name)) + { + m_frame_type = eSigtrampFrame; + } + else + { + // FIXME: Detect eDebuggerFrame here. + m_frame_type = eNormalFrame; + } + + // If we were able to find a symbol/function, set addr_range to the bounds of that symbol/function. + // else treat the current pc value as the start_pc and record no offset. + if (addr_range.GetBaseAddress().IsValid()) + { + m_start_pc = addr_range.GetBaseAddress(); + if (m_current_pc.GetSection() == m_start_pc.GetSection()) + { + m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset(); + } + else if (m_current_pc.GetModule() == m_start_pc.GetModule()) + { + // This means that whatever symbol we kicked up isn't really correct + // --- we should not cross section boundaries ... We really should NULL out + // the function/symbol in this case unless there is a bad assumption + // here due to inlined functions? + m_current_offset = m_current_pc.GetFileAddress() - m_start_pc.GetFileAddress(); + } + m_current_offset_backed_up_one = m_current_offset; + } + else + { + m_start_pc = m_current_pc; + m_current_offset = -1; + m_current_offset_backed_up_one = -1; + } + + // We've set m_frame_type and m_sym_ctx before these calls. + + m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame (); + m_full_unwind_plan_sp = GetFullUnwindPlanForFrame (); + + UnwindPlan::RowSP active_row; + int cfa_offset = 0; + int row_register_kind = -1; + if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + { + active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + row_register_kind = m_full_unwind_plan_sp->GetRegisterKind (); + if (active_row.get() && log) + { + StreamString active_row_strm; + active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); + UnwindLogMsg ("%s", active_row_strm.GetString().c_str()); + } + } + + if (!active_row.get()) + { + m_frame_type = eNotAValidFrame; + return; + } + + + addr_t cfa_regval = LLDB_INVALID_ADDRESS; + if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval)) + { + m_frame_type = eNotAValidFrame; + return; + } + + cfa_offset = active_row->GetCFAOffset (); + m_cfa = cfa_regval + cfa_offset; + + UnwindLogMsg ("cfa_regval = 0x%16.16" PRIx64 " (cfa_regval = 0x%16.16" PRIx64 ", cfa_offset = %i)", m_cfa, cfa_regval, cfa_offset); + UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " using %s UnwindPlan", + (uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()), + (uint64_t) m_cfa, + m_full_unwind_plan_sp->GetSourceName().GetCString()); +} + +// Initialize a RegisterContextLLDB for the non-zeroth frame -- rely on the RegisterContextLLDB "below" it +// to provide things like its current pc value. + +void +RegisterContextLLDB::InitializeNonZerothFrame() +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + if (IsFrameZero ()) + { + m_frame_type = eNotAValidFrame; + return; + } + + if (!GetNextFrame().get() || !GetNextFrame()->IsValid()) + { + m_frame_type = eNotAValidFrame; + return; + } + if (!m_thread.GetRegisterContext()) + { + m_frame_type = eNotAValidFrame; + return; + } + + addr_t pc; + if (!ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) + { + UnwindLogMsg ("could not get pc value"); + m_frame_type = eNotAValidFrame; + return; + } + + if (log) + { + UnwindLogMsg ("pc = 0x%16.16" PRIx64, pc); + addr_t reg_val; + if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val)) + UnwindLogMsg ("fp = 0x%16.16" PRIx64, reg_val); + if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val)) + UnwindLogMsg ("sp = 0x%16.16" PRIx64, reg_val); + } + + // A pc of 0x0 means it's the end of the stack crawl + if (pc == 0) + { + m_frame_type = eNotAValidFrame; + return; + } + + ExecutionContext exe_ctx(m_thread.shared_from_this()); + Process *process = exe_ctx.GetProcessPtr(); + // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs + // this will strip bit zero in case we read a PC from memory or from the LR. + ABI *abi = process->GetABI().get(); + if (abi) + pc = abi->FixCodeAddress(pc); + + process->GetTarget().GetSectionLoadList().ResolveLoadAddress (pc, m_current_pc); + + // If we don't have a Module for some reason, we're not going to find symbol/function information - just + // stick in some reasonable defaults and hope we can unwind past this frame. + ModuleSP pc_module_sp (m_current_pc.GetModule()); + if (!m_current_pc.IsValid() || !pc_module_sp) + { + UnwindLogMsg ("using architectural default unwind method"); + + // Test the pc value to see if we know it's in an unmapped/non-executable region of memory. + uint32_t permissions; + if (process->GetLoadAddressPermissions(pc, permissions) + && (permissions & ePermissionsExecutable) == 0) + { + // If this is the second frame off the stack, we may have unwound the first frame + // incorrectly. But using the architecture default unwind plan may get us back on + // track -- albeit possibly skipping a real frame. Give this frame a clearly-invalid + // pc and see if we can get any further. + if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsFrameZero()) + { + UnwindLogMsg ("had a pc of 0x%" PRIx64 " which is not in executable memory but on frame 1 -- allowing it once.", + (uint64_t) pc); + m_frame_type = eSkipFrame; + } + else + { + // anywhere other than the second frame, a non-executable pc means we're off in the weeds -- stop now. + m_frame_type = eNotAValidFrame; + return; + } + } + + if (abi) + { + m_fast_unwind_plan_sp.reset (); + m_full_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp); + if (m_frame_type != eSkipFrame) // don't override eSkipFrame + { + m_frame_type = eNormalFrame; + } + m_all_registers_available = false; + m_current_offset = -1; + m_current_offset_backed_up_one = -1; + addr_t cfa_regval = LLDB_INVALID_ADDRESS; + int row_register_kind = m_full_unwind_plan_sp->GetRegisterKind (); + UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0); + if (row.get()) + { + uint32_t cfa_regnum = row->GetCFARegister(); + int cfa_offset = row->GetCFAOffset(); + if (!ReadGPRValue (row_register_kind, cfa_regnum, cfa_regval)) + { + UnwindLogMsg ("failed to get cfa value"); + if (m_frame_type != eSkipFrame) // don't override eSkipFrame + { + m_frame_type = eNormalFrame; + } + return; + } + m_cfa = cfa_regval + cfa_offset; + + // A couple of sanity checks.. + if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1) + { + UnwindLogMsg ("could not find a valid cfa address"); + m_frame_type = eNotAValidFrame; + return; + } + + // cfa_regval should point into the stack memory; if we can query memory region permissions, + // see if the memory is allocated & readable. + if (process->GetLoadAddressPermissions(cfa_regval, permissions) + && (permissions & ePermissionsReadable) == 0) + { + m_frame_type = eNotAValidFrame; + return; + } + } + else + { + UnwindLogMsg ("could not find a row for function offset zero"); + m_frame_type = eNotAValidFrame; + return; + } + + UnwindLogMsg ("initialized frame cfa is 0x%" PRIx64, (uint64_t) m_cfa); + return; + } + m_frame_type = eNotAValidFrame; + return; + } + + // We require that eSymbolContextSymbol be successfully filled in or this context is of no use to us. + if ((pc_module_sp->ResolveSymbolContextForAddress (m_current_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol) + { + m_sym_ctx_valid = true; + } + + AddressRange addr_range; + if (!m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range)) + { + m_sym_ctx_valid = false; + } + + bool decr_pc_and_recompute_addr_range = false; + + // If the symbol lookup failed... + if (m_sym_ctx_valid == false) + decr_pc_and_recompute_addr_range = true; + + // Or if we're in the middle of the stack (and not "above" an asynchronous event like sigtramp), + // and our "current" pc is the start of a function... + if (m_sym_ctx_valid + && GetNextFrame()->m_frame_type != eSigtrampFrame + && GetNextFrame()->m_frame_type != eDebuggerFrame + && addr_range.GetBaseAddress().IsValid() + && addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection() + && addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset()) + { + decr_pc_and_recompute_addr_range = true; + } + + // We need to back up the pc by 1 byte and re-search for the Symbol to handle the case where the "saved pc" + // value is pointing to the next function, e.g. if a function ends with a CALL instruction. + // FIXME this may need to be an architectural-dependent behavior; if so we'll need to add a member function + // to the ABI plugin and consult that. + if (decr_pc_and_recompute_addr_range) + { + Address temporary_pc(m_current_pc); + temporary_pc.SetOffset(m_current_pc.GetOffset() - 1); + m_sym_ctx.Clear(false); + m_sym_ctx_valid = false; + if ((pc_module_sp->ResolveSymbolContextForAddress (temporary_pc, eSymbolContextFunction| eSymbolContextSymbol, m_sym_ctx) & eSymbolContextSymbol) == eSymbolContextSymbol) + { + m_sym_ctx_valid = true; + } + if (!m_sym_ctx.GetAddressRange (eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range)) + { + m_sym_ctx_valid = false; + } + } + + // If we were able to find a symbol/function, set addr_range_ptr to the bounds of that symbol/function. + // else treat the current pc value as the start_pc and record no offset. + if (addr_range.GetBaseAddress().IsValid()) + { + m_start_pc = addr_range.GetBaseAddress(); + m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset(); + m_current_offset_backed_up_one = m_current_offset; + if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0) + { + m_current_offset_backed_up_one--; + if (m_sym_ctx_valid) + m_current_pc.SetOffset(m_current_pc.GetOffset() - 1); + } + } + else + { + m_start_pc = m_current_pc; + m_current_offset = -1; + m_current_offset_backed_up_one = -1; + } + + static ConstString sigtramp_name ("_sigtramp"); + if ((m_sym_ctx.function && m_sym_ctx.function->GetMangled().GetMangledName() == sigtramp_name) + || (m_sym_ctx.symbol && m_sym_ctx.symbol->GetMangled().GetMangledName() == sigtramp_name)) + { + m_frame_type = eSigtrampFrame; + } + else + { + // FIXME: Detect eDebuggerFrame here. + if (m_frame_type != eSkipFrame) // don't override eSkipFrame + { + m_frame_type = eNormalFrame; + } + } + + // We've set m_frame_type and m_sym_ctx before this call. + m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame (); + + UnwindPlan::RowSP active_row; + int cfa_offset = 0; + int row_register_kind = -1; + + // Try to get by with just the fast UnwindPlan if possible - the full UnwindPlan may be expensive to get + // (e.g. if we have to parse the entire eh_frame section of an ObjectFile for the first time.) + + if (m_fast_unwind_plan_sp && m_fast_unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + { + active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + row_register_kind = m_fast_unwind_plan_sp->GetRegisterKind (); + if (active_row.get() && log) + { + StreamString active_row_strm; + active_row->Dump(active_row_strm, m_fast_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); + UnwindLogMsg ("active row: %s", active_row_strm.GetString().c_str()); + } + } + else + { + m_full_unwind_plan_sp = GetFullUnwindPlanForFrame (); + if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + { + active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + row_register_kind = m_full_unwind_plan_sp->GetRegisterKind (); + if (active_row.get() && log) + { + StreamString active_row_strm; + active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread, m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); + UnwindLogMsg ("active row: %s", active_row_strm.GetString().c_str()); + } + } + } + + if (!active_row.get()) + { + m_frame_type = eNotAValidFrame; + return; + } + + addr_t cfa_regval = LLDB_INVALID_ADDRESS; + if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval)) + { + UnwindLogMsg ("failed to get cfa reg %d/%d", row_register_kind, active_row->GetCFARegister()); + m_frame_type = eNotAValidFrame; + return; + } + + cfa_offset = active_row->GetCFAOffset (); + m_cfa = cfa_regval + cfa_offset; + + UnwindLogMsg ("cfa_regval = 0x%16.16" PRIx64 " (cfa_regval = 0x%16.16" PRIx64 ", cfa_offset = %i)", m_cfa, cfa_regval, cfa_offset); + + // A couple of sanity checks.. + if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1) + { + UnwindLogMsg ("could not find a valid cfa address"); + m_frame_type = eNotAValidFrame; + return; + } + + // If we have a bad stack setup, we can get the same CFA value multiple times -- or even + // more devious, we can actually oscillate between two CFA values. Detect that here and + // break out to avoid a possible infinite loop in lldb trying to unwind the stack. + addr_t next_frame_cfa; + addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS; + if (GetNextFrame().get() && GetNextFrame()->GetCFA(next_frame_cfa)) + { + bool repeating_frames = false; + if (next_frame_cfa == m_cfa) + { + repeating_frames = true; + } + else + { + if (GetNextFrame()->GetNextFrame() && GetNextFrame()->GetNextFrame()->GetCFA(next_next_frame_cfa) + && next_next_frame_cfa == m_cfa) + { + repeating_frames = true; + } + } + if (repeating_frames && abi->FunctionCallsChangeCFA()) + { + UnwindLogMsg ("same CFA address as next frame, assuming the unwind is looping - stopping"); + m_frame_type = eNotAValidFrame; + return; + } + } + + UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64, + (uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()), (uint64_t) m_cfa); +} + + +bool +RegisterContextLLDB::IsFrameZero () const +{ + return m_frame_number == 0; +} + + +// Find a fast unwind plan for this frame, if possible. +// +// On entry to this method, +// +// 1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame if either of those are correct, +// 2. m_sym_ctx should already be filled in, and +// 3. m_current_pc should have the current pc value for this frame +// 4. m_current_offset_backed_up_one should have the current byte offset into the function, maybe backed up by 1, -1 if unknown + +UnwindPlanSP +RegisterContextLLDB::GetFastUnwindPlanForFrame () +{ + UnwindPlanSP unwind_plan_sp; + ModuleSP pc_module_sp (m_current_pc.GetModule()); + + if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL) + return unwind_plan_sp; + + if (IsFrameZero ()) + return unwind_plan_sp; + + FuncUnwindersSP func_unwinders_sp (pc_module_sp->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx)); + if (!func_unwinders_sp) + return unwind_plan_sp; + + // If we're in _sigtramp(), unwinding past this frame requires special knowledge. + if (m_frame_type == eSigtrampFrame || m_frame_type == eDebuggerFrame) + return unwind_plan_sp; + + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanFastUnwind (m_thread); + if (unwind_plan_sp) + { + if (unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + { + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + if (log && log->GetVerbose()) + { + if (m_fast_unwind_plan_sp) + UnwindLogMsgVerbose ("frame, and has a fast UnwindPlan"); + else + UnwindLogMsgVerbose ("frame"); + } + m_frame_type = eNormalFrame; + return unwind_plan_sp; + } + else + { + unwind_plan_sp.reset(); + } + } + return unwind_plan_sp; +} + +// On entry to this method, +// +// 1. m_frame_type should already be set to eSigtrampFrame/eDebuggerFrame if either of those are correct, +// 2. m_sym_ctx should already be filled in, and +// 3. m_current_pc should have the current pc value for this frame +// 4. m_current_offset_backed_up_one should have the current byte offset into the function, maybe backed up by 1, -1 if unknown + +UnwindPlanSP +RegisterContextLLDB::GetFullUnwindPlanForFrame () +{ + UnwindPlanSP unwind_plan_sp; + UnwindPlanSP arch_default_unwind_plan_sp; + ExecutionContext exe_ctx(m_thread.shared_from_this()); + Process *process = exe_ctx.GetProcessPtr(); + ABI *abi = process ? process->GetABI().get() : NULL; + if (abi) + { + arch_default_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp); + } + + bool behaves_like_zeroth_frame = false; + if (IsFrameZero () + || GetNextFrame()->m_frame_type == eSigtrampFrame + || GetNextFrame()->m_frame_type == eDebuggerFrame) + { + behaves_like_zeroth_frame = true; + // If this frame behaves like a 0th frame (currently executing or + // interrupted asynchronously), all registers can be retrieved. + m_all_registers_available = true; + } + + // If we've done a jmp 0x0 / bl 0x0 (called through a null function pointer) so the pc is 0x0 + // in the zeroth frame, we need to use the "unwind at first instruction" arch default UnwindPlan + // Also, if this Process can report on memory region attributes, any non-executable region means + // we jumped through a bad function pointer - handle the same way as 0x0. + // Note, if the symbol context has a function for the symbol, then we don't need to do this check. + + if ((!m_sym_ctx_valid || m_sym_ctx.function == NULL) && behaves_like_zeroth_frame && m_current_pc.IsValid()) + { + uint32_t permissions; + addr_t current_pc_addr = m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()); + if (current_pc_addr == 0 + || (process->GetLoadAddressPermissions(current_pc_addr, permissions) + && (permissions & ePermissionsExecutable) == 0)) + { + unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp); + m_frame_type = eNormalFrame; + return unwind_plan_sp; + } + } + + // No Module for the current pc, try using the architecture default unwind. + ModuleSP pc_module_sp (m_current_pc.GetModule()); + if (!m_current_pc.IsValid() || !pc_module_sp || pc_module_sp->GetObjectFile() == NULL) + { + m_frame_type = eNormalFrame; + return arch_default_unwind_plan_sp; + } + + FuncUnwindersSP func_unwinders_sp; + if (m_sym_ctx_valid) + { + func_unwinders_sp = pc_module_sp->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx); + } + + // No FuncUnwinders available for this pc (i.e. a stripped function symbol and -fomit-frame-pointer). + // Try using the eh_frame information relative to the current PC, + // and finally fall back on the architectural default unwind. + if (!func_unwinders_sp) + { + DWARFCallFrameInfo *eh_frame = pc_module_sp && pc_module_sp->GetObjectFile() ? + pc_module_sp->GetObjectFile()->GetUnwindTable().GetEHFrameInfo() : nullptr; + + m_frame_type = eNormalFrame; + if (eh_frame && m_current_pc.IsValid()) + { + unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + // Even with -fomit-frame-pointer, we can try eh_frame to get back on track. + if (eh_frame->GetUnwindPlan (m_current_pc, *unwind_plan_sp)) + return unwind_plan_sp; + else + unwind_plan_sp.reset(); + } + return arch_default_unwind_plan_sp; + } + + // If we're in _sigtramp(), unwinding past this frame requires special knowledge. On Mac OS X this knowledge + // is properly encoded in the eh_frame section, so prefer that if available. + // On other platforms we may need to provide a platform-specific UnwindPlan which encodes the details of + // how to unwind out of sigtramp. + if (m_frame_type == eSigtrampFrame) + { + m_fast_unwind_plan_sp.reset(); + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one); + if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + return unwind_plan_sp; + } + + // Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame even when it's frame zero + // This comes up if we have hand-written functions in a Module and hand-written eh_frame. The assembly + // instruction inspection may fail and the eh_frame CFI were probably written with some care to do the + // right thing. It'd be nice if there was a way to ask the eh_frame directly if it is asynchronous + // (can be trusted at every instruction point) or synchronous (the normal case - only at call sites). + // But there is not. + if (process && process->GetDynamicLoader() && process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo (m_sym_ctx)) + { + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one); + if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + { + UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan because the DynamicLoader suggested we prefer it", + unwind_plan_sp->GetSourceName().GetCString()); + return unwind_plan_sp; + } + } + + // Typically the NonCallSite UnwindPlan is the unwind created by inspecting the assembly language instructions + if (behaves_like_zeroth_frame) + { + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread); + if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + { + UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); + return unwind_plan_sp; + } + } + + // Typically this is unwind info from an eh_frame section intended for exception handling; only valid at call sites + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one); + if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + { + UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); + return unwind_plan_sp; + } + + // We'd prefer to use an UnwindPlan intended for call sites when we're at a call site but if we've + // struck out on that, fall back to using the non-call-site assembly inspection UnwindPlan if possible. + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread); + if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc)) + { + UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString()); + return unwind_plan_sp; + } + + // If nothing else, use the architectural default UnwindPlan and hope that does the job. + UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", arch_default_unwind_plan_sp->GetSourceName().GetCString()); + return arch_default_unwind_plan_sp; +} + + +void +RegisterContextLLDB::InvalidateAllRegisters () +{ + m_frame_type = eNotAValidFrame; +} + +size_t +RegisterContextLLDB::GetRegisterCount () +{ + return m_thread.GetRegisterContext()->GetRegisterCount(); +} + +const RegisterInfo * +RegisterContextLLDB::GetRegisterInfoAtIndex (size_t reg) +{ + return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex (reg); +} + +size_t +RegisterContextLLDB::GetRegisterSetCount () +{ + return m_thread.GetRegisterContext()->GetRegisterSetCount (); +} + +const RegisterSet * +RegisterContextLLDB::GetRegisterSet (size_t reg_set) +{ + return m_thread.GetRegisterContext()->GetRegisterSet (reg_set); +} + +uint32_t +RegisterContextLLDB::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num); +} + +bool +RegisterContextLLDB::ReadRegisterValueFromRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc, + const RegisterInfo *reg_info, + RegisterValue &value) +{ + if (!IsValid()) + return false; + bool success = false; + + switch (regloc.type) + { + case UnwindLLDB::RegisterLocation::eRegisterInRegister: + { + const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); + + if (!other_reg_info) + return false; + + if (IsFrameZero ()) + { + success = m_thread.GetRegisterContext()->ReadRegister (other_reg_info, value); + } + else + { + success = GetNextFrame()->ReadRegister (other_reg_info, value); + } + } + break; + case UnwindLLDB::RegisterLocation::eRegisterValueInferred: + success = value.SetUInt (regloc.location.inferred_value, reg_info->byte_size); + break; + + case UnwindLLDB::RegisterLocation::eRegisterNotSaved: + break; + case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation: + assert ("FIXME debugger inferior function call unwind"); + break; + case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: + { + Error error (ReadRegisterValueFromMemory(reg_info, + regloc.location.target_memory_location, + reg_info->byte_size, + value)); + success = error.Success(); + } + break; + default: + assert ("Unknown RegisterLocation type."); + break; + } + return success; +} + +bool +RegisterContextLLDB::WriteRegisterValueToRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc, + const RegisterInfo *reg_info, + const RegisterValue &value) +{ + if (!IsValid()) + return false; + + bool success = false; + + switch (regloc.type) + { + case UnwindLLDB::RegisterLocation::eRegisterInRegister: + { + const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); + if (IsFrameZero ()) + { + success = m_thread.GetRegisterContext()->WriteRegister (other_reg_info, value); + } + else + { + success = GetNextFrame()->WriteRegister (other_reg_info, value); + } + } + break; + case UnwindLLDB::RegisterLocation::eRegisterValueInferred: + case UnwindLLDB::RegisterLocation::eRegisterNotSaved: + break; + case UnwindLLDB::RegisterLocation::eRegisterSavedAtHostMemoryLocation: + assert ("FIXME debugger inferior function call unwind"); + break; + case UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation: + { + Error error (WriteRegisterValueToMemory (reg_info, + regloc.location.target_memory_location, + reg_info->byte_size, + value)); + success = error.Success(); + } + break; + default: + assert ("Unknown RegisterLocation type."); + break; + } + return success; +} + + +bool +RegisterContextLLDB::IsValid () const +{ + return m_frame_type != eNotAValidFrame; +} + +// A skip frame is a bogus frame on the stack -- but one where we're likely to find a real frame farther +// up the stack if we keep looking. It's always the second frame in an unwind (i.e. the first frame after +// frame zero) where unwinding can be the trickiest. Ideally we'll mark up this frame in some way so the +// user knows we're displaying bad data and we may have skipped one frame of their real program in the +// process of getting back on track. + +bool +RegisterContextLLDB::IsSkipFrame () const +{ + return m_frame_type == eSkipFrame; +} + +// Answer the question: Where did THIS frame save the CALLER frame ("previous" frame)'s register value? + +enum UnwindLLDB::RegisterSearchResult +RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc) +{ + // Have we already found this register location? + if (!m_registers.empty()) + { + std::map<uint32_t, lldb_private::UnwindLLDB::RegisterLocation>::const_iterator iterator; + iterator = m_registers.find (lldb_regnum); + if (iterator != m_registers.end()) + { + regloc = iterator->second; + UnwindLogMsg ("supplying caller's saved reg %d's location, cached", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + } + + uint32_t sp_regnum = LLDB_INVALID_REGNUM; + uint32_t pc_regnum = LLDB_INVALID_REGNUM; + m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, eRegisterKindLLDB, sp_regnum); + m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, eRegisterKindLLDB, pc_regnum); + + // Are we looking for the CALLER's stack pointer? The stack pointer is defined to be the same as THIS frame's + // CFA so just return the CFA value. This is true on x86-32/x86-64 at least. + if (sp_regnum != LLDB_INVALID_REGNUM && sp_regnum == lldb_regnum) + { + // make sure we won't lose precision copying an addr_t (m_cfa) into a uint64_t (.inferred_value) + assert (sizeof (addr_t) <= sizeof (uint64_t)); + regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; + regloc.location.inferred_value = m_cfa; + m_registers[lldb_regnum] = regloc; + UnwindLogMsg ("supplying caller's stack pointer (%d) value, computed from CFA", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + + // Look through the available UnwindPlans for the register location. + + UnwindPlan::Row::RegisterLocation unwindplan_regloc; + bool have_unwindplan_regloc = false; + RegisterKind unwindplan_registerkind = (RegisterKind)-1; + + if (m_fast_unwind_plan_sp) + { + UnwindPlan::RowSP active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind (); + uint32_t row_regnum; + if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum)) + { + UnwindLogMsg ("could not convert lldb regnum %d into %d RegisterKind reg numbering scheme", + lldb_regnum, (int) unwindplan_registerkind); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc)) + { + UnwindLogMsg ("supplying caller's saved reg %d's location using FastUnwindPlan", lldb_regnum); + have_unwindplan_regloc = true; + } + } + + if (!have_unwindplan_regloc) + { + // m_full_unwind_plan_sp being NULL means that we haven't tried to find a full UnwindPlan yet + if (!m_full_unwind_plan_sp) + m_full_unwind_plan_sp = GetFullUnwindPlanForFrame (); + + if (m_full_unwind_plan_sp) + { + UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind (); + uint32_t row_regnum; + bool row_register_rewritten_to_return_address_reg = false; + + // If we're fetching the saved pc and this UnwindPlan defines a ReturnAddress register (e.g. lr on arm), + // look for the return address register number in the UnwindPlan's row. + if (lldb_regnum == pc_regnum && m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM) + { + row_regnum = m_full_unwind_plan_sp->GetReturnAddressRegister(); + row_register_rewritten_to_return_address_reg = true; + UnwindLogMsg ("requested caller's saved PC but this UnwindPlan uses a RA reg; getting reg %d instead", + row_regnum); + } + else + { + if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum)) + { + if (unwindplan_registerkind == eRegisterKindGeneric) + UnwindLogMsg ("could not convert lldb regnum %d into eRegisterKindGeneric reg numbering scheme", lldb_regnum); + else + UnwindLogMsg ("could not convert lldb regnum %d into %d RegisterKind reg numbering scheme", + lldb_regnum, (int) unwindplan_registerkind); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + } + + if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc)) + { + have_unwindplan_regloc = true; + UnwindLogMsg ("supplying caller's saved reg %d's location using %s UnwindPlan", lldb_regnum, + m_full_unwind_plan_sp->GetSourceName().GetCString()); + } + + // This is frame 0 and we're retrieving the PC and it's saved in a Return Address register and + // it hasn't been saved anywhere yet -- that is, it's still live in the actual register. + // Handle this specially. + + if (have_unwindplan_regloc == false + && row_register_rewritten_to_return_address_reg == true + && IsFrameZero() + && row_regnum != LLDB_INVALID_REGNUM) + { + uint32_t ra_regnum_in_lldb_reg_numbering; + if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (unwindplan_registerkind, row_regnum, eRegisterKindLLDB, ra_regnum_in_lldb_reg_numbering)) + { + lldb_private::UnwindLLDB::RegisterLocation new_regloc; + new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; + new_regloc.location.register_number = ra_regnum_in_lldb_reg_numbering; + m_registers[lldb_regnum] = new_regloc; + regloc = new_regloc; + UnwindLogMsg ("supplying caller's register %d from the live RegisterContext at frame 0, saved in %d", lldb_regnum, ra_regnum_in_lldb_reg_numbering); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + } + + // If this architecture stores the return address in a register (it defines a Return Address register) + // and we're on a non-zero stack frame and the Full UnwindPlan says that the pc is stored in the + // RA registers (e.g. lr on arm), then we know that the full unwindplan is not trustworthy -- this + // is an impossible situation and the instruction emulation code has likely been misled. + // If this stack frame meets those criteria, we need to throw away the Full UnwindPlan that the + // instruction emulation came up with and fall back to the architecture's Default UnwindPlan so + // the stack walk can get past this point. + + // Special note: If the Full UnwindPlan was generated from the compiler, don't second-guess it + // when we're at a call site location. + + // arch_default_ra_regnum is the return address register # in the Full UnwindPlan register numbering + uint32_t arch_default_ra_regnum = LLDB_INVALID_REGNUM; + if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, unwindplan_registerkind, arch_default_ra_regnum) + && arch_default_ra_regnum != LLDB_INVALID_REGNUM + && pc_regnum != LLDB_INVALID_REGNUM + && pc_regnum == lldb_regnum + && unwindplan_regloc.IsInOtherRegister() + && unwindplan_regloc.GetRegisterNumber() == arch_default_ra_regnum + && m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes + && !m_all_registers_available) + { + UnwindLogMsg ("%s UnwindPlan tried to restore the pc from the link register but this is a non-zero frame", + m_full_unwind_plan_sp->GetSourceName().GetCString()); + + // Throw away the full unwindplan; install the arch default unwindplan + InvalidateFullUnwindPlan(); + + // Now re-fetch the pc value we're searching for + uint32_t arch_default_pc_reg = LLDB_INVALID_REGNUM; + UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, m_full_unwind_plan_sp->GetRegisterKind(), arch_default_pc_reg) + && arch_default_pc_reg != LLDB_INVALID_REGNUM + && active_row + && active_row->GetRegisterInfo (arch_default_pc_reg, unwindplan_regloc)) + { + have_unwindplan_regloc = true; + } + else + { + have_unwindplan_regloc = false; + } + } + } + } + + + ExecutionContext exe_ctx(m_thread.shared_from_this()); + Process *process = exe_ctx.GetProcessPtr(); + if (have_unwindplan_regloc == false) + { + // If a volatile register is being requested, we don't want to forward the next frame's register contents + // up the stack -- the register is not retrievable at this frame. + ABI *abi = process ? process->GetABI().get() : NULL; + if (abi) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum); + if (reg_info && abi->RegisterIsVolatile (reg_info)) + { + UnwindLogMsg ("did not supply reg location for %d because it is volatile", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile; + } + } + + if (IsFrameZero ()) + { + // This is frame 0 - we should return the actual live register context value + lldb_private::UnwindLLDB::RegisterLocation new_regloc; + new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; + new_regloc.location.register_number = lldb_regnum; + m_registers[lldb_regnum] = new_regloc; + regloc = new_regloc; + UnwindLogMsg ("supplying caller's register %d from the live RegisterContext at frame 0", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + else + UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + + // unwindplan_regloc has valid contents about where to retrieve the register + if (unwindplan_regloc.IsUnspecified()) + { + lldb_private::UnwindLLDB::RegisterLocation new_regloc; + new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterNotSaved; + m_registers[lldb_regnum] = new_regloc; + UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + + if (unwindplan_regloc.IsSame()) + { + if (IsFrameZero ()) + { + UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + else + { + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + } + + if (unwindplan_regloc.IsCFAPlusOffset()) + { + int offset = unwindplan_regloc.GetOffset(); + regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; + regloc.location.inferred_value = m_cfa + offset; + m_registers[lldb_regnum] = regloc; + UnwindLogMsg ("supplying caller's register %d, value is CFA plus offset", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + + if (unwindplan_regloc.IsAtCFAPlusOffset()) + { + int offset = unwindplan_regloc.GetOffset(); + regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; + regloc.location.target_memory_location = m_cfa + offset; + m_registers[lldb_regnum] = regloc; + UnwindLogMsg ("supplying caller's register %d from the stack, saved at CFA plus offset", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + + if (unwindplan_regloc.IsInOtherRegister()) + { + uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber(); + uint32_t row_regnum_in_lldb; + if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (unwindplan_registerkind, unwindplan_regnum, eRegisterKindLLDB, row_regnum_in_lldb)) + { + UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; + regloc.location.register_number = row_regnum_in_lldb; + m_registers[lldb_regnum] = regloc; + UnwindLogMsg ("supplying caller's register %d, saved in register %d", lldb_regnum, row_regnum_in_lldb); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + + if (unwindplan_regloc.IsDWARFExpression() || unwindplan_regloc.IsAtDWARFExpression()) + { + DataExtractor dwarfdata (unwindplan_regloc.GetDWARFExpressionBytes(), + unwindplan_regloc.GetDWARFExpressionLength(), + process->GetByteOrder(), process->GetAddressByteSize()); + DWARFExpression dwarfexpr (dwarfdata, 0, unwindplan_regloc.GetDWARFExpressionLength()); + dwarfexpr.SetRegisterKind (unwindplan_registerkind); + Value result; + Error error; + if (dwarfexpr.Evaluate (&exe_ctx, NULL, NULL, this, 0, NULL, result, &error)) + { + addr_t val; + val = result.GetScalar().ULongLong(); + if (unwindplan_regloc.IsDWARFExpression()) + { + regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; + regloc.location.inferred_value = val; + m_registers[lldb_regnum] = regloc; + UnwindLogMsg ("supplying caller's register %d via DWARF expression (IsDWARFExpression)", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + else + { + regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; + regloc.location.target_memory_location = val; + m_registers[lldb_regnum] = regloc; + UnwindLogMsg ("supplying caller's register %d via DWARF expression (IsAtDWARFExpression)", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + } + UnwindLogMsg ("tried to use IsDWARFExpression or IsAtDWARFExpression for reg %d but failed", lldb_regnum); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + + UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + + // FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are unsupported. + + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; +} + +// If the Full unwindplan has been determined to be incorrect, this method will +// replace it with the architecture's default unwindplna, if one is defined. +// It will also find the FuncUnwinders object for this function and replace the +// Full unwind method for the function there so we don't use the errant Full unwindplan +// again in the future of this debug session. +// We're most likely doing this because the Full unwindplan was generated by assembly +// instruction profiling and the profiler got something wrong. + +void +RegisterContextLLDB::InvalidateFullUnwindPlan () +{ + UnwindPlan::Row::RegisterLocation unwindplan_regloc; + ExecutionContext exe_ctx (m_thread.shared_from_this()); + Process *process = exe_ctx.GetProcessPtr(); + ABI *abi = process ? process->GetABI().get() : NULL; + if (abi) + { + UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp; + UnwindPlanSP arch_default_unwind_plan_sp; + arch_default_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp); + if (arch_default_unwind_plan_sp) + { + UnwindPlan::RowSP active_row = arch_default_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + + if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM) + { + FuncUnwindersSP func_unwinders_sp; + if (m_sym_ctx_valid && m_current_pc.IsValid() && m_current_pc.GetModule()) + { + func_unwinders_sp = m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx); + if (func_unwinders_sp) + { + func_unwinders_sp->InvalidateNonCallSiteUnwindPlan (m_thread); + } + } + m_registers.clear(); + m_full_unwind_plan_sp = arch_default_unwind_plan_sp; + addr_t cfa_regval = LLDB_INVALID_ADDRESS; + if (ReadGPRValue (arch_default_unwind_plan_sp->GetRegisterKind(), active_row->GetCFARegister(), cfa_regval)) + { + m_cfa = cfa_regval + active_row->GetCFAOffset (); + } + + UnwindLogMsg ("full unwind plan '%s' has been replaced by architecture default unwind plan '%s' for this function from now on.", + original_full_unwind_plan_sp->GetSourceName().GetCString(), arch_default_unwind_plan_sp->GetSourceName().GetCString()); + } + } + } +} + +// Retrieve a general purpose register value for THIS frame, as saved by the NEXT frame, i.e. the frame that +// this frame called. e.g. +// +// foo () { } +// bar () { foo (); } +// main () { bar (); } +// +// stopped in foo() so +// frame 0 - foo +// frame 1 - bar +// frame 2 - main +// and this RegisterContext is for frame 1 (bar) - if we want to get the pc value for frame 1, we need to ask +// where frame 0 (the "next" frame) saved that and retrieve the value. + +bool +RegisterContextLLDB::ReadGPRValue (int register_kind, uint32_t regnum, addr_t &value) +{ + if (!IsValid()) + return false; + + uint32_t lldb_regnum; + if (register_kind == eRegisterKindLLDB) + { + lldb_regnum = regnum; + } + else if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindLLDB, lldb_regnum)) + { + return false; + } + + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum); + RegisterValue reg_value; + // if this is frame 0 (currently executing frame), get the requested reg contents from the actual thread registers + if (IsFrameZero ()) + { + if (m_thread.GetRegisterContext()->ReadRegister (reg_info, reg_value)) + { + value = reg_value.GetAsUInt64(); + return true; + } + return false; + } + + bool pc_register = false; + uint32_t generic_regnum; + if (register_kind == eRegisterKindGeneric && regnum == LLDB_REGNUM_GENERIC_PC) + { + pc_register = true; + } + else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindGeneric, generic_regnum) + && generic_regnum == LLDB_REGNUM_GENERIC_PC) + { + pc_register = true; + } + + lldb_private::UnwindLLDB::RegisterLocation regloc; + if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, pc_register)) + { + return false; + } + if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value)) + { + value = reg_value.GetAsUInt64(); + return true; + } + return false; +} + +// Find the value of a register in THIS frame + +bool +RegisterContextLLDB::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value) +{ + if (!IsValid()) + return false; + + const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB]; + UnwindLogMsgVerbose ("looking for register saved location for reg %d", lldb_regnum); + + // If this is the 0th frame, hand this over to the live register context + if (IsFrameZero ()) + { + UnwindLogMsgVerbose ("passing along to the live register context for reg %d", lldb_regnum); + return m_thread.GetRegisterContext()->ReadRegister (reg_info, value); + } + + lldb_private::UnwindLLDB::RegisterLocation regloc; + // Find out where the NEXT frame saved THIS frame's register contents + if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, false)) + return false; + + return ReadRegisterValueFromRegisterLocation (regloc, reg_info, value); +} + +bool +RegisterContextLLDB::WriteRegister (const RegisterInfo *reg_info, const RegisterValue &value) +{ + if (!IsValid()) + return false; + + const uint32_t lldb_regnum = reg_info->kinds[eRegisterKindLLDB]; + UnwindLogMsgVerbose ("looking for register saved location for reg %d", lldb_regnum); + + // If this is the 0th frame, hand this over to the live register context + if (IsFrameZero ()) + { + UnwindLogMsgVerbose ("passing along to the live register context for reg %d", lldb_regnum); + return m_thread.GetRegisterContext()->WriteRegister (reg_info, value); + } + + lldb_private::UnwindLLDB::RegisterLocation regloc; + // Find out where the NEXT frame saved THIS frame's register contents + if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, false)) + return false; + + return WriteRegisterValueToRegisterLocation (regloc, reg_info, value); +} + +// Don't need to implement this one +bool +RegisterContextLLDB::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + return false; +} + +// Don't need to implement this one +bool +RegisterContextLLDB::WriteAllRegisterValues (const lldb::DataBufferSP& data_sp) +{ + return false; +} + +// Retrieve the pc value for THIS from + +bool +RegisterContextLLDB::GetCFA (addr_t& cfa) +{ + if (!IsValid()) + { + return false; + } + if (m_cfa == LLDB_INVALID_ADDRESS) + { + return false; + } + cfa = m_cfa; + return true; +} + + +RegisterContextLLDB::SharedPtr +RegisterContextLLDB::GetNextFrame () const +{ + RegisterContextLLDB::SharedPtr regctx; + if (m_frame_number == 0) + return regctx; + return m_parent_unwind.GetRegisterContextForFrameNum (m_frame_number - 1); +} + +RegisterContextLLDB::SharedPtr +RegisterContextLLDB::GetPrevFrame () const +{ + RegisterContextLLDB::SharedPtr regctx; + return m_parent_unwind.GetRegisterContextForFrameNum (m_frame_number + 1); +} + +// Retrieve the address of the start of the function of THIS frame + +bool +RegisterContextLLDB::GetStartPC (addr_t& start_pc) +{ + if (!IsValid()) + return false; + + if (!m_start_pc.IsValid()) + { + return ReadPC (start_pc); + } + start_pc = m_start_pc.GetLoadAddress (CalculateTarget().get()); + return true; +} + +// Retrieve the current pc value for THIS frame, as saved by the NEXT frame. + +bool +RegisterContextLLDB::ReadPC (addr_t& pc) +{ + if (!IsValid()) + return false; + + if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) + { + // A pc value of 0 or 1 is impossible in the middle of the stack -- it indicates the end of a stack walk. + // On the currently executing frame (or such a frame interrupted asynchronously by sigtramp et al) this may + // occur if code has jumped through a NULL pointer -- we want to be able to unwind past that frame to help + // find the bug. + + if (m_all_registers_available == false + && (pc == 0 || pc == 1)) + { + return false; + } + else + { + return true; + } + } + else + { + return false; + } +} + + +void +RegisterContextLLDB::UnwindLogMsg (const char *fmt, ...) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + if (log) + { + va_list args; + va_start (args, fmt); + + char *logmsg; + if (vasprintf (&logmsg, fmt, args) == -1 || logmsg == NULL) + { + if (logmsg) + free (logmsg); + va_end (args); + return; + } + va_end (args); + + log->Printf ("%*sth%d/fr%u %s", + m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number, + logmsg); + free (logmsg); + } +} + +void +RegisterContextLLDB::UnwindLogMsgVerbose (const char *fmt, ...) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + if (log && log->GetVerbose()) + { + va_list args; + va_start (args, fmt); + + char *logmsg; + if (vasprintf (&logmsg, fmt, args) == -1 || logmsg == NULL) + { + if (logmsg) + free (logmsg); + va_end (args); + return; + } + va_end (args); + + log->Printf ("%*sth%d/fr%u %s", + m_frame_number < 100 ? m_frame_number : 100, "", m_thread.GetIndexID(), m_frame_number, + logmsg); + free (logmsg); + } +} + diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.h b/source/Plugins/Process/Utility/RegisterContextLLDB.h new file mode 100644 index 000000000000..dc6d8c61fa4a --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextLLDB.h @@ -0,0 +1,212 @@ +//===-- RegisterContextLLDB.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_RegisterContextLLDB_h_ +#define lldb_RegisterContextLLDB_h_ + +#include <vector> + +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Symbol/SymbolContext.h" +#include "UnwindLLDB.h" + +namespace lldb_private { + +class UnwindLLDB; + +class RegisterContextLLDB : public lldb_private::RegisterContext +{ +public: + typedef std::shared_ptr<RegisterContextLLDB> SharedPtr; + + RegisterContextLLDB (lldb_private::Thread &thread, + const SharedPtr& next_frame, + lldb_private::SymbolContext& sym_ctx, + uint32_t frame_number, lldb_private::UnwindLLDB& unwind_lldb); + + /// + // pure virtual functions from the base class that we must implement + /// + + virtual + ~RegisterContextLLDB () { } + + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb_private::RegisterSet * + GetRegisterSet (size_t reg_set); + + virtual bool + ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + bool + IsValid () const; + + bool + GetCFA (lldb::addr_t& cfa); + + bool + GetStartPC (lldb::addr_t& start_pc); + + bool + ReadPC (lldb::addr_t& start_pc); + +private: + + enum FrameType + { + eNormalFrame, + eSigtrampFrame, + eDebuggerFrame, // a debugger inferior function call frame; we get caller's registers from debugger + eSkipFrame, // The unwind resulted in a bogus frame but may get back on track so we don't want to give up yet + eNotAValidFrame // this frame is invalid for some reason - most likely it is past the top (end) of the stack + }; + + // UnwindLLDB needs to pass around references to RegisterLocations + friend class UnwindLLDB; + + // Indicates whether this frame is frame zero -- the currently + // executing frame -- or not. + bool + IsFrameZero () const; + + void + InitializeZerothFrame (); + + void + InitializeNonZerothFrame(); + + SharedPtr + GetNextFrame () const; + + SharedPtr + GetPrevFrame () const; + + // A SkipFrame occurs when the unwind out of frame 0 didn't go right -- we've got one bogus frame at frame #1. + // There is a good chance we'll get back on track if we follow the frame pointer chain (or whatever is appropriate + // on this ABI) so we allow one invalid frame to be in the stack. Ideally we'll mark this frame specially at some + // point and indicate to the user that the unwinder had a hiccup. Often when this happens we will miss a frame of + // the program's actual stack in the unwind and we want to flag that for the user somehow. + bool + IsSkipFrame () const; + + // Provide a location for where THIS function saved the CALLER's register value + // Or a frame "below" this one saved it, i.e. a function called by this one, preserved a register that this + // function didn't modify/use. + // + // The RegisterLocation type may be set to eRegisterNotAvailable -- this will happen for a volatile register + // being queried mid-stack. Instead of floating frame 0's contents of that register up the stack (which may + // or may not be the value of that reg when the function was executing), we won't return any value. + // + // If a non-volatile register (a "preserved" register) is requested mid-stack and no frames "below" the requested + // stack have saved the register anywhere, it is safe to assume that frame 0's register values are still the same + // as the requesting frame's. + lldb_private::UnwindLLDB::RegisterSearchResult + SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc); + + bool + ReadRegisterValueFromRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc, + const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue &value); + + bool + WriteRegisterValueToRegisterLocation (lldb_private::UnwindLLDB::RegisterLocation regloc, + const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue &value); + + void + InvalidateFullUnwindPlan (); + + // Get the contents of a general purpose (address-size) register for this frame + // (usually retrieved from the next frame) + bool + ReadGPRValue (int register_kind, uint32_t regnum, lldb::addr_t &value); + + lldb::UnwindPlanSP + GetFastUnwindPlanForFrame (); + + lldb::UnwindPlanSP + GetFullUnwindPlanForFrame (); + + void + UnwindLogMsg (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + UnwindLogMsgVerbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + lldb_private::Thread& m_thread; + + /// + // The following tell us how to retrieve the CALLER's register values (ie the "previous" frame, aka the frame above) + // i.e. where THIS frame saved them + /// + + lldb::UnwindPlanSP m_fast_unwind_plan_sp; // may be NULL + lldb::UnwindPlanSP m_full_unwind_plan_sp; + bool m_all_registers_available; // Can we retrieve all regs or just nonvolatile regs? + int m_frame_type; // enum FrameType + + lldb::addr_t m_cfa; + lldb_private::Address m_start_pc; + lldb_private::Address m_current_pc; + + int m_current_offset; // how far into the function we've executed; -1 if unknown + // 0 if no instructions have been executed yet. + + int m_current_offset_backed_up_one; // how far into the function we've executed; -1 if unknown + // 0 if no instructions have been executed yet. + // On architectures where the return address on the stack points + // to the instruction after the CALL, this value will have 1 + // subtracted from it. Else a function that ends in a CALL will + // have an offset pointing into the next function's address range. + // m_current_pc has the actual address of the "current" pc. + + lldb_private::SymbolContext& m_sym_ctx; + bool m_sym_ctx_valid; // if ResolveSymbolContextForAddress fails, don't try to use m_sym_ctx + + uint32_t m_frame_number; // What stack frame this RegisterContext is + + std::map<uint32_t, lldb_private::UnwindLLDB::RegisterLocation> m_registers; // where to find reg values for this frame + + lldb_private::UnwindLLDB& m_parent_unwind; // The UnwindLLDB that is creating this RegisterContextLLDB + + //------------------------------------------------------------------ + // For RegisterContextLLDB only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (RegisterContextLLDB); +}; + +} // namespace lldb_private + +#endif // lldb_RegisterContextLLDB_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp new file mode 100644 index 000000000000..2c3eee452488 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp @@ -0,0 +1,206 @@ +//===-- RegisterContextMacOSXFrameBackchain.cpp -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMacOSXFrameBackchain.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Thread.h" +// Project includes +#include "Utility/StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// RegisterContextMacOSXFrameBackchain constructor +//---------------------------------------------------------------------- +RegisterContextMacOSXFrameBackchain::RegisterContextMacOSXFrameBackchain +( + Thread &thread, + uint32_t concrete_frame_idx, + const UnwindMacOSXFrameBackchain::Cursor &cursor +) : + RegisterContext (thread, concrete_frame_idx), + m_cursor (cursor), + m_cursor_is_valid (true) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RegisterContextMacOSXFrameBackchain::~RegisterContextMacOSXFrameBackchain() +{ +} + +void +RegisterContextMacOSXFrameBackchain::InvalidateAllRegisters () +{ + m_cursor_is_valid = false; +} + +size_t +RegisterContextMacOSXFrameBackchain::GetRegisterCount () +{ + return m_thread.GetRegisterContext()->GetRegisterCount(); +} + +const RegisterInfo * +RegisterContextMacOSXFrameBackchain::GetRegisterInfoAtIndex (size_t reg) +{ + return m_thread.GetRegisterContext()->GetRegisterInfoAtIndex(reg); +} + +size_t +RegisterContextMacOSXFrameBackchain::GetRegisterSetCount () +{ + return m_thread.GetRegisterContext()->GetRegisterSetCount(); +} + + + +const RegisterSet * +RegisterContextMacOSXFrameBackchain::GetRegisterSet (size_t reg_set) +{ + return m_thread.GetRegisterContext()->GetRegisterSet (reg_set); +} + + + +bool +RegisterContextMacOSXFrameBackchain::ReadRegister (const RegisterInfo *reg_info, + RegisterValue &value) +{ + if (!m_cursor_is_valid) + return false; + + uint64_t reg_value = LLDB_INVALID_ADDRESS; + + switch (reg_info->kinds[eRegisterKindGeneric]) + { + case LLDB_REGNUM_GENERIC_PC: + if (m_cursor.pc == LLDB_INVALID_ADDRESS) + return false; + reg_value = m_cursor.pc; + break; + + case LLDB_REGNUM_GENERIC_FP: + if (m_cursor.fp == LLDB_INVALID_ADDRESS) + return false; + reg_value = m_cursor.fp; + break; + + default: + return false; + } + + switch (reg_info->encoding) + { + case eEncodingInvalid: + case eEncodingVector: + break; + + case eEncodingUint: + case eEncodingSint: + value.SetUInt(reg_value, reg_info->byte_size); + return true; + + case eEncodingIEEE754: + switch (reg_info->byte_size) + { + case sizeof (float): + if (sizeof (float) == sizeof(uint32_t)) + { + value.SetUInt32(reg_value, RegisterValue::eTypeFloat); + return true; + } + else if (sizeof (float) == sizeof(uint64_t)) + { + value.SetUInt64(reg_value, RegisterValue::eTypeFloat); + return true; + } + break; + + case sizeof (double): + if (sizeof (double) == sizeof(uint32_t)) + { + value.SetUInt32(reg_value, RegisterValue::eTypeDouble); + return true; + } + else if (sizeof (double) == sizeof(uint64_t)) + { + value.SetUInt64(reg_value, RegisterValue::eTypeDouble); + return true; + } + break; + + // TOOD: need a better way to detect when "long double" types are + // the same bytes size as "double" +#if !defined(__arm__) + case sizeof (long double): + if (sizeof (long double) == sizeof(uint32_t)) + { + value.SetUInt32(reg_value, RegisterValue::eTypeLongDouble); + return true; + } + else if (sizeof (long double) == sizeof(uint64_t)) + { + value.SetUInt64(reg_value, RegisterValue::eTypeLongDouble); + return true; + } + break; +#endif + } + break; + } + return false; +} + +bool +RegisterContextMacOSXFrameBackchain::WriteRegister (const RegisterInfo *reg_info, + const RegisterValue &value) +{ + // Not supported yet. We could easily add support for this by remembering + // the address of each entry (it would need to be part of the cursor) + return false; +} + +bool +RegisterContextMacOSXFrameBackchain::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + // libunwind frames can't handle this it doesn't always have all register + // values. This call should only be called on frame zero anyway so there + // shouldn't be any problem + return false; +} + +bool +RegisterContextMacOSXFrameBackchain::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + // Since this class doesn't respond to "ReadAllRegisterValues()", it must + // not have been the one that saved all the register values. So we just let + // the thread's register context (the register context for frame zero) do + // the writing. + return m_thread.GetRegisterContext()->WriteAllRegisterValues(data_sp); +} + + +uint32_t +RegisterContextMacOSXFrameBackchain::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_thread.GetRegisterContext()->ConvertRegisterKindToRegisterNumber (kind, num); +} + diff --git a/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h new file mode 100644 index 000000000000..449e053e5ef1 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h @@ -0,0 +1,77 @@ +//===-- RegisterContextMacOSXFrameBackchain.h -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_RegisterContextMacOSXFrameBackchain_h_ +#define lldb_RegisterContextMacOSXFrameBackchain_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" + +#include "UnwindMacOSXFrameBackchain.h" + +class RegisterContextMacOSXFrameBackchain : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextMacOSXFrameBackchain (lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + const UnwindMacOSXFrameBackchain::Cursor &cursor); + + virtual + ~RegisterContextMacOSXFrameBackchain (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb_private::RegisterSet * + GetRegisterSet (size_t reg_set); + + virtual bool + ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + +private: + UnwindMacOSXFrameBackchain::Cursor m_cursor; + bool m_cursor_is_valid; + //------------------------------------------------------------------ + // For RegisterContextMacOSXFrameBackchain only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (RegisterContextMacOSXFrameBackchain); +}; + +#endif // lldb_RegisterContextMacOSXFrameBackchain_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp b/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp new file mode 100644 index 000000000000..7ceb536272f4 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMach_arm.cpp @@ -0,0 +1,87 @@ +//===-- RegisterContextMach_arm.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) + +#include "RegisterContextMach_arm.h" + +// C Includes +#include <mach/mach_types.h> +#include <mach/thread_act.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextMach_arm::RegisterContextMach_arm(Thread &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_arm (thread, concrete_frame_idx) +{ +} + +RegisterContextMach_arm::~RegisterContextMach_arm() +{ +} + +int +RegisterContextMach_arm::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + mach_msg_type_number_t count = GPRWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count); +} + +int +RegisterContextMach_arm::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + mach_msg_type_number_t count = FPUWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count); +} + +int +RegisterContextMach_arm::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + mach_msg_type_number_t count = EXCWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count); +} + +int +RegisterContextMach_arm::DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg) +{ + mach_msg_type_number_t count = DBGWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&dbg, &count); +} + +int +RegisterContextMach_arm::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&gpr, GPRWordCount); +} + +int +RegisterContextMach_arm::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&fpu, FPUWordCount); +} + +int +RegisterContextMach_arm::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&exc, EXCWordCount); +} + +int +RegisterContextMach_arm::DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&dbg, DBGWordCount); +} + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextMach_arm.h b/source/Plugins/Process/Utility/RegisterContextMach_arm.h new file mode 100644 index 000000000000..e97a4bfff2b6 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMach_arm.h @@ -0,0 +1,56 @@ +//===-- RegisterContextMach_arm.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextMach_arm_h_ +#define liblldb_RegisterContextMach_arm_h_ + +// C Includes + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextDarwin_arm.h" + +class RegisterContextMach_arm : public RegisterContextDarwin_arm +{ +public: + + RegisterContextMach_arm(lldb_private::Thread &thread, uint32_t concrete_frame_idx); + + virtual + ~RegisterContextMach_arm(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoReadDBG (lldb::tid_t tid, int flavor, DBG &dbg); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); + + int + DoWriteDBG (lldb::tid_t tid, int flavor, const DBG &dbg); +}; + +#endif // liblldb_RegisterContextMach_arm_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp b/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp new file mode 100644 index 000000000000..3d6c9a6baca6 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMach_i386.cpp @@ -0,0 +1,72 @@ +//===-- RegisterContextMach_i386.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) + +// C Includes +#include <mach/thread_act.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextMach_i386.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextMach_i386::RegisterContextMach_i386(Thread &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_i386 (thread, concrete_frame_idx) +{ +} + +RegisterContextMach_i386::~RegisterContextMach_i386() +{ +} + +int +RegisterContextMach_i386::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + mach_msg_type_number_t count = GPRWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count); +} + +int +RegisterContextMach_i386::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + mach_msg_type_number_t count = FPUWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count); +} + +int +RegisterContextMach_i386::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + mach_msg_type_number_t count = EXCWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count); +} + +int +RegisterContextMach_i386::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&gpr, GPRWordCount); +} + +int +RegisterContextMach_i386::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&fpu, FPUWordCount); +} + +int +RegisterContextMach_i386::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&exc, EXCWordCount); +} + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextMach_i386.h b/source/Plugins/Process/Utility/RegisterContextMach_i386.h new file mode 100644 index 000000000000..ad0f69d1c052 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMach_i386.h @@ -0,0 +1,49 @@ +//===-- RegisterContextMach_i386.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextMach_i386_h_ +#define liblldb_RegisterContextMach_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextDarwin_i386.h" + +class RegisterContextMach_i386 : public RegisterContextDarwin_i386 +{ +public: + + RegisterContextMach_i386(lldb_private::Thread &thread, uint32_t concrete_frame_idx); + + virtual + ~RegisterContextMach_i386(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); +}; + +#endif // liblldb_RegisterContextMach_i386_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp b/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp new file mode 100644 index 000000000000..f03685e1313f --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp @@ -0,0 +1,72 @@ +//===-- RegisterContextMach_x86_64.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) + +// C Includes +#include <mach/thread_act.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextMach_x86_64.h" + +using namespace lldb; +using namespace lldb_private; + + +RegisterContextMach_x86_64::RegisterContextMach_x86_64(Thread &thread, uint32_t concrete_frame_idx) : + RegisterContextDarwin_x86_64 (thread, concrete_frame_idx) +{ +} + +RegisterContextMach_x86_64::~RegisterContextMach_x86_64() +{ +} + +int +RegisterContextMach_x86_64::DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr) +{ + mach_msg_type_number_t count = GPRWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&gpr, &count); +} + +int +RegisterContextMach_x86_64::DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu) +{ + mach_msg_type_number_t count = FPUWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&fpu, &count); +} + +int +RegisterContextMach_x86_64::DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc) +{ + mach_msg_type_number_t count = EXCWordCount; + return ::thread_get_state(tid, flavor, (thread_state_t)&exc, &count); +} + +int +RegisterContextMach_x86_64::DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&gpr, GPRWordCount); +} + +int +RegisterContextMach_x86_64::DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&fpu, FPUWordCount); +} + +int +RegisterContextMach_x86_64::DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc) +{ + return ::thread_set_state(tid, flavor, (thread_state_t)&exc, EXCWordCount); +} + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h b/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h new file mode 100644 index 000000000000..9e6dfa395500 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMach_x86_64.h @@ -0,0 +1,49 @@ +//===-- RegisterContextMach_x86_64.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextMach_x86_64_h_ +#define liblldb_RegisterContextMach_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "RegisterContextDarwin_x86_64.h" + +class RegisterContextMach_x86_64 : public RegisterContextDarwin_x86_64 +{ +public: + + RegisterContextMach_x86_64(lldb_private::Thread &thread, uint32_t concrete_frame_idx); + + virtual + ~RegisterContextMach_x86_64(); + +protected: + + virtual int + DoReadGPR (lldb::tid_t tid, int flavor, GPR &gpr); + + int + DoReadFPU (lldb::tid_t tid, int flavor, FPU &fpu); + + int + DoReadEXC (lldb::tid_t tid, int flavor, EXC &exc); + + int + DoWriteGPR (lldb::tid_t tid, int flavor, const GPR &gpr); + + int + DoWriteFPU (lldb::tid_t tid, int flavor, const FPU &fpu); + + int + DoWriteEXC (lldb::tid_t tid, int flavor, const EXC &exc); +}; + +#endif // liblldb_RegisterContextMach_x86_64_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextMemory.cpp b/source/Plugins/Process/Utility/RegisterContextMemory.cpp new file mode 100644 index 000000000000..8c33a6814acc --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMemory.cpp @@ -0,0 +1,174 @@ +//===-- RegisterContextMemory.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextMemory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "DynamicRegisterInfo.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// RegisterContextMemory constructor +//---------------------------------------------------------------------- +RegisterContextMemory::RegisterContextMemory +( + Thread &thread, + uint32_t concrete_frame_idx, + DynamicRegisterInfo ®_infos, + addr_t reg_data_addr +) : + RegisterContext (thread, concrete_frame_idx), + m_reg_infos (reg_infos), + m_reg_valid (), + m_reg_data (), + m_reg_data_addr (reg_data_addr) +{ + // Resize our vector of bools to contain one bool for every register. + // We will use these boolean values to know when a register value + // is valid in m_reg_data. + const size_t num_regs = reg_infos.GetNumRegisters(); + assert (num_regs > 0); + m_reg_valid.resize (num_regs); + + // Make a heap based buffer that is big enough to store all registers + DataBufferSP reg_data_sp(new DataBufferHeap (reg_infos.GetRegisterDataByteSize(), 0)); + m_reg_data.SetData (reg_data_sp); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +RegisterContextMemory::~RegisterContextMemory() +{ +} + +void +RegisterContextMemory::InvalidateAllRegisters () +{ + if (m_reg_data_addr != LLDB_INVALID_ADDRESS) + SetAllRegisterValid (false); +} + +void +RegisterContextMemory::SetAllRegisterValid (bool b) +{ + std::vector<bool>::iterator pos, end = m_reg_valid.end(); + for (pos = m_reg_valid.begin(); pos != end; ++pos) + *pos = b; +} + +size_t +RegisterContextMemory::GetRegisterCount () +{ + return m_reg_infos.GetNumRegisters (); +} + +const RegisterInfo * +RegisterContextMemory::GetRegisterInfoAtIndex (size_t reg) +{ + return m_reg_infos.GetRegisterInfoAtIndex (reg); +} + +size_t +RegisterContextMemory::GetRegisterSetCount () +{ + return m_reg_infos.GetNumRegisterSets (); +} + +const RegisterSet * +RegisterContextMemory::GetRegisterSet (size_t reg_set) +{ + return m_reg_infos.GetRegisterSet (reg_set); +} + +uint32_t +RegisterContextMemory::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_reg_infos.ConvertRegisterKindToRegisterNumber (kind, num); +} + +bool +RegisterContextMemory::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB]; + if (!m_reg_valid[reg_num]) + { + if (!ReadAllRegisterValues(m_reg_data.GetSharedDataBuffer ())) + return false; + } + const bool partial_data_ok = false; + return reg_value.SetValueFromData(reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok).Success(); +} + +bool +RegisterContextMemory::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (m_reg_data_addr != LLDB_INVALID_ADDRESS) + { + const uint32_t reg_num = reg_info->kinds[eRegisterKindLLDB]; + addr_t reg_addr = m_reg_data_addr + reg_info->byte_offset; + Error error (WriteRegisterValueToMemory(reg_info, reg_addr, reg_info->byte_size, reg_value)); + m_reg_valid[reg_num] = false; + return error.Success(); + } + return false; +} + +bool +RegisterContextMemory::ReadAllRegisterValues (DataBufferSP &data_sp) +{ + if (m_reg_data_addr != LLDB_INVALID_ADDRESS) + { + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + if (process_sp->ReadMemory(m_reg_data_addr, data_sp->GetBytes(), data_sp->GetByteSize(), error) == data_sp->GetByteSize()) + { + SetAllRegisterValid (true); + return true; + } + } + } + return false; +} + +bool +RegisterContextMemory::WriteAllRegisterValues (const DataBufferSP &data_sp) +{ + if (m_reg_data_addr != LLDB_INVALID_ADDRESS) + { + ProcessSP process_sp (CalculateProcess()); + if (process_sp) + { + Error error; + SetAllRegisterValid (false); + if (process_sp->WriteMemory(m_reg_data_addr, data_sp->GetBytes(), data_sp->GetByteSize(), error) == data_sp->GetByteSize()) + return true; + } + } + return false; +} + +void +RegisterContextMemory::SetAllRegisterData (const lldb::DataBufferSP &data_sp) +{ + m_reg_data.SetData(data_sp); + SetAllRegisterValid (true); +} diff --git a/source/Plugins/Process/Utility/RegisterContextMemory.h b/source/Plugins/Process/Utility/RegisterContextMemory.h new file mode 100644 index 000000000000..8bba52c627f3 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextMemory.h @@ -0,0 +1,102 @@ +//===-- RegisterContextMemory.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_RegisterContextMemory_h_ +#define lldb_RegisterContextMemory_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/RegisterContext.h" + +class DynamicRegisterInfo; + +class RegisterContextMemory : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContextMemory (lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + DynamicRegisterInfo ®_info, + lldb::addr_t reg_data_addr); + + virtual + ~RegisterContextMemory (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb_private::RegisterSet * + GetRegisterSet (size_t reg_set); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + + //------------------------------------------------------------------ + // If all of the thread register are in a contiguous buffer in + // memory, then the default ReadRegister/WriteRegister and + // ReadAllRegisterValues/WriteAllRegisterValues will work. If thread + // registers are not contiguous, clients will want to subclass this + // class and modify the read/write functions as needed. + //------------------------------------------------------------------ + + virtual bool + ReadRegister (const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value); + + virtual bool + WriteRegister (const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + void + SetAllRegisterData (const lldb::DataBufferSP &data_sp); +protected: + + void + SetAllRegisterValid (bool b); + + DynamicRegisterInfo &m_reg_infos; + std::vector<bool> m_reg_valid; + lldb_private::DataExtractor m_reg_data; + lldb::addr_t m_reg_data_addr; // If this is valid, then we have a register context that is stored in memmory + +private: + //------------------------------------------------------------------ + // For RegisterContextMemory only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (RegisterContextMemory); +}; + +#endif // lldb_RegisterContextMemory_h_ diff --git a/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp b/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp new file mode 100644 index 000000000000..d35a5d095705 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp @@ -0,0 +1,261 @@ +//===-- RegisterContextThreadMemory.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/OperatingSystem.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +#include "RegisterContextThreadMemory.h" + +using namespace lldb; +using namespace lldb_private; + +RegisterContextThreadMemory::RegisterContextThreadMemory (Thread &thread, + lldb::addr_t register_data_addr) : + RegisterContext (thread, 0), + m_thread_wp (thread.shared_from_this()), + m_reg_ctx_sp (), + m_register_data_addr (register_data_addr), + m_stop_id(0) +{ +} + +RegisterContextThreadMemory::~RegisterContextThreadMemory() +{ +} + +void +RegisterContextThreadMemory::UpdateRegisterContext () +{ + ThreadSP thread_sp (m_thread_wp.lock()); + if (thread_sp) + { + ProcessSP process_sp (thread_sp->GetProcess()); + + if (process_sp) + { + const uint32_t stop_id = process_sp->GetModID().GetStopID(); + if (m_stop_id != stop_id) + { + m_stop_id = stop_id; + m_reg_ctx_sp.reset(); + } + if (!m_reg_ctx_sp) + { + ThreadSP backing_thread_sp (thread_sp->GetBackingThread()); + if (backing_thread_sp) + { + m_reg_ctx_sp = backing_thread_sp->GetRegisterContext(); + } + else + { + OperatingSystem *os = process_sp->GetOperatingSystem (); + if (os->IsOperatingSystemPluginThread (thread_sp)) + m_reg_ctx_sp = os->CreateRegisterContextForThread (thread_sp.get(), LLDB_INVALID_ADDRESS); + } + } + } + else + { + m_reg_ctx_sp.reset(); + } + } + else + { + m_reg_ctx_sp.reset(); + } +} + +//------------------------------------------------------------------ +// Subclasses must override these functions +//------------------------------------------------------------------ +void +RegisterContextThreadMemory::InvalidateAllRegisters () +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + m_reg_ctx_sp->InvalidateAllRegisters(); +} + +size_t +RegisterContextThreadMemory::GetRegisterCount () +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->GetRegisterCount(); + return 0; +} + +const RegisterInfo * +RegisterContextThreadMemory::GetRegisterInfoAtIndex (size_t reg) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->GetRegisterInfoAtIndex(reg); + return NULL; +} + +size_t +RegisterContextThreadMemory::GetRegisterSetCount () +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->GetRegisterSetCount(); + return 0; +} + +const RegisterSet * +RegisterContextThreadMemory::GetRegisterSet (size_t reg_set) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->GetRegisterSet(reg_set); + return NULL; +} + +bool +RegisterContextThreadMemory::ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ReadRegister(reg_info, reg_value); + return false; +} + +bool +RegisterContextThreadMemory::WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->WriteRegister (reg_info, reg_value); + return false; +} + +bool +RegisterContextThreadMemory::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ReadAllRegisterValues(data_sp); + return false; +} + +bool +RegisterContextThreadMemory::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->WriteAllRegisterValues (data_sp); + return false; +} + +bool +RegisterContextThreadMemory::CopyFromRegisterContext (lldb::RegisterContextSP reg_ctx_sp) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->CopyFromRegisterContext(reg_ctx_sp); + return false; +} + +uint32_t +RegisterContextThreadMemory::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ConvertRegisterKindToRegisterNumber(kind, num); + return false; +} + +uint32_t +RegisterContextThreadMemory::NumSupportedHardwareBreakpoints () +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->NumSupportedHardwareBreakpoints(); + return false; +} + +uint32_t +RegisterContextThreadMemory::SetHardwareBreakpoint (lldb::addr_t addr, size_t size) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->SetHardwareBreakpoint(addr, size); + return 0; +} + +bool +RegisterContextThreadMemory::ClearHardwareBreakpoint (uint32_t hw_idx) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ClearHardwareBreakpoint (hw_idx); + return false; +} + +uint32_t +RegisterContextThreadMemory::NumSupportedHardwareWatchpoints () +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->NumSupportedHardwareWatchpoints(); + return 0; +} + +uint32_t +RegisterContextThreadMemory::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->SetHardwareWatchpoint(addr, size, read, write); + return 0; +} + +bool +RegisterContextThreadMemory::ClearHardwareWatchpoint (uint32_t hw_index) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ClearHardwareWatchpoint(hw_index); + return false; +} + +bool +RegisterContextThreadMemory::HardwareSingleStep (bool enable) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->HardwareSingleStep(enable); + return false; +} + +Error +RegisterContextThreadMemory::ReadRegisterValueFromMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr, uint32_t src_len, RegisterValue ®_value) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->ReadRegisterValueFromMemory (reg_info, src_addr, src_len, reg_value); + Error error; + error.SetErrorString("invalid register context"); + return error; +} + +Error +RegisterContextThreadMemory::WriteRegisterValueToMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len, const RegisterValue ®_value) +{ + UpdateRegisterContext (); + if (m_reg_ctx_sp) + return m_reg_ctx_sp->WriteRegisterValueToMemory (reg_info, dst_addr, dst_len, reg_value); + Error error; + error.SetErrorString("invalid register context"); + return error; +} diff --git a/source/Plugins/Process/Utility/RegisterContextThreadMemory.h b/source/Plugins/Process/Utility/RegisterContextThreadMemory.h new file mode 100644 index 000000000000..8d7a4b622fe8 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextThreadMemory.h @@ -0,0 +1,114 @@ +//===-- RegisterContextThreadMemory.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_RegisterContextThreadMemory_h_ +#define lldb_RegisterContextThreadMemory_h_ + +#include <vector> + +#include "lldb/lldb-private.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Symbol/SymbolContext.h" + +namespace lldb_private { + +class RegisterContextThreadMemory : public lldb_private::RegisterContext +{ +public: + RegisterContextThreadMemory (Thread &thread, + lldb::addr_t register_data_addr); + + virtual ~RegisterContextThreadMemory(); + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const RegisterSet * + GetRegisterSet (size_t reg_set); + + virtual bool + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value); + + virtual bool + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value); + + // These two functions are used to implement "push" and "pop" of register states. They are used primarily + // for expression evaluation, where we need to push a new state (storing the old one in data_sp) and then + // restoring the original state by passing the data_sp we got from ReadAllRegisters to WriteAllRegisterValues. + // ReadAllRegisters will do what is necessary to return a coherent set of register values for this thread, which + // may mean e.g. interrupting a thread that is sitting in a kernel trap. That is a somewhat disruptive operation, + // so these API's should only be used when this behavior is needed. + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + bool + CopyFromRegisterContext (lldb::RegisterContextSP context); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + + //------------------------------------------------------------------ + // Subclasses can override these functions if desired + //------------------------------------------------------------------ + virtual uint32_t + NumSupportedHardwareBreakpoints (); + + virtual uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size); + + virtual bool + ClearHardwareBreakpoint (uint32_t hw_idx); + + virtual uint32_t + NumSupportedHardwareWatchpoints (); + + virtual uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write); + + virtual bool + ClearHardwareWatchpoint (uint32_t hw_index); + + virtual bool + HardwareSingleStep (bool enable); + + virtual Error + ReadRegisterValueFromMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr, uint32_t src_len, RegisterValue ®_value); + + virtual Error + WriteRegisterValueToMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len, const RegisterValue ®_value); + +protected: + void + UpdateRegisterContext (); + + lldb::ThreadWP m_thread_wp; + lldb::RegisterContextSP m_reg_ctx_sp; + lldb::addr_t m_register_data_addr; + uint32_t m_stop_id; +private: + DISALLOW_COPY_AND_ASSIGN (RegisterContextThreadMemory); +}; +} // namespace lldb_private + +#endif // lldb_RegisterContextThreadMemory_h_ diff --git a/source/Plugins/Process/Utility/StopInfoMachException.cpp b/source/Plugins/Process/Utility/StopInfoMachException.cpp new file mode 100644 index 000000000000..51d2052e1931 --- /dev/null +++ b/source/Plugins/Process/Utility/StopInfoMachException.cpp @@ -0,0 +1,482 @@ +//===-- StopInfoMachException.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StopInfoMachException.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/UnixSignals.h" + +using namespace lldb; +using namespace lldb_private; + +const char * +StopInfoMachException::GetDescription () +{ + if (m_description.empty() && m_value != 0) + { + ExecutionContext exe_ctx (m_thread_wp.lock()); + Target *target = exe_ctx.GetTargetPtr(); + const llvm::Triple::ArchType cpu = target ? target->GetArchitecture().GetMachine() : llvm::Triple::UnknownArch; + + const char *exc_desc = NULL; + const char *code_label = "code"; + const char *code_desc = NULL; + const char *subcode_label = "subcode"; + const char *subcode_desc = NULL; + switch (m_value) + { + case 1: // EXC_BAD_ACCESS + exc_desc = "EXC_BAD_ACCESS"; + subcode_label = "address"; + switch (cpu) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + switch (m_exc_code) + { + case 0xd: code_desc = "EXC_I386_GPFLT"; m_exc_data_count = 1; break; + } + break; + case llvm::Triple::arm: + switch (m_exc_code) + { + case 0x101: code_desc = "EXC_ARM_DA_ALIGN"; break; + case 0x102: code_desc = "EXC_ARM_DA_DEBUG"; break; + } + break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + switch (m_exc_code) + { + case 0x101: code_desc = "EXC_PPC_VM_PROT_READ"; break; + case 0x102: code_desc = "EXC_PPC_BADSPACE"; break; + case 0x103: code_desc = "EXC_PPC_UNALIGNED"; break; + } + break; + + default: + break; + } + break; + + case 2: // EXC_BAD_INSTRUCTION + exc_desc = "EXC_BAD_INSTRUCTION"; + switch (cpu) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + if (m_exc_code == 1) + code_desc = "EXC_I386_INVOP"; + break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + switch (m_exc_code) + { + case 1: code_desc = "EXC_PPC_INVALID_SYSCALL"; break; + case 2: code_desc = "EXC_PPC_UNIPL_INST"; break; + case 3: code_desc = "EXC_PPC_PRIVINST"; break; + case 4: code_desc = "EXC_PPC_PRIVREG"; break; + case 5: code_desc = "EXC_PPC_TRACE"; break; + case 6: code_desc = "EXC_PPC_PERFMON"; break; + } + break; + + case llvm::Triple::arm: + if (m_exc_code == 1) + code_desc = "EXC_ARM_UNDEFINED"; + break; + + default: + break; + } + break; + + case 3: // EXC_ARITHMETIC + exc_desc = "EXC_ARITHMETIC"; + switch (cpu) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + switch (m_exc_code) + { + case 1: code_desc = "EXC_I386_DIV"; break; + case 2: code_desc = "EXC_I386_INTO"; break; + case 3: code_desc = "EXC_I386_NOEXT"; break; + case 4: code_desc = "EXC_I386_EXTOVR"; break; + case 5: code_desc = "EXC_I386_EXTERR"; break; + case 6: code_desc = "EXC_I386_EMERR"; break; + case 7: code_desc = "EXC_I386_BOUND"; break; + case 8: code_desc = "EXC_I386_SSEEXTERR"; break; + } + break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + switch (m_exc_code) + { + case 1: code_desc = "EXC_PPC_OVERFLOW"; break; + case 2: code_desc = "EXC_PPC_ZERO_DIVIDE"; break; + case 3: code_desc = "EXC_PPC_FLT_INEXACT"; break; + case 4: code_desc = "EXC_PPC_FLT_ZERO_DIVIDE"; break; + case 5: code_desc = "EXC_PPC_FLT_UNDERFLOW"; break; + case 6: code_desc = "EXC_PPC_FLT_OVERFLOW"; break; + case 7: code_desc = "EXC_PPC_FLT_NOT_A_NUMBER"; break; + } + break; + + default: + break; + } + break; + + case 4: // EXC_EMULATION + exc_desc = "EXC_EMULATION"; + break; + + + case 5: // EXC_SOFTWARE + exc_desc = "EXC_SOFTWARE"; + if (m_exc_code == 0x10003) + { + subcode_desc = "EXC_SOFT_SIGNAL"; + subcode_label = "signo"; + } + break; + + case 6: // EXC_BREAKPOINT + { + exc_desc = "EXC_BREAKPOINT"; + switch (cpu) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + switch (m_exc_code) + { + case 1: code_desc = "EXC_I386_SGL"; break; + case 2: code_desc = "EXC_I386_BPT"; break; + } + break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + switch (m_exc_code) + { + case 1: code_desc = "EXC_PPC_BREAKPOINT"; break; + } + break; + + case llvm::Triple::arm: + switch (m_exc_code) + { + case 0x101: code_desc = "EXC_ARM_DA_ALIGN"; break; + case 0x102: code_desc = "EXC_ARM_DA_DEBUG"; break; + case 1: code_desc = "EXC_ARM_BREAKPOINT"; break; + // FIXME temporary workaround, exc_code 0 does not really mean EXC_ARM_BREAKPOINT + case 0: code_desc = "EXC_ARM_BREAKPOINT"; break; + } + break; + + default: + break; + } + } + break; + + case 7: + exc_desc = "EXC_SYSCALL"; + break; + + case 8: + exc_desc = "EXC_MACH_SYSCALL"; + break; + + case 9: + exc_desc = "EXC_RPC_ALERT"; + break; + + case 10: + exc_desc = "EXC_CRASH"; + break; + case 11: + exc_desc = "EXC_RESOURCE"; + break; + case 12: + exc_desc = "EXC_GUARD"; + break; + } + + StreamString strm; + + if (exc_desc) + strm.PutCString(exc_desc); + else + strm.Printf("EXC_??? (%" PRIu64 ")", m_value); + + if (m_exc_data_count >= 1) + { + if (code_desc) + strm.Printf(" (%s=%s", code_label, code_desc); + else + strm.Printf(" (%s=%" PRIu64, code_label, m_exc_code); + } + + if (m_exc_data_count >= 2) + { + if (subcode_desc) + strm.Printf(", %s=%s", subcode_label, subcode_desc); + else + strm.Printf(", %s=0x%" PRIx64, subcode_label, m_exc_subcode); + } + + if (m_exc_data_count > 0) + strm.PutChar(')'); + + m_description.swap (strm.GetString()); + } + return m_description.c_str(); +} + + + + + +StopInfoSP +StopInfoMachException::CreateStopReasonWithMachException +( + Thread &thread, + uint32_t exc_type, + uint32_t exc_data_count, + uint64_t exc_code, + uint64_t exc_sub_code, + uint64_t exc_sub_sub_code, + bool pc_already_adjusted, + bool adjust_pc_if_needed +) +{ + if (exc_type != 0) + { + uint32_t pc_decrement = 0; + ExecutionContext exe_ctx (thread.shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); + const llvm::Triple::ArchType cpu = target ? target->GetArchitecture().GetMachine() : llvm::Triple::UnknownArch; + + switch (exc_type) + { + case 1: // EXC_BAD_ACCESS + break; + + case 2: // EXC_BAD_INSTRUCTION + switch (cpu) + { + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + switch (exc_code) + { + case 1: // EXC_PPC_INVALID_SYSCALL + case 2: // EXC_PPC_UNIPL_INST + case 3: // EXC_PPC_PRIVINST + case 4: // EXC_PPC_PRIVREG + break; + case 5: // EXC_PPC_TRACE + return StopInfo::CreateStopReasonToTrace (thread); + case 6: // EXC_PPC_PERFMON + break; + } + break; + + default: + break; + } + break; + + case 3: // EXC_ARITHMETIC + case 4: // EXC_EMULATION + break; + + case 5: // EXC_SOFTWARE + if (exc_code == 0x10003) // EXC_SOFT_SIGNAL + { + if (exc_sub_code == 5) + { + // On MacOSX, a SIGTRAP can signify that a process has called + // exec, so we should check with our dynamic loader to verify. + ProcessSP process_sp (thread.GetProcess()); + if (process_sp) + { + DynamicLoader *dynamic_loader = process_sp->GetDynamicLoader(); + if (dynamic_loader && dynamic_loader->ProcessDidExec()) + { + // The program was re-exec'ed + return StopInfo::CreateStopReasonWithExec (thread); + } +// if (!process_did_exec) +// { +// // We have a SIGTRAP, make sure we didn't exec by checking +// // for the PC being at "_dyld_start"... +// lldb::StackFrameSP frame_sp (thread.GetStackFrameAtIndex(0)); +// if (frame_sp) +// { +// const Symbol *symbol = frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol; +// if (symbol) +// { +// if (symbol->GetName() == ConstString("_dyld_start")) +// process_did_exec = true; +// } +// } +// } + } + } + return StopInfo::CreateStopReasonWithSignal (thread, exc_sub_code); + } + break; + + case 6: // EXC_BREAKPOINT + { + bool is_actual_breakpoint = false; + bool is_trace_if_actual_breakpoint_missing = false; + switch (cpu) + { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + if (exc_code == 1) // EXC_I386_SGL + { + if (!exc_sub_code) + return StopInfo::CreateStopReasonToTrace(thread); + + // It's a watchpoint, then. + // The exc_sub_code indicates the data break address. + lldb::WatchpointSP wp_sp; + if (target) + wp_sp = target->GetWatchpointList().FindByAddress((lldb::addr_t)exc_sub_code); + if (wp_sp && wp_sp->IsEnabled()) + { + // Debugserver may piggyback the hardware index of the fired watchpoint in the exception data. + // Set the hardware index if that's the case. + if (exc_data_count >=3) + wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code); + return StopInfo::CreateStopReasonWithWatchpointID(thread, wp_sp->GetID()); + } + } + else if (exc_code == 2 || // EXC_I386_BPT + exc_code == 3) // EXC_I386_BPTFLT + { + // KDP returns EXC_I386_BPTFLT for trace breakpoints + if (exc_code == 3) + is_trace_if_actual_breakpoint_missing = true; + + is_actual_breakpoint = true; + if (!pc_already_adjusted) + pc_decrement = 1; + } + break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + is_actual_breakpoint = exc_code == 1; // EXC_PPC_BREAKPOINT + break; + + case llvm::Triple::arm: + if (exc_code == 0x102) // EXC_ARM_DA_DEBUG + { + // It's a watchpoint, then, if the exc_sub_code indicates a known/enabled + // data break address from our watchpoint list. + lldb::WatchpointSP wp_sp; + if (target) + wp_sp = target->GetWatchpointList().FindByAddress((lldb::addr_t)exc_sub_code); + if (wp_sp && wp_sp->IsEnabled()) + { + // Debugserver may piggyback the hardware index of the fired watchpoint in the exception data. + // Set the hardware index if that's the case. + if (exc_data_count >=3) + wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code); + return StopInfo::CreateStopReasonWithWatchpointID(thread, wp_sp->GetID()); + } + // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as EXC_BAD_ACCESS + if (thread.GetTemporaryResumeState() == eStateStepping) + return StopInfo::CreateStopReasonToTrace(thread); + } + else if (exc_code == 1) // EXC_ARM_BREAKPOINT + { + is_actual_breakpoint = true; + is_trace_if_actual_breakpoint_missing = true; + } + else if (exc_code == 0) // FIXME not EXC_ARM_BREAKPOINT but a kernel is currently returning this so accept it as indicating a breakpoint until the kernel is fixed + { + is_actual_breakpoint = true; + is_trace_if_actual_breakpoint_missing = true; + } + break; + + default: + break; + } + + if (is_actual_breakpoint) + { + RegisterContextSP reg_ctx_sp (thread.GetRegisterContext()); + addr_t pc = reg_ctx_sp->GetPC() - pc_decrement; + + ProcessSP process_sp (thread.CalculateProcess()); + + lldb::BreakpointSiteSP bp_site_sp; + if (process_sp) + bp_site_sp = process_sp->GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp && bp_site_sp->IsEnabled()) + { + // Update the PC if we were asked to do so, but only do + // so if we find a breakpoint that we know about cause + // this could be a trap instruction in the code + if (pc_decrement > 0 && adjust_pc_if_needed) + reg_ctx_sp->SetPC (pc); + + // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, + // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that + // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. + if (bp_site_sp->ValidForThisThread (&thread)) + return StopInfo::CreateStopReasonWithBreakpointSiteID (thread, bp_site_sp->GetID()); + else + return StopInfoSP(); + } + + // Don't call this a trace if we weren't single stepping this thread. + if (is_trace_if_actual_breakpoint_missing && thread.GetTemporaryResumeState() == eStateStepping) + { + return StopInfo::CreateStopReasonToTrace (thread); + } + } + } + break; + + case 7: // EXC_SYSCALL + case 8: // EXC_MACH_SYSCALL + case 9: // EXC_RPC_ALERT + case 10: // EXC_CRASH + break; + } + + return StopInfoSP(new StopInfoMachException (thread, exc_type, exc_data_count, exc_code, exc_sub_code)); + } + return StopInfoSP(); +} diff --git a/source/Plugins/Process/Utility/StopInfoMachException.h b/source/Plugins/Process/Utility/StopInfoMachException.h new file mode 100644 index 000000000000..130ee0b709b0 --- /dev/null +++ b/source/Plugins/Process/Utility/StopInfoMachException.h @@ -0,0 +1,77 @@ +//===-- StopInfoMachException.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StopInfoMachException_h_ +#define liblldb_StopInfoMachException_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/Target/StopInfo.h" + +namespace lldb_private { + +class StopInfoMachException : public StopInfo +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StopInfoMachException (Thread &thread, + uint32_t exc_type, + uint32_t exc_data_count, + uint64_t exc_code, + uint64_t exc_subcode) : + StopInfo (thread, exc_type), + m_exc_data_count (exc_data_count), + m_exc_code (exc_code), + m_exc_subcode (exc_subcode) + { + } + + virtual ~StopInfoMachException() + { + } + + + virtual lldb::StopReason + GetStopReason () const + { + return lldb::eStopReasonException; + } + + virtual const char * + GetDescription (); + + // Since some mach exceptions will be reported as breakpoints, signals, + // or trace, we use this static accessor which will translate the mach + // exception into the correct StopInfo. + static lldb::StopInfoSP + CreateStopReasonWithMachException (Thread &thread, + uint32_t exc_type, + uint32_t exc_data_count, + uint64_t exc_code, + uint64_t exc_sub_code, + uint64_t exc_sub_sub_code, + bool pc_already_adjusted = true, + bool adjust_pc_if_needed = false); + +protected: + uint32_t m_exc_data_count; + uint64_t m_exc_code; + uint64_t m_exc_subcode; +}; + + +} // namespace lldb_private + +#endif // liblldb_StopInfoMachException_h_ diff --git a/source/Plugins/Process/Utility/ThreadMemory.cpp b/source/Plugins/Process/Utility/ThreadMemory.cpp new file mode 100644 index 000000000000..56e5a9a59fab --- /dev/null +++ b/source/Plugins/Process/Utility/ThreadMemory.cpp @@ -0,0 +1,140 @@ +//===-- ThreadMemory.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Process/Utility/ThreadMemory.h" +#include "lldb/Target/OperatingSystem.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Unwind.h" +#include "Plugins/Process/Utility/RegisterContextThreadMemory.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadMemory::ThreadMemory (Process &process, + tid_t tid, + const ValueObjectSP &thread_info_valobj_sp) : + Thread (process, tid), + m_backing_thread_sp (), + m_thread_info_valobj_sp (thread_info_valobj_sp), + m_name(), + m_queue() +{ +} + + +ThreadMemory::ThreadMemory (Process &process, + lldb::tid_t tid, + const char *name, + const char *queue, + lldb::addr_t register_data_addr) : + Thread (process, tid), + m_backing_thread_sp (), + m_thread_info_valobj_sp (), + m_name(), + m_queue(), + m_register_data_addr (register_data_addr) +{ + if (name) + m_name = name; + if (queue) + m_queue = queue; +} + + +ThreadMemory::~ThreadMemory() +{ + DestroyThread(); +} + +void +ThreadMemory::WillResume (StateType resume_state) +{ + if (m_backing_thread_sp) + m_backing_thread_sp->WillResume(resume_state); +} + +void +ThreadMemory::ClearStackFrames () +{ + if (m_backing_thread_sp) + m_backing_thread_sp->ClearStackFrames(); + Thread::ClearStackFrames(); +} + +RegisterContextSP +ThreadMemory::GetRegisterContext () +{ + if (!m_reg_context_sp) + m_reg_context_sp.reset (new RegisterContextThreadMemory (*this, m_register_data_addr)); + return m_reg_context_sp; +} + +RegisterContextSP +ThreadMemory::CreateRegisterContextForFrame (StackFrame *frame) +{ + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex (); + + if (concrete_frame_idx == 0) + { + reg_ctx_sp = GetRegisterContext (); + } + else + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame); + } + return reg_ctx_sp; +} + +bool +ThreadMemory::CalculateStopInfo () +{ + if (m_backing_thread_sp) + { + lldb::StopInfoSP backing_stop_info_sp (m_backing_thread_sp->GetPrivateStopInfo()); + if (backing_stop_info_sp) + { + backing_stop_info_sp->SetThread (shared_from_this()); + SetStopInfo (backing_stop_info_sp); + return true; + } + } + else + { + ProcessSP process_sp (GetProcess()); + + if (process_sp) + { + OperatingSystem *os = process_sp->GetOperatingSystem (); + if (os) + { + SetStopInfo (os->CreateThreadStopReason (this)); + return true; + } + } + } + return false; +} + +void +ThreadMemory::RefreshStateAfterStop() +{ + if (m_backing_thread_sp) + return m_backing_thread_sp->RefreshStateAfterStop(); + + if (m_reg_context_sp) + m_reg_context_sp->InvalidateAllRegisters(); +} diff --git a/source/Plugins/Process/Utility/ThreadMemory.h b/source/Plugins/Process/Utility/ThreadMemory.h new file mode 100644 index 000000000000..07eb45dcb431 --- /dev/null +++ b/source/Plugins/Process/Utility/ThreadMemory.h @@ -0,0 +1,152 @@ +//===-- ThreadMemory.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadMemory_h_ +#define liblldb_ThreadMemory_h_ + +#include "lldb/Target/Thread.h" + +class ThreadMemory : + public lldb_private::Thread +{ +public: + + ThreadMemory (lldb_private::Process &process, + lldb::tid_t tid, + const lldb::ValueObjectSP &thread_info_valobj_sp); + + ThreadMemory (lldb_private::Process &process, + lldb::tid_t tid, + const char *name, + const char *queue, + lldb::addr_t register_data_addr); + + virtual + ~ThreadMemory(); + + //------------------------------------------------------------------ + // lldb_private::Thread methods + //------------------------------------------------------------------ + virtual lldb::RegisterContextSP + GetRegisterContext (); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + virtual bool + CalculateStopInfo (); + + virtual const char * + GetInfo () + { + if (m_backing_thread_sp) + m_backing_thread_sp->GetInfo(); + return NULL; + } + + virtual const char * + GetName () + { + if (!m_name.empty()) + return m_name.c_str(); + if (m_backing_thread_sp) + m_backing_thread_sp->GetName(); + return NULL; + } + + virtual const char * + GetQueueName () + { + if (!m_queue.empty()) + return m_queue.c_str(); + if (m_backing_thread_sp) + m_backing_thread_sp->GetQueueName(); + return NULL; + } + + virtual void + WillResume (lldb::StateType resume_state); + + virtual void + DidResume () + { + if (m_backing_thread_sp) + m_backing_thread_sp->DidResume(); + } + + virtual lldb::user_id_t + GetProtocolID () const + { + if (m_backing_thread_sp) + return m_backing_thread_sp->GetProtocolID(); + return Thread::GetProtocolID(); + } + + virtual void + RefreshStateAfterStop(); + + lldb::ValueObjectSP & + GetValueObject () + { + return m_thread_info_valobj_sp; + } + + virtual void + ClearStackFrames (); + + virtual void + ClearBackingThread () + { + m_backing_thread_sp.reset(); + } + + virtual bool + SetBackingThread (const lldb::ThreadSP &thread_sp) + { + //printf ("Thread 0x%llx is being backed by thread 0x%llx\n", GetID(), thread_sp->GetID()); + m_backing_thread_sp = thread_sp; + return (bool)thread_sp; + } + + virtual lldb::ThreadSP + GetBackingThread () const + { + return m_backing_thread_sp; + } + +protected: + + virtual bool + IsOperatingSystemPluginThread () const + { + return true; + } + + + //------------------------------------------------------------------ + // For ThreadMemory and subclasses + //------------------------------------------------------------------ + // If this memory thread is actually represented by a thread from the + // lldb_private::Process subclass, then fill in the thread here and + // all APIs will be routed through this thread object. If m_backing_thread_sp + // is empty, then this thread is simply in memory with no representation + // through the process plug-in. + lldb::ThreadSP m_backing_thread_sp; + lldb::ValueObjectSP m_thread_info_valobj_sp; + std::string m_name; + std::string m_queue; + lldb::addr_t m_register_data_addr; +private: + //------------------------------------------------------------------ + // For ThreadMemory only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ThreadMemory); +}; + +#endif // liblldb_ThreadMemory_h_ diff --git a/source/Plugins/Process/Utility/UnwindLLDB.cpp b/source/Plugins/Process/Utility/UnwindLLDB.cpp new file mode 100644 index 000000000000..0eea00363498 --- /dev/null +++ b/source/Plugins/Process/Utility/UnwindLLDB.cpp @@ -0,0 +1,322 @@ +//===-- UnwindLLDB.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Module.h" +#include "lldb/Core/Log.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" + +#include "UnwindLLDB.h" +#include "RegisterContextLLDB.h" + +using namespace lldb; +using namespace lldb_private; + +UnwindLLDB::UnwindLLDB (Thread &thread) : + Unwind (thread), + m_frames(), + m_unwind_complete(false) +{ +} + +uint32_t +UnwindLLDB::DoGetFrameCount() +{ + if (!m_unwind_complete) + { +//#define DEBUG_FRAME_SPEED 1 +#if DEBUG_FRAME_SPEED +#define FRAME_COUNT 10000 + TimeValue time_value (TimeValue::Now()); +#endif + if (!AddFirstFrame ()) + return 0; + + ProcessSP process_sp (m_thread.GetProcess()); + ABI *abi = process_sp ? process_sp->GetABI().get() : NULL; + + while (AddOneMoreFrame (abi)) + { +#if DEBUG_FRAME_SPEED + if ((m_frames.size() % FRAME_COUNT) == 0) + { + TimeValue now(TimeValue::Now()); + uint64_t delta_t = now - time_value; + printf ("%u frames in %" PRIu64 ".%09llu ms (%g frames/sec)\n", + FRAME_COUNT, + delta_t / TimeValue::NanoSecPerSec, + delta_t % TimeValue::NanoSecPerSec, + (float)FRAME_COUNT / ((float)delta_t / (float)TimeValue::NanoSecPerSec)); + time_value = now; + } +#endif + } + } + return m_frames.size (); +} + +bool +UnwindLLDB::AddFirstFrame () +{ + if (m_frames.size() > 0) + return true; + + // First, set up the 0th (initial) frame + CursorSP first_cursor_sp(new Cursor ()); + RegisterContextLLDBSP reg_ctx_sp (new RegisterContextLLDB (m_thread, + RegisterContextLLDBSP(), + first_cursor_sp->sctx, + 0, *this)); + if (reg_ctx_sp.get() == NULL) + goto unwind_done; + + if (!reg_ctx_sp->IsValid()) + goto unwind_done; + + if (!reg_ctx_sp->GetCFA (first_cursor_sp->cfa)) + goto unwind_done; + + if (!reg_ctx_sp->ReadPC (first_cursor_sp->start_pc)) + goto unwind_done; + + // Everything checks out, so release the auto pointer value and let the + // cursor own it in its shared pointer + first_cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp; + m_frames.push_back (first_cursor_sp); + return true; +unwind_done: + m_unwind_complete = true; + return false; +} + +// For adding a non-zero stack frame to m_frames. +bool +UnwindLLDB::AddOneMoreFrame (ABI *abi) +{ + // If we've already gotten to the end of the stack, don't bother to try again... + if (m_unwind_complete) + return false; + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + CursorSP cursor_sp(new Cursor ()); + + // Frame zero is a little different + if (m_frames.size() == 0) + return false; + + uint32_t cur_idx = m_frames.size (); + RegisterContextLLDBSP reg_ctx_sp(new RegisterContextLLDB (m_thread, + m_frames[cur_idx - 1]->reg_ctx_lldb_sp, + cursor_sp->sctx, + cur_idx, + *this)); + + // We want to detect an unwind that cycles erronously and stop backtracing. + // Don't want this maximum unwind limit to be too low -- if you have a backtrace + // with an "infinitely recursing" bug, it will crash when the stack blows out + // and the first 35,000 frames are uninteresting - it's the top most 5 frames that + // you actually care about. So you can't just cap the unwind at 10,000 or something. + // Realistically anything over around 200,000 is going to blow out the stack space. + // If we're still unwinding at that point, we're probably never going to finish. + if (cur_idx > 300000) + { + if (log) + log->Printf ("%*sFrame %d unwound too many frames, assuming unwind has gone astray, stopping.", + cur_idx < 100 ? cur_idx : 100, "", cur_idx); + goto unwind_done; + } + + if (reg_ctx_sp.get() == NULL) + goto unwind_done; + + if (!reg_ctx_sp->IsValid()) + { + if (log) + { + log->Printf("%*sFrame %d invalid RegisterContext for this frame, stopping stack walk", + cur_idx < 100 ? cur_idx : 100, "", cur_idx); + } + goto unwind_done; + } + if (!reg_ctx_sp->GetCFA (cursor_sp->cfa)) + { + if (log) + { + log->Printf("%*sFrame %d did not get CFA for this frame, stopping stack walk", + cur_idx < 100 ? cur_idx : 100, "", cur_idx); + } + goto unwind_done; + } + if (abi && !abi->CallFrameAddressIsValid(cursor_sp->cfa)) + { + if (log) + { + log->Printf("%*sFrame %d did not get a valid CFA for this frame, stopping stack walk", + cur_idx < 100 ? cur_idx : 100, "", cur_idx); + } + goto unwind_done; + } + if (!reg_ctx_sp->ReadPC (cursor_sp->start_pc)) + { + if (log) + { + log->Printf("%*sFrame %d did not get PC for this frame, stopping stack walk", + cur_idx < 100 ? cur_idx : 100, "", cur_idx); + } + goto unwind_done; + } + if (abi && !abi->CodeAddressIsValid (cursor_sp->start_pc)) + { + if (log) + { + log->Printf("%*sFrame %d did not get a valid PC, stopping stack walk", + cur_idx < 100 ? cur_idx : 100, "", cur_idx); + } + goto unwind_done; + } + if (!m_frames.empty()) + { + if (m_frames.back()->start_pc == cursor_sp->start_pc) + { + if (m_frames.back()->cfa == cursor_sp->cfa) + goto unwind_done; // Infinite loop where the current cursor is the same as the previous one... + else if (abi && abi->StackUsesFrames()) + { + // We might have a CFA that is not using the frame pointer and + // we want to validate that the frame pointer is valid. + if (reg_ctx_sp->GetFP() == 0) + goto unwind_done; + } + } + } + cursor_sp->reg_ctx_lldb_sp = reg_ctx_sp; + m_frames.push_back (cursor_sp); + return true; + +unwind_done: + m_unwind_complete = true; + return false; +} + +bool +UnwindLLDB::DoGetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc) +{ + if (m_frames.size() == 0) + { + if (!AddFirstFrame()) + return false; + } + + ProcessSP process_sp (m_thread.GetProcess()); + ABI *abi = process_sp ? process_sp->GetABI().get() : NULL; + + while (idx >= m_frames.size() && AddOneMoreFrame (abi)) + ; + + if (idx < m_frames.size ()) + { + cfa = m_frames[idx]->cfa; + pc = m_frames[idx]->start_pc; + return true; + } + return false; +} + +lldb::RegisterContextSP +UnwindLLDB::DoCreateRegisterContextForFrame (StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t idx = frame->GetConcreteFrameIndex (); + + if (idx == 0) + { + return m_thread.GetRegisterContext(); + } + + if (m_frames.size() == 0) + { + if (!AddFirstFrame()) + return reg_ctx_sp; + } + + ProcessSP process_sp (m_thread.GetProcess()); + ABI *abi = process_sp ? process_sp->GetABI().get() : NULL; + + while (idx >= m_frames.size()) + { + if (!AddOneMoreFrame (abi)) + break; + } + + const uint32_t num_frames = m_frames.size(); + if (idx < num_frames) + { + Cursor *frame_cursor = m_frames[idx].get(); + reg_ctx_sp = frame_cursor->reg_ctx_lldb_sp; + } + return reg_ctx_sp; +} + +UnwindLLDB::RegisterContextLLDBSP +UnwindLLDB::GetRegisterContextForFrameNum (uint32_t frame_num) +{ + RegisterContextLLDBSP reg_ctx_sp; + if (frame_num < m_frames.size()) + reg_ctx_sp = m_frames[frame_num]->reg_ctx_lldb_sp; + return reg_ctx_sp; +} + +bool +UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, uint32_t starting_frame_num, bool pc_reg) +{ + int64_t frame_num = starting_frame_num; + if (frame_num >= m_frames.size()) + return false; + + // Never interrogate more than one level while looking for the saved pc value. If the value + // isn't saved by frame_num, none of the frames lower on the stack will have a useful value. + if (pc_reg) + { + UnwindLLDB::RegisterSearchResult result; + result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc); + if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound) + return true; + else + return false; + } + while (frame_num >= 0) + { + UnwindLLDB::RegisterSearchResult result; + result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc); + + // If we have unwind instructions saying that register N is saved in register M in the middle of + // the stack (and N can equal M here, meaning the register was not used in this function), then + // change the register number we're looking for to M and keep looking for a concrete location + // down the stack, or an actual value from a live RegisterContext at frame 0. + if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound + && regloc.type == UnwindLLDB::RegisterLocation::eRegisterInRegister + && frame_num > 0) + { + result = UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + lldb_regnum = regloc.location.register_number; + } + + if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound) + return true; + if (result == UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile) + return false; + frame_num--; + } + return false; +} diff --git a/source/Plugins/Process/Utility/UnwindLLDB.h b/source/Plugins/Process/Utility/UnwindLLDB.h new file mode 100644 index 000000000000..5725654a6869 --- /dev/null +++ b/source/Plugins/Process/Utility/UnwindLLDB.h @@ -0,0 +1,125 @@ +//===-- UnwindLLDB.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UnwindLLDB_h_ +#define lldb_UnwindLLDB_h_ + +#include <vector> + +#include "lldb/lldb-public.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Unwind.h" + +namespace lldb_private { + +class RegisterContextLLDB; + +class UnwindLLDB : public lldb_private::Unwind +{ +public: + UnwindLLDB (lldb_private::Thread &thread); + + virtual + ~UnwindLLDB() { } + + enum RegisterSearchResult + { + eRegisterFound = 0, + eRegisterNotFound, + eRegisterIsVolatile + }; + +protected: + friend class lldb_private::RegisterContextLLDB; + + struct RegisterLocation { + enum RegisterLocationTypes + { + eRegisterNotSaved = 0, // register was not preserved by callee. If volatile reg, is unavailable + eRegisterSavedAtMemoryLocation, // register is saved at a specific word of target mem (target_memory_location) + eRegisterInRegister, // register is available in a (possible other) register (register_number) + eRegisterSavedAtHostMemoryLocation, // register is saved at a word in lldb's address space + eRegisterValueInferred // register val was computed (and is in inferred_value) + }; + int type; + union + { + lldb::addr_t target_memory_location; + uint32_t register_number; // in eRegisterKindLLDB register numbering system + void* host_memory_location; + uint64_t inferred_value; // eRegisterValueInferred - e.g. stack pointer == cfa + offset + } location; + }; + + void + DoClear() + { + m_frames.clear(); + m_unwind_complete = false; + } + + virtual uint32_t + DoGetFrameCount(); + + bool + DoGetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& start_pc); + + lldb::RegisterContextSP + DoCreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + typedef std::shared_ptr<RegisterContextLLDB> RegisterContextLLDBSP; + + // Needed to retrieve the "next" frame (e.g. frame 2 needs to retrieve frame 1's RegisterContextLLDB) + // The RegisterContext for frame_num must already exist or this returns an empty shared pointer. + RegisterContextLLDBSP + GetRegisterContextForFrameNum (uint32_t frame_num); + + // Iterate over the RegisterContextLLDB's in our m_frames vector, look for the first one that + // has a saved location for this reg. + bool + SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, uint32_t starting_frame_num, bool pc_register); + + +private: + + struct Cursor + { + lldb::addr_t start_pc; // The start address of the function/symbol for this frame - current pc if unknown + lldb::addr_t cfa; // The canonical frame address for this stack frame + lldb_private::SymbolContext sctx; // A symbol context we'll contribute to & provide to the StackFrame creation + RegisterContextLLDBSP reg_ctx_lldb_sp; // These are all RegisterContextLLDB's + + Cursor () : start_pc (LLDB_INVALID_ADDRESS), cfa (LLDB_INVALID_ADDRESS), sctx(), reg_ctx_lldb_sp() { } + private: + DISALLOW_COPY_AND_ASSIGN (Cursor); + }; + + typedef std::shared_ptr<Cursor> CursorSP; + std::vector<CursorSP> m_frames; + bool m_unwind_complete; // If this is true, we've enumerated all the frames in the stack, and m_frames.size() is the + // number of frames, etc. Otherwise we've only gone as far as directly asked, and m_frames.size() + // is how far we've currently gone. + + + bool AddOneMoreFrame (ABI *abi); + bool AddFirstFrame (); + + //------------------------------------------------------------------ + // For UnwindLLDB only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (UnwindLLDB); +}; + +} // namespace lldb_private + +#endif // lldb_UnwindLLDB_h_ diff --git a/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp b/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp new file mode 100644 index 000000000000..d011314b0963 --- /dev/null +++ b/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp @@ -0,0 +1,275 @@ +//===-- UnwindMacOSXFrameBackchain.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include "RegisterContextMacOSXFrameBackchain.h" + +using namespace lldb; +using namespace lldb_private; + +UnwindMacOSXFrameBackchain::UnwindMacOSXFrameBackchain (Thread &thread) : + Unwind (thread), + m_cursors() +{ +} + +uint32_t +UnwindMacOSXFrameBackchain::DoGetFrameCount() +{ + if (m_cursors.empty()) + { + ExecutionContext exe_ctx (m_thread.shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + const ArchSpec& target_arch = target->GetArchitecture (); + // Frame zero should always be supplied by the thread... + exe_ctx.SetFrameSP (m_thread.GetStackFrameAtIndex (0)); + + if (target_arch.GetAddressByteSize() == 8) + GetStackFrameData_x86_64 (exe_ctx); + else + GetStackFrameData_i386 (exe_ctx); + } + } + return m_cursors.size(); +} + +bool +UnwindMacOSXFrameBackchain::DoGetFrameInfoAtIndex (uint32_t idx, addr_t& cfa, addr_t& pc) +{ + const uint32_t frame_count = GetFrameCount(); + if (idx < frame_count) + { + if (m_cursors[idx].pc == LLDB_INVALID_ADDRESS) + return false; + if (m_cursors[idx].fp == LLDB_INVALID_ADDRESS) + return false; + + pc = m_cursors[idx].pc; + cfa = m_cursors[idx].fp; + + return true; + } + return false; +} + +lldb::RegisterContextSP +UnwindMacOSXFrameBackchain::DoCreateRegisterContextForFrame (StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_idx = frame->GetConcreteFrameIndex (); + const uint32_t frame_count = GetFrameCount(); + if (concrete_idx < frame_count) + reg_ctx_sp.reset (new RegisterContextMacOSXFrameBackchain (m_thread, concrete_idx, m_cursors[concrete_idx])); + return reg_ctx_sp; +} + +size_t +UnwindMacOSXFrameBackchain::GetStackFrameData_i386 (const ExecutionContext &exe_ctx) +{ + m_cursors.clear(); + + StackFrame *first_frame = exe_ctx.GetFramePtr(); + + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + return 0; + + std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair; + + struct Frame_i386 + { + uint32_t fp; + uint32_t pc; + }; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); + assert (reg_ctx); + + Cursor cursor; + cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS); + cursor.fp = reg_ctx->GetFP (0); + + Frame_i386 frame = { static_cast<uint32_t>(cursor.fp), static_cast<uint32_t>(cursor.pc) }; + + m_cursors.push_back(cursor); + + const size_t k_frame_size = sizeof(frame); + Error error; + while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) + { + // Read both the FP and PC (8 bytes) + if (process->ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) + break; + if (frame.pc >= 0x1000) + { + cursor.pc = frame.pc; + cursor.fp = frame.fp; + m_cursors.push_back (cursor); + } + } + if (!m_cursors.empty()) + { + lldb::addr_t first_frame_pc = m_cursors.front().pc; + if (first_frame_pc != LLDB_INVALID_ADDRESS) + { + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextSymbol; + + SymbolContext first_frame_sc (first_frame->GetSymbolContext(resolve_scope)); + const AddressRange *addr_range_ptr = NULL; + AddressRange range; + if (first_frame_sc.function) + addr_range_ptr = &first_frame_sc.function->GetAddressRange(); + else if (first_frame_sc.symbol) + { + range.GetBaseAddress() = first_frame_sc.symbol->GetAddress(); + range.SetByteSize (first_frame_sc.symbol->GetByteSize()); + addr_range_ptr = ⦥ + } + + if (addr_range_ptr) + { + if (first_frame->GetFrameCodeAddress() == addr_range_ptr->GetBaseAddress()) + { + // We are at the first instruction, so we can recover the + // previous PC by dereferencing the SP + lldb::addr_t first_frame_sp = reg_ctx->GetSP (0); + // Read the real second frame return address into frame.pc + if (first_frame_sp && process->ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) + { + cursor.fp = m_cursors.front().fp; + cursor.pc = frame.pc; // Set the new second frame PC + + // Insert the second frame + m_cursors.insert(m_cursors.begin()+1, cursor); + + m_cursors.front().fp = first_frame_sp; + } + } + } + } + } +// uint32_t i=0; +// printf(" PC FP\n"); +// printf(" ------------------ ------------------ \n"); +// for (i=0; i<m_cursors.size(); ++i) +// { +// printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 "\n", i, m_cursors[i].pc, m_cursors[i].fp); +// } + return m_cursors.size(); +} + + +size_t +UnwindMacOSXFrameBackchain::GetStackFrameData_x86_64 (const ExecutionContext &exe_ctx) +{ + m_cursors.clear(); + + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + return 0; + + StackFrame *first_frame = exe_ctx.GetFramePtr(); + + std::pair<lldb::addr_t, lldb::addr_t> fp_pc_pair; + + struct Frame_x86_64 + { + uint64_t fp; + uint64_t pc; + }; + + RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); + assert (reg_ctx); + + Cursor cursor; + cursor.pc = reg_ctx->GetPC (LLDB_INVALID_ADDRESS); + cursor.fp = reg_ctx->GetFP (0); + + Frame_x86_64 frame = { cursor.fp, cursor.pc }; + + m_cursors.push_back(cursor); + Error error; + const size_t k_frame_size = sizeof(frame); + while (frame.fp != 0 && frame.pc != 0 && ((frame.fp & 7) == 0)) + { + // Read both the FP and PC (16 bytes) + if (process->ReadMemory (frame.fp, &frame.fp, k_frame_size, error) != k_frame_size) + break; + + if (frame.pc >= 0x1000) + { + cursor.pc = frame.pc; + cursor.fp = frame.fp; + m_cursors.push_back (cursor); + } + } + if (!m_cursors.empty()) + { + lldb::addr_t first_frame_pc = m_cursors.front().pc; + if (first_frame_pc != LLDB_INVALID_ADDRESS) + { + const uint32_t resolve_scope = eSymbolContextModule | + eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextSymbol; + + SymbolContext first_frame_sc(first_frame->GetSymbolContext(resolve_scope)); + const AddressRange *addr_range_ptr = NULL; + AddressRange range; + if (first_frame_sc.function) + addr_range_ptr = &first_frame_sc.function->GetAddressRange(); + else if (first_frame_sc.symbol) + { + range.GetBaseAddress() = first_frame_sc.symbol->GetAddress(); + range.SetByteSize (first_frame_sc.symbol->GetByteSize()); + addr_range_ptr = ⦥ + } + + if (addr_range_ptr) + { + if (first_frame->GetFrameCodeAddress() == addr_range_ptr->GetBaseAddress()) + { + // We are at the first instruction, so we can recover the + // previous PC by dereferencing the SP + lldb::addr_t first_frame_sp = reg_ctx->GetSP (0); + // Read the real second frame return address into frame.pc + if (process->ReadMemory (first_frame_sp, &frame.pc, sizeof(frame.pc), error) == sizeof(frame.pc)) + { + cursor.fp = m_cursors.front().fp; + cursor.pc = frame.pc; // Set the new second frame PC + + // Insert the second frame + m_cursors.insert(m_cursors.begin()+1, cursor); + + m_cursors.front().fp = first_frame_sp; + } + } + } + } + } + return m_cursors.size(); +} + diff --git a/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h b/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h new file mode 100644 index 000000000000..2695376fd6e0 --- /dev/null +++ b/source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h @@ -0,0 +1,74 @@ +//===-- UnwindMacOSXFrameBackchain.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UnwindMacOSXFrameBackchain_h_ +#define lldb_UnwindMacOSXFrameBackchain_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Unwind.h" + +class UnwindMacOSXFrameBackchain : public lldb_private::Unwind +{ +public: + UnwindMacOSXFrameBackchain (lldb_private::Thread &thread); + + virtual + ~UnwindMacOSXFrameBackchain() + { + } + +protected: + virtual void + DoClear() + { + m_cursors.clear(); + } + + virtual uint32_t + DoGetFrameCount(); + + bool + DoGetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc); + + lldb::RegisterContextSP + DoCreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + friend class RegisterContextMacOSXFrameBackchain; + + struct Cursor + { + lldb::addr_t pc; // Program counter + lldb::addr_t fp; // Frame pointer for us with backchain + }; + +private: + std::vector<Cursor> m_cursors; + + size_t + GetStackFrameData_i386 (const lldb_private::ExecutionContext &exe_ctx); + + size_t + GetStackFrameData_x86_64 (const lldb_private::ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + // For UnwindMacOSXFrameBackchain only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (UnwindMacOSXFrameBackchain); +}; + +#endif // lldb_UnwindMacOSXFrameBackchain_h_ diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/source/Plugins/Process/elf-core/ProcessElfCore.cpp new file mode 100644 index 000000000000..dd553ce36c89 --- /dev/null +++ b/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -0,0 +1,619 @@ +//===-- ProcessElfCore.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <stdlib.h> + +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/DynamicLoader.h" +#include "ProcessPOSIXLog.h" + +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +#include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" + +// Project includes +#include "ProcessElfCore.h" +#include "ThreadElfCore.h" + +using namespace lldb_private; + +ConstString +ProcessElfCore::GetPluginNameStatic() +{ + static ConstString g_name("elf-core"); + return g_name; +} + +const char * +ProcessElfCore::GetPluginDescriptionStatic() +{ + return "ELF core dump plug-in."; +} + +void +ProcessElfCore::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessElfCore::CreateInstance); +} + + +lldb::ProcessSP +ProcessElfCore::CreateInstance (Target &target, Listener &listener, const FileSpec *crash_file) +{ + lldb::ProcessSP process_sp; + if (crash_file) + process_sp.reset(new ProcessElfCore (target, listener, *crash_file)); + return process_sp; +} + +bool +ProcessElfCore::CanDebug(Target &target, bool plugin_specified_by_name) +{ + // For now we are just making sure the file exists for a given module + if (!m_core_module_sp && m_core_file.Exists()) + { + ModuleSpec core_module_spec(m_core_file, target.GetArchitecture()); + Error error (ModuleList::GetSharedModule (core_module_spec, m_core_module_sp, + NULL, NULL, NULL)); + if (m_core_module_sp) + { + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile) + return true; + } + } + return false; +} + +//---------------------------------------------------------------------- +// ProcessElfCore constructor +//---------------------------------------------------------------------- +ProcessElfCore::ProcessElfCore(Target& target, Listener &listener, + const FileSpec &core_file) : + Process (target, listener), + m_core_module_sp (), + m_core_file (core_file), + m_dyld_plugin_name (), + m_thread_data_valid(false), + m_thread_data(), + m_core_aranges () +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessElfCore::~ProcessElfCore() +{ + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +ConstString +ProcessElfCore::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessElfCore::GetPluginVersion() +{ + return 1; +} + +lldb::addr_t +ProcessElfCore::AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader *header) +{ + lldb::addr_t addr = header->p_vaddr; + FileRange file_range (header->p_offset, header->p_filesz); + VMRangeToFileOffset::Entry range_entry(addr, header->p_memsz, file_range); + + VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); + if (last_entry && + last_entry->GetRangeEnd() == range_entry.GetRangeBase() && + last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase()) + { + last_entry->SetRangeEnd (range_entry.GetRangeEnd()); + last_entry->data.SetRangeEnd (range_entry.data.GetRangeEnd()); + } + else + { + m_core_aranges.Append(range_entry); + } + + return addr; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessElfCore::DoLoadCore () +{ + Error error; + if (!m_core_module_sp) + { + error.SetErrorString ("invalid core module"); + return error; + } + + ObjectFileELF *core = (ObjectFileELF *)(m_core_module_sp->GetObjectFile()); + if (core == NULL) + { + error.SetErrorString ("invalid core object file"); + return error; + } + + const uint32_t num_segments = core->GetProgramHeaderCount(); + if (num_segments == 0) + { + error.SetErrorString ("core file has no sections"); + return error; + } + + SetCanJIT(false); + + m_thread_data_valid = true; + + bool ranges_are_sorted = true; + lldb::addr_t vm_addr = 0; + /// Walk through segments and Thread and Address Map information. + /// PT_NOTE - Contains Thread and Register information + /// PT_LOAD - Contains a contiguous range of Process Address Space + for(uint32_t i = 1; i <= num_segments; i++) + { + const elf::ELFProgramHeader *header = core->GetProgramHeaderByIndex(i); + assert(header != NULL); + + DataExtractor data = core->GetSegmentDataByIndex(i); + + // Parse thread contexts and auxv structure + if (header->p_type == llvm::ELF::PT_NOTE) + ParseThreadContextsFromNoteSegment(header, data); + + // PT_LOAD segments contains address map + if (header->p_type == llvm::ELF::PT_LOAD) + { + lldb::addr_t last_addr = AddAddressRangeFromLoadSegment(header); + if (vm_addr > last_addr) + ranges_are_sorted = false; + vm_addr = last_addr; + } + } + + if (!ranges_are_sorted) + m_core_aranges.Sort(); + + // Even if the architecture is set in the target, we need to override + // it to match the core file which is always single arch. + ArchSpec arch (m_core_module_sp->GetArchitecture()); + if (arch.IsValid()) + m_target.SetArchitecture(arch); + + return error; +} + +lldb_private::DynamicLoader * +ProcessElfCore::GetDynamicLoader () +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, DynamicLoaderPOSIXDYLD::GetPluginNameStatic().GetCString())); + return m_dyld_ap.get(); +} + +bool +ProcessElfCore::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + const uint32_t num_threads = GetNumThreadContexts (); + if (!m_thread_data_valid) + return false; + + for (lldb::tid_t tid = 0; tid < num_threads; ++tid) + { + const ThreadData &td = m_thread_data[tid]; + lldb::ThreadSP thread_sp(new ThreadElfCore (*this, tid, td)); + new_thread_list.AddThread (thread_sp); + } + return new_thread_list.GetSize(false) > 0; +} + +void +ProcessElfCore::RefreshStateAfterStop () +{ +} + +Error +ProcessElfCore::DoDestroy () +{ + return Error(); +} + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessElfCore::IsAlive () +{ + return true; +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessElfCore::ReadMemory (lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + // Don't allow the caching that lldb_private::Process::ReadMemory does + // since in core files we have it all cached our our core file anyway. + return DoReadMemory (addr, buf, size, error); +} + +size_t +ProcessElfCore::DoReadMemory (lldb::addr_t addr, void *buf, size_t size, Error &error) +{ + ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); + + if (core_objfile == NULL) + return 0; + + // Get the address range + const VMRangeToFileOffset::Entry *address_range = m_core_aranges.FindEntryThatContains (addr); + if (address_range == NULL || address_range->GetRangeEnd() < addr) + { + error.SetErrorStringWithFormat ("core file does not contain 0x%" PRIx64, addr); + return 0; + } + + // Convert the address into core file offset + const lldb::addr_t offset = addr - address_range->GetRangeBase(); + const lldb::addr_t file_start = address_range->data.GetRangeBase(); + const lldb::addr_t file_end = address_range->data.GetRangeEnd(); + size_t bytes_to_read = size; // Number of bytes to read from the core file + size_t bytes_copied = 0; // Number of bytes actually read from the core file + size_t zero_fill_size = 0; // Padding + lldb::addr_t bytes_left = 0; // Number of bytes available in the core file from the given address + + if (file_end > offset) + bytes_left = file_end - offset; + + if (bytes_to_read > bytes_left) + { + zero_fill_size = bytes_to_read - bytes_left; + bytes_to_read = bytes_left; + } + + // If there is data available on the core file read it + if (bytes_to_read) + bytes_copied = core_objfile->CopyData(offset + file_start, bytes_to_read, buf); + + assert(zero_fill_size <= size); + // Pad remaining bytes + if (zero_fill_size) + memset(((char *)buf) + bytes_copied, 0, zero_fill_size); + + return bytes_copied + zero_fill_size; +} + +void +ProcessElfCore::Clear() +{ + m_thread_list.Clear(); +} + +void +ProcessElfCore::Initialize() +{ + static bool g_initialized = false; + + if (g_initialized == false) + { + g_initialized = true; + PluginManager::RegisterPlugin (GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); + } +} + +lldb::addr_t +ProcessElfCore::GetImageInfoAddress() +{ + Target *target = &GetTarget(); + ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(); + + if (addr.IsValid()) + return addr.GetLoadAddress(target); + return LLDB_INVALID_ADDRESS; +} + +/// Core files PT_NOTE segment descriptor types +enum { + NT_PRSTATUS = 1, + NT_FPREGSET, + NT_PRPSINFO, + NT_TASKSTRUCT, + NT_PLATFORM, + NT_AUXV +}; + +enum { + NT_FREEBSD_PRSTATUS = 1, + NT_FREEBSD_FPREGSET, + NT_FREEBSD_PRPSINFO, + NT_FREEBSD_THRMISC = 7, + NT_FREEBSD_PROCSTAT_AUXV = 16 +}; + +/// Align the given value to next boundary specified by the alignment bytes +static uint32_t +AlignToNext(uint32_t value, int alignment_bytes) +{ + return (value + alignment_bytes - 1) & ~(alignment_bytes - 1); +} + +/// Note Structure found in ELF core dumps. +/// This is PT_NOTE type program/segments in the core file. +struct ELFNote +{ + elf::elf_word n_namesz; + elf::elf_word n_descsz; + elf::elf_word n_type; + + std::string n_name; + + ELFNote() : n_namesz(0), n_descsz(0), n_type(0) + { + } + + /// Parse an ELFNote entry from the given DataExtractor starting at position + /// \p offset. + /// + /// @param[in] data + /// The DataExtractor to read from. + /// + /// @param[in,out] offset + /// Pointer to an offset in the data. On return the offset will be + /// advanced by the number of bytes read. + /// + /// @return + /// True if the ELFRel entry was successfully read and false otherwise. + bool + Parse(const DataExtractor &data, lldb::offset_t *offset) + { + // Read all fields. + if (data.GetU32(offset, &n_namesz, 3) == NULL) + return false; + + // The name field is required to be nul-terminated, and n_namesz + // includes the terminating nul in observed implementations (contrary + // to the ELF-64 spec). A special case is needed for cores generated + // by some older Linux versions, which write a note named "CORE" + // without a nul terminator and n_namesz = 4. + if (n_namesz == 4) + { + char buf[4]; + if (data.ExtractBytes (*offset, 4, data.GetByteOrder(), buf) != 4) + return false; + if (strncmp (buf, "CORE", 4) == 0) + { + n_name = "CORE"; + *offset += 4; + return true; + } + } + + const char *cstr = data.GetCStr(offset, AlignToNext(n_namesz, 4)); + if (cstr == NULL) + { + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log) + log->Printf("Failed to parse note name lacking nul terminator"); + + return false; + } + n_name = cstr; + return true; + } +}; + +// Parse a FreeBSD NT_PRSTATUS note - see FreeBSD sys/procfs.h for details. +static void +ParseFreeBSDPrStatus(ThreadData *thread_data, DataExtractor &data, + ArchSpec &arch) +{ + lldb::offset_t offset = 0; + bool have_padding = (arch.GetMachine() == llvm::Triple::x86_64); + int pr_version = data.GetU32(&offset); + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log) + { + if (pr_version > 1) + log->Printf("FreeBSD PRSTATUS unexpected version %d", pr_version); + } + + if (have_padding) + offset += 4; + offset += 28; // pr_statussz, pr_gregsetsz, pr_fpregsetsz, pr_osreldate + thread_data->signo = data.GetU32(&offset); // pr_cursig + offset += 4; // pr_pid + if (have_padding) + offset += 4; + + size_t len = data.GetByteSize() - offset; + thread_data->gpregset = DataExtractor(data, offset, len); +} + +static void +ParseFreeBSDThrMisc(ThreadData *thread_data, DataExtractor &data) +{ + lldb::offset_t offset = 0; + thread_data->name = data.GetCStr(&offset, 20); +} + +/// Parse Thread context from PT_NOTE segment and store it in the thread list +/// Notes: +/// 1) A PT_NOTE segment is composed of one or more NOTE entries. +/// 2) NOTE Entry contains a standard header followed by variable size data. +/// (see ELFNote structure) +/// 3) A Thread Context in a core file usually described by 3 NOTE entries. +/// a) NT_PRSTATUS - Register context +/// b) NT_PRPSINFO - Process info(pid..) +/// c) NT_FPREGSET - Floating point registers +/// 4) The NOTE entries can be in any order +/// 5) If a core file contains multiple thread contexts then there is two data forms +/// a) Each thread context(2 or more NOTE entries) contained in its own segment (PT_NOTE) +/// b) All thread context is stored in a single segment(PT_NOTE). +/// This case is little tricker since while parsing we have to find where the +/// new thread starts. The current implementation marks beginning of +/// new thread when it finds NT_PRSTATUS or NT_PRPSINFO NOTE entry. +/// For case (b) there may be either one NT_PRPSINFO per thread, or a single +/// one that applies to all threads (depending on the platform type). +void +ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader *segment_header, + DataExtractor segment_data) +{ + assert(segment_header && segment_header->p_type == llvm::ELF::PT_NOTE); + + lldb::offset_t offset = 0; + ThreadData *thread_data = new ThreadData(); + bool have_prstatus = false; + bool have_prpsinfo = false; + + ArchSpec arch = GetArchitecture(); + ELFLinuxPrPsInfo prpsinfo; + ELFLinuxPrStatus prstatus; + size_t header_size; + size_t len; + + // Loop through the NOTE entires in the segment + while (offset < segment_header->p_filesz) + { + ELFNote note = ELFNote(); + note.Parse(segment_data, &offset); + + // Beginning of new thread + if ((note.n_type == NT_PRSTATUS && have_prstatus) || + (note.n_type == NT_PRPSINFO && have_prpsinfo)) + { + assert(thread_data->gpregset.GetByteSize() > 0); + // Add the new thread to thread list + m_thread_data.push_back(*thread_data); + thread_data = new ThreadData(); + have_prstatus = false; + have_prpsinfo = false; + } + + size_t note_start, note_size; + note_start = offset; + note_size = AlignToNext(note.n_descsz, 4); + + // Store the NOTE information in the current thread + DataExtractor note_data (segment_data, note_start, note_size); + if (note.n_name == "FreeBSD") + { + switch (note.n_type) + { + case NT_FREEBSD_PRSTATUS: + have_prstatus = true; + ParseFreeBSDPrStatus(thread_data, note_data, arch); + break; + case NT_FREEBSD_FPREGSET: + thread_data->fpregset = note_data; + break; + case NT_FREEBSD_PRPSINFO: + have_prpsinfo = true; + break; + case NT_FREEBSD_THRMISC: + ParseFreeBSDThrMisc(thread_data, note_data); + break; + case NT_FREEBSD_PROCSTAT_AUXV: + // FIXME: FreeBSD sticks an int at the beginning of the note + m_auxv = DataExtractor(segment_data, note_start + 4, note_size - 4); + break; + default: + break; + } + } + else + { + switch (note.n_type) + { + case NT_PRSTATUS: + have_prstatus = true; + prstatus.Parse(note_data, arch); + thread_data->signo = prstatus.pr_cursig; + header_size = ELFLinuxPrStatus::GetSize(arch); + len = note_data.GetByteSize() - header_size; + thread_data->gpregset = DataExtractor(note_data, header_size, len); + break; + case NT_FPREGSET: + thread_data->fpregset = note_data; + break; + case NT_PRPSINFO: + have_prpsinfo = true; + prpsinfo.Parse(note_data, arch); + thread_data->name = prpsinfo.pr_fname; + break; + case NT_AUXV: + m_auxv = DataExtractor(note_data); + break; + default: + break; + } + } + + offset += note_size; + } + // Add last entry in the note section + if (thread_data && thread_data->gpregset.GetByteSize() > 0) + { + m_thread_data.push_back(*thread_data); + } +} + +uint32_t +ProcessElfCore::GetNumThreadContexts () +{ + if (!m_thread_data_valid) + DoLoadCore(); + return m_thread_data.size(); +} + +ArchSpec +ProcessElfCore::GetArchitecture() +{ + ObjectFileELF *core_file = (ObjectFileELF *)(m_core_module_sp->GetObjectFile()); + ArchSpec arch; + core_file->GetArchitecture(arch); + return arch; +} + +const lldb::DataBufferSP +ProcessElfCore::GetAuxvData() +{ + const uint8_t *start = m_auxv.GetDataStart(); + size_t len = m_auxv.GetByteSize(); + lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(start, len)); + return buffer; +} + diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.h b/source/Plugins/Process/elf-core/ProcessElfCore.h new file mode 100644 index 000000000000..1c1ed98ce17a --- /dev/null +++ b/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -0,0 +1,171 @@ +//===-- ProcessElfCore.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// Notes about Linux Process core dumps: +// 1) Linux core dump is stored as ELF file. +// 2) The ELF file's PT_NOTE and PT_LOAD segments describes the program's +// address space and thread contexts. +// 3) PT_NOTE segment contains note entries which describes a thread context. +// 4) PT_LOAD segment describes a valid contigous range of process address +// space. +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessElfCore_h_ +#define liblldb_ProcessElfCore_h_ + +// C++ Includes +#include <list> +#include <vector> + +// Other libraries and framework includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Target/Process.h" + +#include "Plugins/ObjectFile/ELF/ELFHeader.h" + +struct ThreadData; + +class ProcessElfCore : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance (lldb_private::Target& target, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessElfCore(lldb_private::Target& target, + lldb_private::Listener &listener, + const lldb_private::FileSpec &core_file); + + virtual + ~ProcessElfCore(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb_private::Target &target, + bool plugin_specified_by_name); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb_private::Error + DoLoadCore (); + + virtual lldb_private::DynamicLoader * + GetDynamicLoader (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t + ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual lldb::addr_t + GetImageInfoAddress (); + + lldb_private::ArchSpec + GetArchitecture(); + + // Returns AUXV structure found in the core file + const lldb::DataBufferSP + GetAuxvData(); + +protected: + void + Clear ( ); + + virtual bool + UpdateThreadList (lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list); + +private: + //------------------------------------------------------------------ + // For ProcessElfCore only + //------------------------------------------------------------------ + typedef lldb_private::Range<lldb::addr_t, lldb::addr_t> FileRange; + typedef lldb_private::RangeDataArray<lldb::addr_t, lldb::addr_t, FileRange, 1> VMRangeToFileOffset; + + lldb::ModuleSP m_core_module_sp; + lldb_private::FileSpec m_core_file; + std::string m_dyld_plugin_name; + DISALLOW_COPY_AND_ASSIGN (ProcessElfCore); + + // True if m_thread_contexts contains valid entries + bool m_thread_data_valid; + + // Contain thread data read from NOTE segments + std::vector<ThreadData> m_thread_data; + + // AUXV structure found from the NOTE segment + lldb_private::DataExtractor m_auxv; + + // Address ranges found in the core + VMRangeToFileOffset m_core_aranges; + + // Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment + void + ParseThreadContextsFromNoteSegment (const elf::ELFProgramHeader *segment_header, + lldb_private::DataExtractor segment_data); + + // Returns number of thread contexts stored in the core file + uint32_t + GetNumThreadContexts(); + + // Parse a contiguous address range of the process from LOAD segment + lldb::addr_t + AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader *header); +}; + +#endif // liblldb_ProcessElffCore_h_ diff --git a/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.cpp b/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.cpp new file mode 100644 index 000000000000..6210175f9a7f --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.cpp @@ -0,0 +1,68 @@ +//===-- RegisterContextCoreFreeBSD_x86_64.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" +#include "RegisterContextCoreFreeBSD_x86_64.h" + +RegisterContextCoreFreeBSD_x86_64::RegisterContextCoreFreeBSD_x86_64(Thread &thread, + const DataExtractor &gpregset, const DataExtractor &fpregset) + : RegisterContextFreeBSD_x86_64(thread, 0) +{ + size_t size, len; + + size = GetGPRSize(); + m_gpregset = new uint8_t[size]; + len = gpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_gpregset); + assert(len == size); +} + +RegisterContextCoreFreeBSD_x86_64::~RegisterContextCoreFreeBSD_x86_64() +{ + delete [] m_gpregset; +} + +bool +RegisterContextCoreFreeBSD_x86_64::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + value = *(uint64_t *)(m_gpregset + reg_info->byte_offset); + return true; +} + +bool +RegisterContextCoreFreeBSD_x86_64::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCoreFreeBSD_x86_64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) +{ + return false; +} + +bool +RegisterContextCoreFreeBSD_x86_64::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCoreFreeBSD_x86_64::UpdateAfterBreakpoint() +{ + return false; +} + +bool +RegisterContextCoreFreeBSD_x86_64::HardwareSingleStep(bool enable) +{ + return false; +} + diff --git a/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.h b/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.h new file mode 100644 index 000000000000..acd594a6e666 --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.h @@ -0,0 +1,47 @@ +//===-- RegisterContextCoreFreeBSD_x86_64.h ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextCoreFreeBSD_x86_64_H_ +#define liblldb_RegisterContextCoreFreeBSD_x86_64_H_ + +#include "Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h" + +using namespace lldb_private; + +class RegisterContextCoreFreeBSD_x86_64: public RegisterContextFreeBSD_x86_64 +{ +public: + RegisterContextCoreFreeBSD_x86_64 (Thread &thread, const DataExtractor &gpregset, + const DataExtractor &fpregset); + + ~RegisterContextCoreFreeBSD_x86_64(); + + virtual bool + ReadRegister(const RegisterInfo *reg_info, RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + bool + HardwareSingleStep(bool enable); + + bool + UpdateAfterBreakpoint(); + +private: + uint8_t *m_gpregset; +}; + +#endif // #ifndef liblldb_RegisterContextCoreFreeBSD_x86_64_H_ diff --git a/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.cpp b/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.cpp new file mode 100644 index 000000000000..d9e3f6d5f90b --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.cpp @@ -0,0 +1,68 @@ +//===-- RegisterContextCoreLinux_x86_64.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" +#include "RegisterContextCoreLinux_x86_64.h" + +RegisterContextCoreLinux_x86_64::RegisterContextCoreLinux_x86_64(Thread &thread, + const DataExtractor &gpregset, + const DataExtractor &fpregset) + : RegisterContextLinux_x86_64(thread, 0) +{ + size_t size, len; + + size = GetGPRSize(); + m_gpregset = new uint8_t[size]; + len = gpregset.ExtractBytes(0, size, lldb::eByteOrderLittle, m_gpregset); + assert(len == size); +} + +RegisterContextCoreLinux_x86_64::~RegisterContextCoreLinux_x86_64() +{ + delete [] m_gpregset; +} + +bool +RegisterContextCoreLinux_x86_64::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + value = *(uint64_t *)(m_gpregset + reg_info->byte_offset); + return true; +} + +bool +RegisterContextCoreLinux_x86_64::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCoreLinux_x86_64::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) +{ + return false; +} + +bool +RegisterContextCoreLinux_x86_64::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCoreLinux_x86_64::UpdateAfterBreakpoint() +{ + return false; +} + +bool +RegisterContextCoreLinux_x86_64::HardwareSingleStep(bool enable) +{ + return false; +} diff --git a/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.h b/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.h new file mode 100644 index 000000000000..9cf545afd56a --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.h @@ -0,0 +1,54 @@ +//===-- RegisterContextCoreLinux_x86_64.h ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextCoreLinux_x86_64_H_ +#define liblldb_RegisterContextCoreLinux_x86_64_H_ + +#include "Plugins/Process/POSIX/RegisterContextLinux_x86_64.h" + +using namespace lldb_private; + +class RegisterContextCoreLinux_x86_64: public RegisterContextLinux_x86_64 +{ +public: + RegisterContextCoreLinux_x86_64 (Thread &thread, const DataExtractor &gpregset, + const DataExtractor &fpregset); + + ~RegisterContextCoreLinux_x86_64(); + + virtual bool + ReadRegister(const RegisterInfo *reg_info, RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + virtual bool + WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + bool + HardwareSingleStep(bool enable); + + bool + UpdateAfterBreakpoint(); + +protected: + bool + ReadFPR() + { + assert(0); + } + +private: + uint8_t *m_gpregset; +}; + +#endif // #ifndef liblldb_RegisterContextCoreLinux_x86_64_H_ diff --git a/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/source/Plugins/Process/elf-core/ThreadElfCore.cpp new file mode 100644 index 000000000000..a7229663dc89 --- /dev/null +++ b/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -0,0 +1,176 @@ +//===-- ThreadElfCore.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "ProcessPOSIXLog.h" + +#include "ThreadElfCore.h" +#include "ProcessElfCore.h" +#include "RegisterContextCoreFreeBSD_x86_64.h" +#include "RegisterContextCoreLinux_x86_64.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Construct a Thread object with given data +//---------------------------------------------------------------------- +ThreadElfCore::ThreadElfCore (Process &process, tid_t tid, + const ThreadData &td) : + Thread(process, tid), + m_thread_name(td.name), + m_thread_reg_ctx_sp (), + m_signo(td.signo), + m_gpregset_data(td.gpregset), + m_fpregset_data(td.fpregset) +{ +} + +ThreadElfCore::~ThreadElfCore () +{ + DestroyThread(); +} + +void +ThreadElfCore::RefreshStateAfterStop() +{ + GetRegisterContext()->InvalidateIfNeeded (false); +} + +void +ThreadElfCore::ClearStackFrames () +{ + Unwind *unwinder = GetUnwinder (); + if (unwinder) + unwinder->Clear(); + Thread::ClearStackFrames(); +} + +RegisterContextSP +ThreadElfCore::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) { + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + } + return m_reg_context_sp; +} + +RegisterContextSP +ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame) +{ + RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex (); + + if (concrete_frame_idx == 0) + { + if (m_thread_reg_ctx_sp) + return m_thread_reg_ctx_sp; + + ProcessElfCore *process = static_cast<ProcessElfCore *>(GetProcess().get()); + ArchSpec arch = process->GetArchitecture(); + switch (arch.GetMachine()) + { + case llvm::Triple::x86_64: + switch (arch.GetTriple().getOS()) + { + case llvm::Triple::FreeBSD: + m_thread_reg_ctx_sp.reset(new RegisterContextCoreFreeBSD_x86_64 (*this, m_gpregset_data, m_fpregset_data)); + break; + case llvm::Triple::Linux: + m_thread_reg_ctx_sp.reset(new RegisterContextCoreLinux_x86_64 (*this, m_gpregset_data, m_fpregset_data)); + break; + default: + if (log) + log->Printf ("elf-core::%s:: OS(%d) not supported", + __FUNCTION__, arch.GetTriple().getOS()); + assert (false && "OS not supported"); + break; + } + break; + default: + if (log) + log->Printf ("elf-core::%s:: Architecture(%d) not supported", + __FUNCTION__, arch.GetMachine()); + assert (false && "Architecture not supported"); + } + reg_ctx_sp = m_thread_reg_ctx_sp; + } + else if (m_unwinder_ap.get()) + { + reg_ctx_sp = m_unwinder_ap->CreateRegisterContextForFrame (frame); + } + return reg_ctx_sp; +} + +bool +ThreadElfCore::CalculateStopInfo () +{ + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + SetStopInfo(StopInfo::CreateStopReasonWithSignal (*this, m_signo)); + return true; + } + return false; +} + +//---------------------------------------------------------------- +// Parse PRSTATUS from NOTE entry +//---------------------------------------------------------------- +ELFLinuxPrStatus::ELFLinuxPrStatus() +{ + memset(this, 0, sizeof(ELFLinuxPrStatus)); +} + +bool +ELFLinuxPrStatus::Parse(DataExtractor &data, ArchSpec &arch) +{ + ByteOrder byteorder = data.GetByteOrder(); + size_t len; + switch(arch.GetCore()) + { + case ArchSpec::eCore_x86_64_x86_64: + len = data.ExtractBytes(0, ELFLINUXPRSTATUS64_SIZE, byteorder, this); + return len == ELFLINUXPRSTATUS64_SIZE; + default: + return false; + } +} + +//---------------------------------------------------------------- +// Parse PRPSINFO from NOTE entry +//---------------------------------------------------------------- +ELFLinuxPrPsInfo::ELFLinuxPrPsInfo() +{ + memset(this, 0, sizeof(ELFLinuxPrPsInfo)); +} + +bool +ELFLinuxPrPsInfo::Parse(DataExtractor &data, ArchSpec &arch) +{ + ByteOrder byteorder = data.GetByteOrder(); + size_t len; + switch(arch.GetCore()) + { + case ArchSpec::eCore_x86_64_x86_64: + len = data.ExtractBytes(0, ELFLINUXPRPSINFO64_SIZE, byteorder, this); + return len == ELFLINUXPRPSINFO64_SIZE; + default: + return false; + } +} + diff --git a/source/Plugins/Process/elf-core/ThreadElfCore.h b/source/Plugins/Process/elf-core/ThreadElfCore.h new file mode 100644 index 000000000000..ca6339d7ec9d --- /dev/null +++ b/source/Plugins/Process/elf-core/ThreadElfCore.h @@ -0,0 +1,174 @@ +//===-- ThreadElfCore.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadElfCore_h_ +#define liblldb_ThreadElfCore_h_ + +#include <string> + +#include "lldb/Target/Thread.h" +#include "lldb/Core/DataExtractor.h" + +struct compat_timeval +{ + int64_t tv_sec; + int32_t tv_usec; +}; + +// PRSTATUS structure's size differs based on architecture. +// Currently parsing done only for x86-64 architecture by +// simply reading data from the buffer. +// The following macros are used to specify the size. +// Calculating size using sizeof() wont work because of padding. +#define ELFLINUXPRSTATUS64_SIZE (112) +#define ELFLINUXPRPSINFO64_SIZE (132) + +struct ELFLinuxPrStatus +{ + int32_t si_signo; + int32_t si_code; + int32_t si_errno; + + int16_t pr_cursig; + + uint64_t pr_sigpend; + uint64_t pr_sighold; + + uint32_t pr_pid; + uint32_t pr_ppid; + uint32_t pr_pgrp; + uint32_t pr_sid; + + compat_timeval pr_utime; + compat_timeval pr_stime; + compat_timeval pr_cutime; + compat_timeval pr_cstime; + + ELFLinuxPrStatus(); + + bool + Parse(lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch); + + static size_t + GetSize(lldb_private::ArchSpec &arch) + { + switch(arch.GetCore()) + { + case lldb_private::ArchSpec::eCore_x86_64_x86_64: + return ELFLINUXPRSTATUS64_SIZE; + default: + return 0; + } + } +}; + +struct ELFLinuxPrPsInfo +{ + char pr_state; + char pr_sname; + char pr_zomb; + char pr_nice; + uint64_t pr_flag; + uint32_t pr_uid; + uint32_t pr_gid; + int32_t pr_pid; + int32_t pr_ppid; + int32_t pr_pgrp; + int32_t pr_sid; + char pr_fname[16]; + char pr_psargs[80]; + + ELFLinuxPrPsInfo(); + + bool + Parse(lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch); + + static size_t + GetSize(lldb_private::ArchSpec &arch) + { + switch(arch.GetCore()) + { + case lldb_private::ArchSpec::eCore_x86_64_x86_64: + return ELFLINUXPRPSINFO64_SIZE; + default: + return 0; + } + } + +}; + +struct ThreadData +{ + lldb_private::DataExtractor gpregset; + lldb_private::DataExtractor fpregset; + int signo; + std::string name; +}; + +class ThreadElfCore : public lldb_private::Thread +{ +public: + ThreadElfCore (lldb_private::Process &process, lldb::tid_t tid, + const ThreadData &td); + + virtual + ~ThreadElfCore (); + + virtual void + RefreshStateAfterStop(); + + virtual lldb::RegisterContextSP + GetRegisterContext (); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + virtual void + ClearStackFrames (); + + static bool + ThreadIDIsValid (lldb::tid_t thread) + { + return thread != 0; + } + + virtual const char * + GetName () + { + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); + } + + void + SetName (const char *name) + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::string m_thread_name; + lldb::RegisterContextSP m_thread_reg_ctx_sp; + + int m_signo; + + lldb_private::DataExtractor m_gpregset_data; + lldb_private::DataExtractor m_fpregset_data; + + virtual bool CalculateStopInfo(); + +}; + +#endif // liblldb_ThreadElfCore_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp new file mode 100644 index 000000000000..d7efdf2302d8 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -0,0 +1,643 @@ +//===-- GDBRemoteCommunication.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "GDBRemoteCommunication.h" + +// C Includes +#include <limits.h> +#include <string.h> + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Target/Process.h" + +// Project includes +#include "ProcessGDBRemoteLog.h" + +#define DEBUGSERVER_BASENAME "debugserver" + +using namespace lldb; +using namespace lldb_private; + +GDBRemoteCommunication::History::History (uint32_t size) : + m_packets(), + m_curr_idx (0), + m_total_packet_count (0), + m_dumped_to_log (false) +{ + m_packets.resize(size); +} + +GDBRemoteCommunication::History::~History () +{ +} + +void +GDBRemoteCommunication::History::AddPacket (char packet_char, + PacketType type, + uint32_t bytes_transmitted) +{ + const size_t size = m_packets.size(); + if (size > 0) + { + const uint32_t idx = GetNextIndex(); + m_packets[idx].packet.assign (1, packet_char); + m_packets[idx].type = type; + m_packets[idx].bytes_transmitted = bytes_transmitted; + m_packets[idx].packet_idx = m_total_packet_count; + m_packets[idx].tid = Host::GetCurrentThreadID(); + } +} + +void +GDBRemoteCommunication::History::AddPacket (const std::string &src, + uint32_t src_len, + PacketType type, + uint32_t bytes_transmitted) +{ + const size_t size = m_packets.size(); + if (size > 0) + { + const uint32_t idx = GetNextIndex(); + m_packets[idx].packet.assign (src, 0, src_len); + m_packets[idx].type = type; + m_packets[idx].bytes_transmitted = bytes_transmitted; + m_packets[idx].packet_idx = m_total_packet_count; + m_packets[idx].tid = Host::GetCurrentThreadID(); + } +} + +void +GDBRemoteCommunication::History::Dump (lldb_private::Stream &strm) const +{ + const uint32_t size = GetNumPacketsInHistory (); + const uint32_t first_idx = GetFirstSavedPacketIndex (); + const uint32_t stop_idx = m_curr_idx + size; + for (uint32_t i = first_idx; i < stop_idx; ++i) + { + const uint32_t idx = NormalizeIndex (i); + const Entry &entry = m_packets[idx]; + if (entry.type == ePacketTypeInvalid || entry.packet.empty()) + break; + strm.Printf ("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n", + entry.packet_idx, + entry.tid, + entry.bytes_transmitted, + (entry.type == ePacketTypeSend) ? "send" : "read", + entry.packet.c_str()); + } +} + +void +GDBRemoteCommunication::History::Dump (lldb_private::Log *log) const +{ + if (log && !m_dumped_to_log) + { + m_dumped_to_log = true; + const uint32_t size = GetNumPacketsInHistory (); + const uint32_t first_idx = GetFirstSavedPacketIndex (); + const uint32_t stop_idx = m_curr_idx + size; + for (uint32_t i = first_idx; i < stop_idx; ++i) + { + const uint32_t idx = NormalizeIndex (i); + const Entry &entry = m_packets[idx]; + if (entry.type == ePacketTypeInvalid || entry.packet.empty()) + break; + log->Printf ("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s", + entry.packet_idx, + entry.tid, + entry.bytes_transmitted, + (entry.type == ePacketTypeSend) ? "send" : "read", + entry.packet.c_str()); + } + } +} + +//---------------------------------------------------------------------- +// GDBRemoteCommunication constructor +//---------------------------------------------------------------------- +GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, + const char *listener_name, + bool is_platform) : + Communication(comm_name), + m_packet_timeout (1), + m_sequence_mutex (Mutex::eMutexTypeRecursive), + m_public_is_running (false), + m_private_is_running (false), + m_history (512), + m_send_acks (true), + m_is_platform (is_platform) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunication::~GDBRemoteCommunication() +{ + if (IsConnected()) + { + Disconnect(); + } +} + +char +GDBRemoteCommunication::CalculcateChecksum (const char *payload, size_t payload_length) +{ + int checksum = 0; + + for (size_t i = 0; i < payload_length; ++i) + checksum += payload[i]; + + return checksum & 255; +} + +size_t +GDBRemoteCommunication::SendAck () +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + ConnectionStatus status = eConnectionStatusSuccess; + char ch = '+'; + const size_t bytes_written = Write (&ch, 1, status, NULL); + if (log) + log->Printf ("<%4zu> send packet: %c", bytes_written, ch); + m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written); + return bytes_written; +} + +size_t +GDBRemoteCommunication::SendNack () +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + ConnectionStatus status = eConnectionStatusSuccess; + char ch = '-'; + const size_t bytes_written = Write (&ch, 1, status, NULL); + if (log) + log->Printf ("<%4zu> send packet: %c", bytes_written, ch); + m_history.AddPacket (ch, History::ePacketTypeSend, bytes_written); + return bytes_written; +} + +size_t +GDBRemoteCommunication::SendPacket (const char *payload, size_t payload_length) +{ + Mutex::Locker locker(m_sequence_mutex); + return SendPacketNoLock (payload, payload_length); +} + +size_t +GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_length) +{ + if (IsConnected()) + { + StreamString packet(0, 4, eByteOrderBig); + + packet.PutChar('$'); + packet.Write (payload, payload_length); + packet.PutChar('#'); + packet.PutHex8(CalculcateChecksum (payload, payload_length)); + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + ConnectionStatus status = eConnectionStatusSuccess; + size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL); + if (log) + { + // If logging was just enabled and we have history, then dump out what + // we have to the log so we get the historical context. The Dump() call that + // logs all of the packet will set a boolean so that we don't dump this more + // than once + if (!m_history.DidDumpToLog ()) + m_history.Dump (log); + + log->Printf ("<%4zu> send packet: %.*s", bytes_written, (int)packet.GetSize(), packet.GetData()); + } + + m_history.AddPacket (packet.GetString(), packet.GetSize(), History::ePacketTypeSend, bytes_written); + + + if (bytes_written == packet.GetSize()) + { + if (GetSendAcks ()) + { + if (GetAck () != '+') + { + if (log) + log->Printf("get ack failed..."); + return 0; + } + } + } + else + { + if (log) + log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData()); + } + return bytes_written; + } + return 0; +} + +char +GDBRemoteCommunication::GetAck () +{ + StringExtractorGDBRemote packet; + if (WaitForPacketWithTimeoutMicroSecondsNoLock (packet, GetPacketTimeoutInMicroSeconds ()) == 1) + return packet.GetChar(); + return 0; +} + +bool +GDBRemoteCommunication::GetSequenceMutex (Mutex::Locker& locker, const char *failure_message) +{ + if (IsRunning()) + return locker.TryLock (m_sequence_mutex, failure_message); + + locker.Lock (m_sequence_mutex); + return true; +} + + +bool +GDBRemoteCommunication::WaitForNotRunningPrivate (const TimeValue *timeout_ptr) +{ + return m_private_is_running.WaitForValueEqualTo (false, timeout_ptr, NULL); +} + +size_t +GDBRemoteCommunication::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &packet, uint32_t timeout_usec) +{ + uint8_t buffer[8192]; + Error error; + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS | GDBR_LOG_VERBOSE)); + + // Check for a packet from our cache first without trying any reading... + if (CheckForPacket (NULL, 0, packet)) + return packet.GetStringRef().size(); + + bool timed_out = false; + while (IsConnected() && !timed_out) + { + lldb::ConnectionStatus status = eConnectionStatusNoConnection; + size_t bytes_read = Read (buffer, sizeof(buffer), timeout_usec, status, &error); + + if (log) + log->Printf ("%s: Read (buffer, (sizeof(buffer), timeout_usec = 0x%x, status = %s, error = %s) => bytes_read = %" PRIu64, + __PRETTY_FUNCTION__, + timeout_usec, + Communication::ConnectionStatusAsCString (status), + error.AsCString(), + (uint64_t)bytes_read); + + if (bytes_read > 0) + { + if (CheckForPacket (buffer, bytes_read, packet)) + return packet.GetStringRef().size(); + } + else + { + switch (status) + { + case eConnectionStatusTimedOut: + timed_out = true; + break; + case eConnectionStatusSuccess: + //printf ("status = success but error = %s\n", error.AsCString("<invalid>")); + break; + + case eConnectionStatusEndOfFile: + case eConnectionStatusNoConnection: + case eConnectionStatusLostConnection: + case eConnectionStatusError: + Disconnect(); + break; + } + } + } + packet.Clear (); + return 0; +} + +bool +GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet) +{ + // Put the packet data into the buffer in a thread safe fashion + Mutex::Locker locker(m_bytes_mutex); + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + + if (src && src_len > 0) + { + if (log && log->GetVerbose()) + { + StreamString s; + log->Printf ("GDBRemoteCommunication::%s adding %u bytes: %.*s", + __FUNCTION__, + (uint32_t)src_len, + (uint32_t)src_len, + src); + } + m_bytes.append ((const char *)src, src_len); + } + + // Parse up the packets into gdb remote packets + if (!m_bytes.empty()) + { + // end_idx must be one past the last valid packet byte. Start + // it off with an invalid value that is the same as the current + // index. + size_t content_start = 0; + size_t content_length = 0; + size_t total_length = 0; + size_t checksum_idx = std::string::npos; + + switch (m_bytes[0]) + { + case '+': // Look for ack + case '-': // Look for cancel + case '\x03': // ^C to halt target + content_length = total_length = 1; // The command is one byte long... + break; + + case '$': + // Look for a standard gdb packet? + { + size_t hash_pos = m_bytes.find('#'); + if (hash_pos != std::string::npos) + { + if (hash_pos + 2 < m_bytes.size()) + { + checksum_idx = hash_pos + 1; + // Skip the dollar sign + content_start = 1; + // Don't include the # in the content or the $ in the content length + content_length = hash_pos - 1; + + total_length = hash_pos + 3; // Skip the # and the two hex checksum bytes + } + else + { + // Checksum bytes aren't all here yet + content_length = std::string::npos; + } + } + } + break; + + default: + { + // We have an unexpected byte and we need to flush all bad + // data that is in m_bytes, so we need to find the first + // byte that is a '+' (ACK), '-' (NACK), \x03 (CTRL+C interrupt), + // or '$' character (start of packet header) or of course, + // the end of the data in m_bytes... + const size_t bytes_len = m_bytes.size(); + bool done = false; + uint32_t idx; + for (idx = 1; !done && idx < bytes_len; ++idx) + { + switch (m_bytes[idx]) + { + case '+': + case '-': + case '\x03': + case '$': + done = true; + break; + + default: + break; + } + } + if (log) + log->Printf ("GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'", + __FUNCTION__, idx, idx, m_bytes.c_str()); + m_bytes.erase(0, idx); + } + break; + } + + if (content_length == std::string::npos) + { + packet.Clear(); + return false; + } + else if (total_length > 0) + { + + // We have a valid packet... + assert (content_length <= m_bytes.size()); + assert (total_length <= m_bytes.size()); + assert (content_length <= total_length); + + bool success = true; + std::string &packet_str = packet.GetStringRef(); + + + if (log) + { + // If logging was just enabled and we have history, then dump out what + // we have to the log so we get the historical context. The Dump() call that + // logs all of the packet will set a boolean so that we don't dump this more + // than once + if (!m_history.DidDumpToLog ()) + m_history.Dump (log); + + log->Printf ("<%4zu> read packet: %.*s", total_length, (int)(total_length), m_bytes.c_str()); + } + + m_history.AddPacket (m_bytes.c_str(), total_length, History::ePacketTypeRecv, total_length); + + packet_str.assign (m_bytes, content_start, content_length); + + if (m_bytes[0] == '$') + { + assert (checksum_idx < m_bytes.size()); + if (::isxdigit (m_bytes[checksum_idx+0]) || + ::isxdigit (m_bytes[checksum_idx+1])) + { + if (GetSendAcks ()) + { + const char *packet_checksum_cstr = &m_bytes[checksum_idx]; + char packet_checksum = strtol (packet_checksum_cstr, NULL, 16); + char actual_checksum = CalculcateChecksum (packet_str.c_str(), packet_str.size()); + success = packet_checksum == actual_checksum; + if (!success) + { + if (log) + log->Printf ("error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x", + (int)(total_length), + m_bytes.c_str(), + (uint8_t)packet_checksum, + (uint8_t)actual_checksum); + } + // Send the ack or nack if needed + if (!success) + SendNack(); + else + SendAck(); + } + } + else + { + success = false; + if (log) + log->Printf ("error: invalid checksum in packet: '%s'\n", m_bytes.c_str()); + } + } + + m_bytes.erase(0, total_length); + packet.SetFilePos(0); + return success; + } + } + packet.Clear(); + return false; +} + +Error +GDBRemoteCommunication::StartDebugserverProcess (const char *debugserver_url, + const char *unix_socket_name, // For handshaking + lldb_private::ProcessLaunchInfo &launch_info) +{ + Error error; + // If we locate debugserver, keep that located version around + static FileSpec g_debugserver_file_spec; + + // This function will fill in the launch information for the debugserver + // instance that gets launched. + launch_info.Clear(); + + char debugserver_path[PATH_MAX]; + FileSpec &debugserver_file_spec = launch_info.GetExecutableFile(); + + // Always check to see if we have an environment override for the path + // to the debugserver to use and use it if we do. + const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); + if (env_debugserver_path) + debugserver_file_spec.SetFile (env_debugserver_path, false); + else + debugserver_file_spec = g_debugserver_file_spec; + bool debugserver_exists = debugserver_file_spec.Exists(); + if (!debugserver_exists) + { + // The debugserver binary is in the LLDB.framework/Resources + // directory. + if (Host::GetLLDBPath (ePathTypeSupportExecutableDir, debugserver_file_spec)) + { + debugserver_file_spec.GetFilename().SetCString(DEBUGSERVER_BASENAME); + debugserver_exists = debugserver_file_spec.Exists(); + if (debugserver_exists) + { + g_debugserver_file_spec = debugserver_file_spec; + } + else + { + g_debugserver_file_spec.Clear(); + debugserver_file_spec.Clear(); + } + } + } + + if (debugserver_exists) + { + debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path)); + + Args &debugserver_args = launch_info.GetArguments(); + debugserver_args.Clear(); + char arg_cstr[PATH_MAX]; + + // Start args with "debugserver /file/path -r --" + debugserver_args.AppendArgument(debugserver_path); + debugserver_args.AppendArgument(debugserver_url); + // use native registers, not the GDB registers + debugserver_args.AppendArgument("--native-regs"); + // make debugserver run in its own session so signals generated by + // special terminal key sequences (^C) don't affect debugserver + debugserver_args.AppendArgument("--setsid"); + + if (unix_socket_name && unix_socket_name[0]) + { + debugserver_args.AppendArgument("--unix-socket"); + debugserver_args.AppendArgument(unix_socket_name); + } + + const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); + if (env_debugserver_log_file) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file); + debugserver_args.AppendArgument(arg_cstr); + } + + const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); + if (env_debugserver_log_flags) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); + debugserver_args.AppendArgument(arg_cstr); + } + // debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt"); + // debugserver_args.AppendArgument("--log-flags=0x802e0e"); + + // We currently send down all arguments, attach pids, or attach + // process names in dedicated GDB server packets, so we don't need + // to pass them as arguments. This is currently because of all the + // things we need to setup prior to launching: the environment, + // current working dir, file actions, etc. +#if 0 + // Now append the program arguments + if (inferior_argv) + { + // Terminate the debugserver args so we can now append the inferior args + debugserver_args.AppendArgument("--"); + + for (int i = 0; inferior_argv[i] != NULL; ++i) + debugserver_args.AppendArgument (inferior_argv[i]); + } + else if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid); + debugserver_args.AppendArgument (arg_cstr); + } + else if (attach_name && attach_name[0]) + { + if (wait_for_launch) + debugserver_args.AppendArgument ("--waitfor"); + else + debugserver_args.AppendArgument ("--attach"); + debugserver_args.AppendArgument (attach_name); + } +#endif + + // Close STDIN, STDOUT and STDERR. We might need to redirect them + // to "/dev/null" if we run into any problems. +// launch_info.AppendCloseFileAction (STDIN_FILENO); +// launch_info.AppendCloseFileAction (STDOUT_FILENO); +// launch_info.AppendCloseFileAction (STDERR_FILENO); + + error = Host::LaunchProcess(launch_info); + } + else + { + error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME ); + } + return error; +} + +void +GDBRemoteCommunication::DumpHistory(Stream &strm) +{ + m_history.Dump (strm); +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h new file mode 100644 index 000000000000..a1077957c6a6 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -0,0 +1,268 @@ +//===-- GDBRemoteCommunication.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunication_h_ +#define liblldb_GDBRemoteCommunication_h_ + +// C Includes +// C++ Includes +#include <list> +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Listener.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/TimeValue.h" + +#include "Utility/StringExtractorGDBRemote.h" + +class ProcessGDBRemote; + +class GDBRemoteCommunication : public lldb_private::Communication +{ +public: + enum + { + eBroadcastBitRunPacketSent = kLoUserBroadcastBit + }; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteCommunication(const char *comm_name, + const char *listener_name, + bool is_platform); + + virtual + ~GDBRemoteCommunication(); + + char + GetAck (); + + size_t + SendAck (); + + size_t + SendNack (); + + char + CalculcateChecksum (const char *payload, + size_t payload_length); + + bool + GetSequenceMutex (lldb_private::Mutex::Locker& locker, const char *failure_message = NULL); + + bool + CheckForPacket (const uint8_t *src, + size_t src_len, + StringExtractorGDBRemote &packet); + bool + IsRunning() const + { + return m_public_is_running.GetValue(); + } + + bool + GetSendAcks () + { + return m_send_acks; + } + + //------------------------------------------------------------------ + // Client and server must implement these pure virtual functions + //------------------------------------------------------------------ + virtual bool + GetThreadSuffixSupported () = 0; + + //------------------------------------------------------------------ + // Set the global packet timeout. + // + // For clients, this is the timeout that gets used when sending + // packets and waiting for responses. For servers, this might not + // get used, and if it doesn't this should be moved to the + // GDBRemoteCommunicationClient. + //------------------------------------------------------------------ + uint32_t + SetPacketTimeout (uint32_t packet_timeout) + { + const uint32_t old_packet_timeout = m_packet_timeout; + m_packet_timeout = packet_timeout; + return old_packet_timeout; + } + + uint32_t + GetPacketTimeoutInMicroSeconds () const + { + return m_packet_timeout * lldb_private::TimeValue::MicroSecPerSec; + } + //------------------------------------------------------------------ + // Start a debugserver instance on the current host using the + // supplied connection URL. + //------------------------------------------------------------------ + lldb_private::Error + StartDebugserverProcess (const char *connect_url, + const char *unix_socket_name, + lldb_private::ProcessLaunchInfo &launch_info); + + void + DumpHistory(lldb_private::Stream &strm); + +protected: + + class History + { + public: + enum PacketType + { + ePacketTypeInvalid = 0, + ePacketTypeSend, + ePacketTypeRecv + }; + + struct Entry + { + Entry() : + packet(), + type (ePacketTypeInvalid), + bytes_transmitted (0), + packet_idx (0), + tid (LLDB_INVALID_THREAD_ID) + { + } + + void + Clear () + { + packet.clear(); + type = ePacketTypeInvalid; + bytes_transmitted = 0; + packet_idx = 0; + tid = LLDB_INVALID_THREAD_ID; + } + std::string packet; + PacketType type; + uint32_t bytes_transmitted; + uint32_t packet_idx; + lldb::tid_t tid; + }; + + History (uint32_t size); + + ~History (); + + // For single char packets for ack, nack and /x03 + void + AddPacket (char packet_char, + PacketType type, + uint32_t bytes_transmitted); + void + AddPacket (const std::string &src, + uint32_t src_len, + PacketType type, + uint32_t bytes_transmitted); + + void + Dump (lldb_private::Stream &strm) const; + + void + Dump (lldb_private::Log *log) const; + + bool + DidDumpToLog () const + { + return m_dumped_to_log; + } + +protected: + uint32_t + GetFirstSavedPacketIndex () const + { + if (m_total_packet_count < m_packets.size()) + return 0; + else + return m_curr_idx + 1; + } + + uint32_t + GetNumPacketsInHistory () const + { + if (m_total_packet_count < m_packets.size()) + return m_total_packet_count; + else + return (uint32_t)m_packets.size(); + } + + uint32_t + GetNextIndex() + { + ++m_total_packet_count; + const uint32_t idx = m_curr_idx; + m_curr_idx = NormalizeIndex(idx + 1); + return idx; + } + + uint32_t + NormalizeIndex (uint32_t i) const + { + return i % m_packets.size(); + } + + + std::vector<Entry> m_packets; + uint32_t m_curr_idx; + uint32_t m_total_packet_count; + mutable bool m_dumped_to_log; + }; + + size_t + SendPacket (const char *payload, + size_t payload_length); + + size_t + SendPacketNoLock (const char *payload, + size_t payload_length); + + size_t + WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractorGDBRemote &response, + uint32_t timeout_usec); + + bool + WaitForNotRunningPrivate (const lldb_private::TimeValue *timeout_ptr); + + //------------------------------------------------------------------ + // Classes that inherit from GDBRemoteCommunication can see and modify these + //------------------------------------------------------------------ + uint32_t m_packet_timeout; +#ifdef LLDB_CONFIGURATION_DEBUG + lldb_private::TrackingMutex m_sequence_mutex; +#else + lldb_private::Mutex m_sequence_mutex; // Restrict access to sending/receiving packets to a single thread at a time +#endif + lldb_private::Predicate<bool> m_public_is_running; + lldb_private::Predicate<bool> m_private_is_running; + History m_history; + bool m_send_acks; + bool m_is_platform; // Set to true if this class represents a platform, + // false if this class represents a debug session for + // a single process + + + + +private: + //------------------------------------------------------------------ + // For GDBRemoteCommunication only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunication); +}; + +#endif // liblldb_GDBRemoteCommunication_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp new file mode 100644 index 000000000000..ca594a8f3fd5 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -0,0 +1,2348 @@ +//===-- GDBRemoteCommunicationClient.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "GDBRemoteCommunicationClient.h" + +// C Includes +// C++ Includes +#include <sstream> + +// Other libraries and framework includes +#include "llvm/ADT/Triple.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" + +// Project includes +#include "Utility/StringExtractorGDBRemote.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// GDBRemoteCommunicationClient constructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : + GDBRemoteCommunication("gdb-remote.client", "gdb-remote.client.rx_packet", is_platform), + m_supports_not_sending_acks (eLazyBoolCalculate), + m_supports_thread_suffix (eLazyBoolCalculate), + m_supports_threads_in_stop_reply (eLazyBoolCalculate), + m_supports_vCont_all (eLazyBoolCalculate), + m_supports_vCont_any (eLazyBoolCalculate), + m_supports_vCont_c (eLazyBoolCalculate), + m_supports_vCont_C (eLazyBoolCalculate), + m_supports_vCont_s (eLazyBoolCalculate), + m_supports_vCont_S (eLazyBoolCalculate), + m_qHostInfo_is_valid (eLazyBoolCalculate), + m_qProcessInfo_is_valid (eLazyBoolCalculate), + m_supports_alloc_dealloc_memory (eLazyBoolCalculate), + m_supports_memory_region_info (eLazyBoolCalculate), + m_supports_watchpoint_support_info (eLazyBoolCalculate), + m_supports_detach_stay_stopped (eLazyBoolCalculate), + m_watchpoints_trigger_after_instruction(eLazyBoolCalculate), + m_attach_or_wait_reply(eLazyBoolCalculate), + m_prepare_for_reg_writing_reply (eLazyBoolCalculate), + m_supports_qProcessInfoPID (true), + m_supports_qfProcessInfo (true), + m_supports_qUserName (true), + m_supports_qGroupName (true), + m_supports_qThreadStopInfo (true), + m_supports_z0 (true), + m_supports_z1 (true), + m_supports_z2 (true), + m_supports_z3 (true), + m_supports_z4 (true), + m_curr_tid (LLDB_INVALID_THREAD_ID), + m_curr_tid_run (LLDB_INVALID_THREAD_ID), + m_num_supported_hardware_watchpoints (0), + m_async_mutex (Mutex::eMutexTypeRecursive), + m_async_packet_predicate (false), + m_async_packet (), + m_async_response (), + m_async_signal (-1), + m_thread_id_to_used_usec_map (), + m_host_arch(), + m_process_arch(), + m_os_version_major (UINT32_MAX), + m_os_version_minor (UINT32_MAX), + m_os_version_update (UINT32_MAX) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationClient::~GDBRemoteCommunicationClient() +{ + if (IsConnected()) + Disconnect(); +} + +bool +GDBRemoteCommunicationClient::HandshakeWithServer (Error *error_ptr) +{ + // Start the read thread after we send the handshake ack since if we + // fail to send the handshake ack, there is no reason to continue... + if (SendAck()) + return true; + + if (error_ptr) + error_ptr->SetErrorString("failed to send the handshake ack"); + return false; +} + +void +GDBRemoteCommunicationClient::QueryNoAckModeSupported () +{ + if (m_supports_not_sending_acks == eLazyBoolCalculate) + { + m_send_acks = true; + m_supports_not_sending_acks = eLazyBoolNo; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("QStartNoAckMode", response, false)) + { + if (response.IsOKResponse()) + { + m_send_acks = false; + m_supports_not_sending_acks = eLazyBoolYes; + } + } + } +} + +void +GDBRemoteCommunicationClient::GetListThreadsInStopReplySupported () +{ + if (m_supports_threads_in_stop_reply == eLazyBoolCalculate) + { + m_supports_threads_in_stop_reply = eLazyBoolNo; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("QListThreadsInStopReply", response, false)) + { + if (response.IsOKResponse()) + m_supports_threads_in_stop_reply = eLazyBoolYes; + } + } +} + +bool +GDBRemoteCommunicationClient::GetVAttachOrWaitSupported () +{ + if (m_attach_or_wait_reply == eLazyBoolCalculate) + { + m_attach_or_wait_reply = eLazyBoolNo; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qVAttachOrWaitSupported", response, false)) + { + if (response.IsOKResponse()) + m_attach_or_wait_reply = eLazyBoolYes; + } + } + if (m_attach_or_wait_reply == eLazyBoolYes) + return true; + else + return false; +} + +bool +GDBRemoteCommunicationClient::GetSyncThreadStateSupported () +{ + if (m_prepare_for_reg_writing_reply == eLazyBoolCalculate) + { + m_prepare_for_reg_writing_reply = eLazyBoolNo; + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qSyncThreadStateSupported", response, false)) + { + if (response.IsOKResponse()) + m_prepare_for_reg_writing_reply = eLazyBoolYes; + } + } + if (m_prepare_for_reg_writing_reply == eLazyBoolYes) + return true; + else + return false; +} + + +void +GDBRemoteCommunicationClient::ResetDiscoverableSettings() +{ + m_supports_not_sending_acks = eLazyBoolCalculate; + m_supports_thread_suffix = eLazyBoolCalculate; + m_supports_threads_in_stop_reply = eLazyBoolCalculate; + m_supports_vCont_c = eLazyBoolCalculate; + m_supports_vCont_C = eLazyBoolCalculate; + m_supports_vCont_s = eLazyBoolCalculate; + m_supports_vCont_S = eLazyBoolCalculate; + m_qHostInfo_is_valid = eLazyBoolCalculate; + m_qProcessInfo_is_valid = eLazyBoolCalculate; + m_supports_alloc_dealloc_memory = eLazyBoolCalculate; + m_supports_memory_region_info = eLazyBoolCalculate; + m_prepare_for_reg_writing_reply = eLazyBoolCalculate; + m_attach_or_wait_reply = eLazyBoolCalculate; + + m_supports_qProcessInfoPID = true; + m_supports_qfProcessInfo = true; + m_supports_qUserName = true; + m_supports_qGroupName = true; + m_supports_qThreadStopInfo = true; + m_supports_z0 = true; + m_supports_z1 = true; + m_supports_z2 = true; + m_supports_z3 = true; + m_supports_z4 = true; + m_host_arch.Clear(); + m_process_arch.Clear(); +} + + +bool +GDBRemoteCommunicationClient::GetThreadSuffixSupported () +{ + if (m_supports_thread_suffix == eLazyBoolCalculate) + { + StringExtractorGDBRemote response; + m_supports_thread_suffix = eLazyBoolNo; + if (SendPacketAndWaitForResponse("QThreadSuffixSupported", response, false)) + { + if (response.IsOKResponse()) + m_supports_thread_suffix = eLazyBoolYes; + } + } + return m_supports_thread_suffix; +} +bool +GDBRemoteCommunicationClient::GetVContSupported (char flavor) +{ + if (m_supports_vCont_c == eLazyBoolCalculate) + { + StringExtractorGDBRemote response; + m_supports_vCont_any = eLazyBoolNo; + m_supports_vCont_all = eLazyBoolNo; + m_supports_vCont_c = eLazyBoolNo; + m_supports_vCont_C = eLazyBoolNo; + m_supports_vCont_s = eLazyBoolNo; + m_supports_vCont_S = eLazyBoolNo; + if (SendPacketAndWaitForResponse("vCont?", response, false)) + { + const char *response_cstr = response.GetStringRef().c_str(); + if (::strstr (response_cstr, ";c")) + m_supports_vCont_c = eLazyBoolYes; + + if (::strstr (response_cstr, ";C")) + m_supports_vCont_C = eLazyBoolYes; + + if (::strstr (response_cstr, ";s")) + m_supports_vCont_s = eLazyBoolYes; + + if (::strstr (response_cstr, ";S")) + m_supports_vCont_S = eLazyBoolYes; + + if (m_supports_vCont_c == eLazyBoolYes && + m_supports_vCont_C == eLazyBoolYes && + m_supports_vCont_s == eLazyBoolYes && + m_supports_vCont_S == eLazyBoolYes) + { + m_supports_vCont_all = eLazyBoolYes; + } + + if (m_supports_vCont_c == eLazyBoolYes || + m_supports_vCont_C == eLazyBoolYes || + m_supports_vCont_s == eLazyBoolYes || + m_supports_vCont_S == eLazyBoolYes) + { + m_supports_vCont_any = eLazyBoolYes; + } + } + } + + switch (flavor) + { + case 'a': return m_supports_vCont_any; + case 'A': return m_supports_vCont_all; + case 'c': return m_supports_vCont_c; + case 'C': return m_supports_vCont_C; + case 's': return m_supports_vCont_s; + case 'S': return m_supports_vCont_S; + default: break; + } + return false; +} + + +size_t +GDBRemoteCommunicationClient::SendPacketAndWaitForResponse +( + const char *payload, + StringExtractorGDBRemote &response, + bool send_async +) +{ + return SendPacketAndWaitForResponse (payload, + ::strlen (payload), + response, + send_async); +} + +size_t +GDBRemoteCommunicationClient::SendPacketAndWaitForResponse +( + const char *payload, + size_t payload_length, + StringExtractorGDBRemote &response, + bool send_async +) +{ + Mutex::Locker locker; + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + size_t response_len = 0; + if (GetSequenceMutex (locker)) + { + if (SendPacketNoLock (payload, payload_length)) + response_len = WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ()); + else + { + if (log) + log->Printf("error: failed to send '%*s'", (int) payload_length, payload); + } + } + else + { + if (send_async) + { + if (IsRunning()) + { + Mutex::Locker async_locker (m_async_mutex); + m_async_packet.assign(payload, payload_length); + m_async_packet_predicate.SetValue (true, eBroadcastNever); + + if (log) + log->Printf ("async: async packet = %s", m_async_packet.c_str()); + + bool timed_out = false; + if (SendInterrupt(locker, 2, timed_out)) + { + if (m_interrupt_sent) + { + m_interrupt_sent = false; + TimeValue timeout_time; + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds (m_packet_timeout); + + if (log) + log->Printf ("async: sent interrupt"); + + if (m_async_packet_predicate.WaitForValueEqualTo (false, &timeout_time, &timed_out)) + { + if (log) + log->Printf ("async: got response"); + + // Swap the response buffer to avoid malloc and string copy + response.GetStringRef().swap (m_async_response.GetStringRef()); + response_len = response.GetStringRef().size(); + } + else + { + if (log) + log->Printf ("async: timed out waiting for response"); + } + + // Make sure we wait until the continue packet has been sent again... + if (m_private_is_running.WaitForValueEqualTo (true, &timeout_time, &timed_out)) + { + if (log) + { + if (timed_out) + log->Printf ("async: timed out waiting for process to resume, but process was resumed"); + else + log->Printf ("async: async packet sent"); + } + } + else + { + if (log) + log->Printf ("async: timed out waiting for process to resume"); + } + } + else + { + // We had a racy condition where we went to send the interrupt + // yet we were able to get the lock, so the process must have + // just stopped? + if (log) + log->Printf ("async: got lock without sending interrupt"); + // Send the packet normally since we got the lock + if (SendPacketNoLock (payload, payload_length)) + response_len = WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ()); + else + { + if (log) + log->Printf("error: failed to send '%*s'", (int) payload_length, payload); + } + } + } + else + { + if (log) + log->Printf ("async: failed to interrupt"); + } + } + else + { + if (log) + log->Printf ("async: not running, async is ignored"); + } + } + else + { + if (log) + log->Printf("error: failed to get packet sequence mutex, not sending packet '%*s'", (int) payload_length, payload); + } + } + if (response_len == 0) + { + if (log) + log->Printf("error: failed to get response for '%*s'", (int) payload_length, payload); + } + return response_len; +} + +static const char *end_delimiter = "--end--;"; +static const int end_delimiter_len = 8; + +std::string +GDBRemoteCommunicationClient::HarmonizeThreadIdsForProfileData +( ProcessGDBRemote *process, + StringExtractorGDBRemote& profileDataExtractor +) +{ + std::map<uint64_t, uint32_t> new_thread_id_to_used_usec_map; + std::stringstream final_output; + std::string name, value; + + // Going to assuming thread_used_usec comes first, else bail out. + while (profileDataExtractor.GetNameColonValue(name, value)) + { + if (name.compare("thread_used_id") == 0) + { + StringExtractor threadIDHexExtractor(value.c_str()); + uint64_t thread_id = threadIDHexExtractor.GetHexMaxU64(false, 0); + + bool has_used_usec = false; + uint32_t curr_used_usec = 0; + std::string usec_name, usec_value; + uint32_t input_file_pos = profileDataExtractor.GetFilePos(); + if (profileDataExtractor.GetNameColonValue(usec_name, usec_value)) + { + if (usec_name.compare("thread_used_usec") == 0) + { + has_used_usec = true; + curr_used_usec = strtoull(usec_value.c_str(), NULL, 0); + } + else + { + // We didn't find what we want, it is probably + // an older version. Bail out. + profileDataExtractor.SetFilePos(input_file_pos); + } + } + + if (has_used_usec) + { + uint32_t prev_used_usec = 0; + std::map<uint64_t, uint32_t>::iterator iterator = m_thread_id_to_used_usec_map.find(thread_id); + if (iterator != m_thread_id_to_used_usec_map.end()) + { + prev_used_usec = m_thread_id_to_used_usec_map[thread_id]; + } + + uint32_t real_used_usec = curr_used_usec - prev_used_usec; + // A good first time record is one that runs for at least 0.25 sec + bool good_first_time = (prev_used_usec == 0) && (real_used_usec > 250000); + bool good_subsequent_time = (prev_used_usec > 0) && + ((real_used_usec > 0) || (process->HasAssignedIndexIDToThread(thread_id))); + + if (good_first_time || good_subsequent_time) + { + // We try to avoid doing too many index id reservation, + // resulting in fast increase of index ids. + + final_output << name << ":"; + int32_t index_id = process->AssignIndexIDToThread(thread_id); + final_output << index_id << ";"; + + final_output << usec_name << ":" << usec_value << ";"; + } + else + { + // Skip past 'thread_used_name'. + std::string local_name, local_value; + profileDataExtractor.GetNameColonValue(local_name, local_value); + } + + // Store current time as previous time so that they can be compared later. + new_thread_id_to_used_usec_map[thread_id] = curr_used_usec; + } + else + { + // Bail out and use old string. + final_output << name << ":" << value << ";"; + } + } + else + { + final_output << name << ":" << value << ";"; + } + } + final_output << end_delimiter; + m_thread_id_to_used_usec_map = new_thread_id_to_used_usec_map; + + return final_output.str(); +} + +StateType +GDBRemoteCommunicationClient::SendContinuePacketAndWaitForResponse +( + ProcessGDBRemote *process, + const char *payload, + size_t packet_length, + StringExtractorGDBRemote &response +) +{ + m_curr_tid = LLDB_INVALID_THREAD_ID; + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + if (log) + log->Printf ("GDBRemoteCommunicationClient::%s ()", __FUNCTION__); + + Mutex::Locker locker(m_sequence_mutex); + StateType state = eStateRunning; + + BroadcastEvent(eBroadcastBitRunPacketSent, NULL); + m_public_is_running.SetValue (true, eBroadcastNever); + // Set the starting continue packet into "continue_packet". This packet + // may change if we are interrupted and we continue after an async packet... + std::string continue_packet(payload, packet_length); + + bool got_async_packet = false; + + while (state == eStateRunning) + { + if (!got_async_packet) + { + if (log) + log->Printf ("GDBRemoteCommunicationClient::%s () sending continue packet: %s", __FUNCTION__, continue_packet.c_str()); + if (SendPacketNoLock(continue_packet.c_str(), continue_packet.size()) == 0) + state = eStateInvalid; + + m_private_is_running.SetValue (true, eBroadcastAlways); + } + + got_async_packet = false; + + if (log) + log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(%s)", __FUNCTION__, continue_packet.c_str()); + + if (WaitForPacketWithTimeoutMicroSecondsNoLock(response, UINT32_MAX)) + { + if (response.Empty()) + state = eStateInvalid; + else + { + const char stop_type = response.GetChar(); + if (log) + log->Printf ("GDBRemoteCommunicationClient::%s () got packet: %s", __FUNCTION__, response.GetStringRef().c_str()); + switch (stop_type) + { + case 'T': + case 'S': + { + if (process->GetStopID() == 0) + { + if (process->GetID() == LLDB_INVALID_PROCESS_ID) + { + lldb::pid_t pid = GetCurrentProcessID (); + if (pid != LLDB_INVALID_PROCESS_ID) + process->SetID (pid); + } + process->BuildDynamicRegisterInfo (true); + } + + // Privately notify any internal threads that we have stopped + // in case we wanted to interrupt our process, yet we might + // send a packet and continue without returning control to the + // user. + m_private_is_running.SetValue (false, eBroadcastAlways); + + const uint8_t signo = response.GetHexU8 (UINT8_MAX); + + bool continue_after_async = m_async_signal != -1 || m_async_packet_predicate.GetValue(); + if (continue_after_async || m_interrupt_sent) + { + // We sent an interrupt packet to stop the inferior process + // for an async signal or to send an async packet while running + // but we might have been single stepping and received the + // stop packet for the step instead of for the interrupt packet. + // Typically when an interrupt is sent a SIGINT or SIGSTOP + // is used, so if we get anything else, we need to try and + // get another stop reply packet that may have been sent + // due to sending the interrupt when the target is stopped + // which will just re-send a copy of the last stop reply + // packet. If we don't do this, then the reply for our + // async packet will be the repeat stop reply packet and cause + // a lot of trouble for us! + if (signo != SIGINT && signo != SIGSTOP) + { + continue_after_async = false; + + // We didn't get a a SIGINT or SIGSTOP, so try for a + // very brief time (1 ms) to get another stop reply + // packet to make sure it doesn't get in the way + StringExtractorGDBRemote extra_stop_reply_packet; + uint32_t timeout_usec = 1000; + if (WaitForPacketWithTimeoutMicroSecondsNoLock (extra_stop_reply_packet, timeout_usec)) + { + switch (extra_stop_reply_packet.GetChar()) + { + case 'T': + case 'S': + // We did get an extra stop reply, which means + // our interrupt didn't stop the target so we + // shouldn't continue after the async signal + // or packet is sent... + continue_after_async = false; + break; + } + } + } + } + + if (m_async_signal != -1) + { + if (log) + log->Printf ("async: send signo = %s", Host::GetSignalAsCString (m_async_signal)); + + // Save off the async signal we are supposed to send + const int async_signal = m_async_signal; + // Clear the async signal member so we don't end up + // sending the signal multiple times... + m_async_signal = -1; + // Check which signal we stopped with + if (signo == async_signal) + { + if (log) + log->Printf ("async: stopped with signal %s, we are done running", Host::GetSignalAsCString (signo)); + + // We already stopped with a signal that we wanted + // to stop with, so we are done + } + else + { + // We stopped with a different signal that the one + // we wanted to stop with, so now we must resume + // with the signal we want + char signal_packet[32]; + int signal_packet_len = 0; + signal_packet_len = ::snprintf (signal_packet, + sizeof (signal_packet), + "C%2.2x", + async_signal); + + if (log) + log->Printf ("async: stopped with signal %s, resume with %s", + Host::GetSignalAsCString (signo), + Host::GetSignalAsCString (async_signal)); + + // Set the continue packet to resume even if the + // interrupt didn't cause our stop (ignore continue_after_async) + continue_packet.assign(signal_packet, signal_packet_len); + continue; + } + } + else if (m_async_packet_predicate.GetValue()) + { + Log * packet_log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); + + // We are supposed to send an asynchronous packet while + // we are running. + m_async_response.Clear(); + if (m_async_packet.empty()) + { + if (packet_log) + packet_log->Printf ("async: error: empty async packet"); + + } + else + { + if (packet_log) + packet_log->Printf ("async: sending packet"); + + SendPacketAndWaitForResponse (&m_async_packet[0], + m_async_packet.size(), + m_async_response, + false); + } + // Let the other thread that was trying to send the async + // packet know that the packet has been sent and response is + // ready... + m_async_packet_predicate.SetValue(false, eBroadcastAlways); + + if (packet_log) + packet_log->Printf ("async: sent packet, continue_after_async = %i", continue_after_async); + + // Set the continue packet to resume if our interrupt + // for the async packet did cause the stop + if (continue_after_async) + { + // Reverting this for now as it is causing deadlocks + // in programs (<rdar://problem/11529853>). In the future + // we should check our thread list and "do the right thing" + // for new threads that show up while we stop and run async + // packets. Setting the packet to 'c' to continue all threads + // is the right thing to do 99.99% of the time because if a + // thread was single stepping, and we sent an interrupt, we + // will notice above that we didn't stop due to an interrupt + // but stopped due to stepping and we would _not_ continue. + continue_packet.assign (1, 'c'); + continue; + } + } + // Stop with signal and thread info + state = eStateStopped; + } + break; + + case 'W': + case 'X': + // process exited + state = eStateExited; + break; + + case 'O': + // STDOUT + { + got_async_packet = true; + std::string inferior_stdout; + inferior_stdout.reserve(response.GetBytesLeft () / 2); + char ch; + while ((ch = response.GetHexU8()) != '\0') + inferior_stdout.append(1, ch); + process->AppendSTDOUT (inferior_stdout.c_str(), inferior_stdout.size()); + } + break; + + case 'A': + // Async miscellaneous reply. Right now, only profile data is coming through this channel. + { + got_async_packet = true; + std::string input = response.GetStringRef().substr(1); // '1' to move beyond 'A' + if (m_partial_profile_data.length() > 0) + { + m_partial_profile_data.append(input); + input = m_partial_profile_data; + m_partial_profile_data.clear(); + } + + size_t found, pos = 0, len = input.length(); + while ((found = input.find(end_delimiter, pos)) != std::string::npos) + { + StringExtractorGDBRemote profileDataExtractor(input.substr(pos, found).c_str()); + std::string profile_data = HarmonizeThreadIdsForProfileData(process, profileDataExtractor); + process->BroadcastAsyncProfileData (profile_data); + + pos = found + end_delimiter_len; + } + + if (pos < len) + { + // Last incomplete chunk. + m_partial_profile_data = input.substr(pos); + } + } + break; + + case 'E': + // ERROR + state = eStateInvalid; + break; + + default: + if (log) + log->Printf ("GDBRemoteCommunicationClient::%s () unrecognized async packet", __FUNCTION__); + state = eStateInvalid; + break; + } + } + } + else + { + if (log) + log->Printf ("GDBRemoteCommunicationClient::%s () WaitForPacket(...) => false", __FUNCTION__); + state = eStateInvalid; + } + } + if (log) + log->Printf ("GDBRemoteCommunicationClient::%s () => %s", __FUNCTION__, StateAsCString(state)); + response.SetFilePos(0); + m_private_is_running.SetValue (false, eBroadcastAlways); + m_public_is_running.SetValue (false, eBroadcastAlways); + return state; +} + +bool +GDBRemoteCommunicationClient::SendAsyncSignal (int signo) +{ + Mutex::Locker async_locker (m_async_mutex); + m_async_signal = signo; + bool timed_out = false; + Mutex::Locker locker; + if (SendInterrupt (locker, 1, timed_out)) + return true; + m_async_signal = -1; + return false; +} + +// This function takes a mutex locker as a parameter in case the GetSequenceMutex +// actually succeeds. If it doesn't succeed in acquiring the sequence mutex +// (the expected result), then it will send the halt packet. If it does succeed +// then the caller that requested the interrupt will want to keep the sequence +// locked down so that no one else can send packets while the caller has control. +// This function usually gets called when we are running and need to stop the +// target. It can also be used when we are running and and we need to do something +// else (like read/write memory), so we need to interrupt the running process +// (gdb remote protocol requires this), and do what we need to do, then resume. + +bool +GDBRemoteCommunicationClient::SendInterrupt +( + Mutex::Locker& locker, + uint32_t seconds_to_wait_for_stop, + bool &timed_out +) +{ + timed_out = false; + Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); + + if (IsRunning()) + { + // Only send an interrupt if our debugserver is running... + if (GetSequenceMutex (locker)) + { + if (log) + log->Printf ("SendInterrupt () - got sequence mutex without having to interrupt"); + } + else + { + // Someone has the mutex locked waiting for a response or for the + // inferior to stop, so send the interrupt on the down low... + char ctrl_c = '\x03'; + ConnectionStatus status = eConnectionStatusSuccess; + size_t bytes_written = Write (&ctrl_c, 1, status, NULL); + if (log) + log->PutCString("send packet: \\x03"); + if (bytes_written > 0) + { + m_interrupt_sent = true; + if (seconds_to_wait_for_stop) + { + TimeValue timeout; + if (seconds_to_wait_for_stop) + { + timeout = TimeValue::Now(); + timeout.OffsetWithSeconds (seconds_to_wait_for_stop); + } + if (m_private_is_running.WaitForValueEqualTo (false, &timeout, &timed_out)) + { + if (log) + log->PutCString ("SendInterrupt () - sent interrupt, private state stopped"); + return true; + } + else + { + if (log) + log->Printf ("SendInterrupt () - sent interrupt, timed out wating for async thread resume"); + } + } + else + { + if (log) + log->Printf ("SendInterrupt () - sent interrupt, not waiting for stop..."); + return true; + } + } + else + { + if (log) + log->Printf ("SendInterrupt () - failed to write interrupt"); + } + return false; + } + } + else + { + if (log) + log->Printf ("SendInterrupt () - not running"); + } + return true; +} + +lldb::pid_t +GDBRemoteCommunicationClient::GetCurrentProcessID () +{ + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, false)) + { + if (response.GetChar() == 'Q') + if (response.GetChar() == 'C') + return response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID); + } + return LLDB_INVALID_PROCESS_ID; +} + +bool +GDBRemoteCommunicationClient::GetLaunchSuccess (std::string &error_str) +{ + error_str.clear(); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qLaunchSuccess", strlen("qLaunchSuccess"), response, false)) + { + if (response.IsOKResponse()) + return true; + if (response.GetChar() == 'E') + { + // A string the describes what failed when launching... + error_str = response.GetStringRef().substr(1); + } + else + { + error_str.assign ("unknown error occurred launching process"); + } + } + else + { + error_str.assign ("timed out waiting for app to launch"); + } + return false; +} + +int +GDBRemoteCommunicationClient::SendArgumentsPacket (char const *argv[]) +{ + if (argv && argv[0]) + { + StreamString packet; + packet.PutChar('A'); + const char *arg; + for (uint32_t i = 0; (arg = argv[i]) != NULL; ++i) + { + const int arg_len = strlen(arg); + if (i > 0) + packet.PutChar(','); + packet.Printf("%i,%i,", arg_len * 2, i); + packet.PutBytesAsRawHex8 (arg, arg_len); + } + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int +GDBRemoteCommunicationClient::SendEnvironmentPacket (char const *name_equal_value) +{ + if (name_equal_value && name_equal_value[0]) + { + StreamString packet; + packet.Printf("QEnvironment:%s", name_equal_value); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int +GDBRemoteCommunicationClient::SendLaunchArchPacket (char const *arch) +{ + if (arch && arch[0]) + { + StreamString packet; + packet.Printf("QLaunchArch:%s", arch); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +bool +GDBRemoteCommunicationClient::GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update) +{ + if (GetHostInfo ()) + { + if (m_os_version_major != UINT32_MAX) + { + major = m_os_version_major; + minor = m_os_version_minor; + update = m_os_version_update; + return true; + } + } + return false; +} + +bool +GDBRemoteCommunicationClient::GetOSBuildString (std::string &s) +{ + if (GetHostInfo ()) + { + if (!m_os_build.empty()) + { + s = m_os_build; + return true; + } + } + s.clear(); + return false; +} + + +bool +GDBRemoteCommunicationClient::GetOSKernelDescription (std::string &s) +{ + if (GetHostInfo ()) + { + if (!m_os_kernel.empty()) + { + s = m_os_kernel; + return true; + } + } + s.clear(); + return false; +} + +bool +GDBRemoteCommunicationClient::GetHostname (std::string &s) +{ + if (GetHostInfo ()) + { + if (!m_hostname.empty()) + { + s = m_hostname; + return true; + } + } + s.clear(); + return false; +} + +ArchSpec +GDBRemoteCommunicationClient::GetSystemArchitecture () +{ + if (GetHostInfo ()) + return m_host_arch; + return ArchSpec(); +} + +const lldb_private::ArchSpec & +GDBRemoteCommunicationClient::GetProcessArchitecture () +{ + if (m_qProcessInfo_is_valid == eLazyBoolCalculate) + GetCurrentProcessInfo (); + return m_process_arch; +} + + +bool +GDBRemoteCommunicationClient::GetHostInfo (bool force) +{ + if (force || m_qHostInfo_is_valid == eLazyBoolCalculate) + { + m_qHostInfo_is_valid = eLazyBoolNo; + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse ("qHostInfo", response, false)) + { + if (response.IsNormalResponse()) + { + std::string name; + std::string value; + uint32_t cpu = LLDB_INVALID_CPUTYPE; + uint32_t sub = 0; + std::string arch_name; + std::string os_name; + std::string vendor_name; + std::string triple; + uint32_t pointer_byte_size = 0; + StringExtractor extractor; + ByteOrder byte_order = eByteOrderInvalid; + uint32_t num_keys_decoded = 0; + while (response.GetNameColonValue(name, value)) + { + if (name.compare("cputype") == 0) + { + // exception type in big endian hex + cpu = Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 0); + if (cpu != LLDB_INVALID_CPUTYPE) + ++num_keys_decoded; + } + else if (name.compare("cpusubtype") == 0) + { + // exception count in big endian hex + sub = Args::StringToUInt32 (value.c_str(), 0, 0); + if (sub != 0) + ++num_keys_decoded; + } + else if (name.compare("arch") == 0) + { + arch_name.swap (value); + ++num_keys_decoded; + } + else if (name.compare("triple") == 0) + { + // The triple comes as ASCII hex bytes since it contains '-' chars + extractor.GetStringRef().swap(value); + extractor.SetFilePos(0); + extractor.GetHexByteString (triple); + ++num_keys_decoded; + } + else if (name.compare("os_build") == 0) + { + extractor.GetStringRef().swap(value); + extractor.SetFilePos(0); + extractor.GetHexByteString (m_os_build); + ++num_keys_decoded; + } + else if (name.compare("hostname") == 0) + { + extractor.GetStringRef().swap(value); + extractor.SetFilePos(0); + extractor.GetHexByteString (m_hostname); + ++num_keys_decoded; + } + else if (name.compare("os_kernel") == 0) + { + extractor.GetStringRef().swap(value); + extractor.SetFilePos(0); + extractor.GetHexByteString (m_os_kernel); + ++num_keys_decoded; + } + else if (name.compare("ostype") == 0) + { + os_name.swap (value); + ++num_keys_decoded; + } + else if (name.compare("vendor") == 0) + { + vendor_name.swap(value); + ++num_keys_decoded; + } + else if (name.compare("endian") == 0) + { + ++num_keys_decoded; + if (value.compare("little") == 0) + byte_order = eByteOrderLittle; + else if (value.compare("big") == 0) + byte_order = eByteOrderBig; + else if (value.compare("pdp") == 0) + byte_order = eByteOrderPDP; + else + --num_keys_decoded; + } + else if (name.compare("ptrsize") == 0) + { + pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 0); + if (pointer_byte_size != 0) + ++num_keys_decoded; + } + else if (name.compare("os_version") == 0) + { + Args::StringToVersion (value.c_str(), + m_os_version_major, + m_os_version_minor, + m_os_version_update); + if (m_os_version_major != UINT32_MAX) + ++num_keys_decoded; + } + else if (name.compare("watchpoint_exceptions_received") == 0) + { + ++num_keys_decoded; + if (strcmp(value.c_str(),"before") == 0) + m_watchpoints_trigger_after_instruction = eLazyBoolNo; + else if (strcmp(value.c_str(),"after") == 0) + m_watchpoints_trigger_after_instruction = eLazyBoolYes; + else + --num_keys_decoded; + } + + } + + if (num_keys_decoded > 0) + m_qHostInfo_is_valid = eLazyBoolYes; + + if (triple.empty()) + { + if (arch_name.empty()) + { + if (cpu != LLDB_INVALID_CPUTYPE) + { + m_host_arch.SetArchitecture (eArchTypeMachO, cpu, sub); + if (pointer_byte_size) + { + assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) + { + assert (byte_order == m_host_arch.GetByteOrder()); + } + + if (!os_name.empty() && vendor_name.compare("apple") == 0 && os_name.find("darwin") == 0) + { + switch (m_host_arch.GetMachine()) + { + case llvm::Triple::arm: + case llvm::Triple::thumb: + os_name = "ios"; + break; + default: + os_name = "macosx"; + break; + } + } + if (!vendor_name.empty()) + m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); + if (!os_name.empty()) + m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name)); + + } + } + else + { + std::string triple; + triple += arch_name; + if (!vendor_name.empty() || !os_name.empty()) + { + triple += '-'; + if (vendor_name.empty()) + triple += "unknown"; + else + triple += vendor_name; + triple += '-'; + if (os_name.empty()) + triple += "unknown"; + else + triple += os_name; + } + m_host_arch.SetTriple (triple.c_str()); + + llvm::Triple &host_triple = m_host_arch.GetTriple(); + if (host_triple.getVendor() == llvm::Triple::Apple && host_triple.getOS() == llvm::Triple::Darwin) + { + switch (m_host_arch.GetMachine()) + { + case llvm::Triple::arm: + case llvm::Triple::thumb: + host_triple.setOS(llvm::Triple::IOS); + break; + default: + host_triple.setOS(llvm::Triple::MacOSX); + break; + } + } + if (pointer_byte_size) + { + assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) + { + assert (byte_order == m_host_arch.GetByteOrder()); + } + + } + } + else + { + m_host_arch.SetTriple (triple.c_str()); + if (pointer_byte_size) + { + assert (pointer_byte_size == m_host_arch.GetAddressByteSize()); + } + if (byte_order != eByteOrderInvalid) + { + assert (byte_order == m_host_arch.GetByteOrder()); + } + } + } + } + } + return m_qHostInfo_is_valid == eLazyBoolYes; +} + +int +GDBRemoteCommunicationClient::SendAttach +( + lldb::pid_t pid, + StringExtractorGDBRemote& response +) +{ + if (pid != LLDB_INVALID_PROCESS_ID) + { + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, pid); + assert (packet_len < (int)sizeof(packet)); + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + if (response.IsErrorResponse()) + return response.GetError(); + return 0; + } + } + return -1; +} + +const lldb_private::ArchSpec & +GDBRemoteCommunicationClient::GetHostArchitecture () +{ + if (m_qHostInfo_is_valid == eLazyBoolCalculate) + GetHostInfo (); + return m_host_arch; +} + +addr_t +GDBRemoteCommunicationClient::AllocateMemory (size_t size, uint32_t permissions) +{ + if (m_supports_alloc_dealloc_memory != eLazyBoolNo) + { + m_supports_alloc_dealloc_memory = eLazyBoolYes; + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "_M%" PRIx64 ",%s%s%s", + (uint64_t)size, + permissions & lldb::ePermissionsReadable ? "r" : "", + permissions & lldb::ePermissionsWritable ? "w" : "", + permissions & lldb::ePermissionsExecutable ? "x" : ""); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + if (!response.IsErrorResponse()) + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + } + else + { + m_supports_alloc_dealloc_memory = eLazyBoolNo; + } + } + return LLDB_INVALID_ADDRESS; +} + +bool +GDBRemoteCommunicationClient::DeallocateMemory (addr_t addr) +{ + if (m_supports_alloc_dealloc_memory != eLazyBoolNo) + { + m_supports_alloc_dealloc_memory = eLazyBoolYes; + char packet[64]; + const int packet_len = ::snprintf(packet, sizeof(packet), "_m%" PRIx64, (uint64_t)addr); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + if (response.IsOKResponse()) + return true; + } + else + { + m_supports_alloc_dealloc_memory = eLazyBoolNo; + } + } + return false; +} + +Error +GDBRemoteCommunicationClient::Detach (bool keep_stopped) +{ + Error error; + + if (keep_stopped) + { + if (m_supports_detach_stay_stopped == eLazyBoolCalculate) + { + char packet[64]; + const int packet_len = ::snprintf(packet, sizeof(packet), "qSupportsDetachAndStayStopped:"); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + m_supports_detach_stay_stopped = eLazyBoolYes; + } + else + { + m_supports_detach_stay_stopped = eLazyBoolNo; + } + } + + if (m_supports_detach_stay_stopped == eLazyBoolNo) + { + error.SetErrorString("Stays stopped not supported by this target."); + return error; + } + else + { + size_t num_sent = SendPacket ("D1", 2); + if (num_sent == 0) + error.SetErrorString ("Sending extended disconnect packet failed."); + } + } + else + { + size_t num_sent = SendPacket ("D", 1); + if (num_sent == 0) + error.SetErrorString ("Sending disconnect packet failed."); + } + return error; +} + +Error +GDBRemoteCommunicationClient::GetMemoryRegionInfo (lldb::addr_t addr, + lldb_private::MemoryRegionInfo ®ion_info) +{ + Error error; + region_info.Clear(); + + if (m_supports_memory_region_info != eLazyBoolNo) + { + m_supports_memory_region_info = eLazyBoolYes; + char packet[64]; + const int packet_len = ::snprintf(packet, sizeof(packet), "qMemoryRegionInfo:%" PRIx64, (uint64_t)addr); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + std::string name; + std::string value; + addr_t addr_value; + bool success = true; + bool saw_permissions = false; + while (success && response.GetNameColonValue(name, value)) + { + if (name.compare ("start") == 0) + { + addr_value = Args::StringToUInt64(value.c_str(), LLDB_INVALID_ADDRESS, 16, &success); + if (success) + region_info.GetRange().SetRangeBase(addr_value); + } + else if (name.compare ("size") == 0) + { + addr_value = Args::StringToUInt64(value.c_str(), 0, 16, &success); + if (success) + region_info.GetRange().SetByteSize (addr_value); + } + else if (name.compare ("permissions") == 0 && region_info.GetRange().IsValid()) + { + saw_permissions = true; + if (region_info.GetRange().Contains (addr)) + { + if (value.find('r') != std::string::npos) + region_info.SetReadable (MemoryRegionInfo::eYes); + else + region_info.SetReadable (MemoryRegionInfo::eNo); + + if (value.find('w') != std::string::npos) + region_info.SetWritable (MemoryRegionInfo::eYes); + else + region_info.SetWritable (MemoryRegionInfo::eNo); + + if (value.find('x') != std::string::npos) + region_info.SetExecutable (MemoryRegionInfo::eYes); + else + region_info.SetExecutable (MemoryRegionInfo::eNo); + } + else + { + // The reported region does not contain this address -- we're looking at an unmapped page + region_info.SetReadable (MemoryRegionInfo::eNo); + region_info.SetWritable (MemoryRegionInfo::eNo); + region_info.SetExecutable (MemoryRegionInfo::eNo); + } + } + else if (name.compare ("error") == 0) + { + StringExtractorGDBRemote name_extractor; + // Swap "value" over into "name_extractor" + name_extractor.GetStringRef().swap(value); + // Now convert the HEX bytes into a string value + name_extractor.GetHexByteString (value); + error.SetErrorString(value.c_str()); + } + } + + // We got a valid address range back but no permissions -- which means this is an unmapped page + if (region_info.GetRange().IsValid() && saw_permissions == false) + { + region_info.SetReadable (MemoryRegionInfo::eNo); + region_info.SetWritable (MemoryRegionInfo::eNo); + region_info.SetExecutable (MemoryRegionInfo::eNo); + } + } + else + { + m_supports_memory_region_info = eLazyBoolNo; + } + } + + if (m_supports_memory_region_info == eLazyBoolNo) + { + error.SetErrorString("qMemoryRegionInfo is not supported"); + } + if (error.Fail()) + region_info.Clear(); + return error; + +} + +Error +GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num) +{ + Error error; + + if (m_supports_watchpoint_support_info == eLazyBoolYes) + { + num = m_num_supported_hardware_watchpoints; + return error; + } + + // Set num to 0 first. + num = 0; + if (m_supports_watchpoint_support_info != eLazyBoolNo) + { + char packet[64]; + const int packet_len = ::snprintf(packet, sizeof(packet), "qWatchpointSupportInfo:"); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + m_supports_watchpoint_support_info = eLazyBoolYes; + std::string name; + std::string value; + while (response.GetNameColonValue(name, value)) + { + if (name.compare ("num") == 0) + { + num = Args::StringToUInt32(value.c_str(), 0, 0); + m_num_supported_hardware_watchpoints = num; + } + } + } + else + { + m_supports_watchpoint_support_info = eLazyBoolNo; + } + } + + if (m_supports_watchpoint_support_info == eLazyBoolNo) + { + error.SetErrorString("qWatchpointSupportInfo is not supported"); + } + return error; + +} + +lldb_private::Error +GDBRemoteCommunicationClient::GetWatchpointSupportInfo (uint32_t &num, bool& after) +{ + Error error(GetWatchpointSupportInfo(num)); + if (error.Success()) + error = GetWatchpointsTriggerAfterInstruction(after); + return error; +} + +lldb_private::Error +GDBRemoteCommunicationClient::GetWatchpointsTriggerAfterInstruction (bool &after) +{ + Error error; + + // we assume watchpoints will happen after running the relevant opcode + // and we only want to override this behavior if we have explicitly + // received a qHostInfo telling us otherwise + if (m_qHostInfo_is_valid != eLazyBoolYes) + after = true; + else + after = (m_watchpoints_trigger_after_instruction != eLazyBoolNo); + return error; +} + +int +GDBRemoteCommunicationClient::SetSTDIN (char const *path) +{ + if (path && path[0]) + { + StreamString packet; + packet.PutCString("QSetSTDIN:"); + packet.PutBytesAsRawHex8(path, strlen(path)); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int +GDBRemoteCommunicationClient::SetSTDOUT (char const *path) +{ + if (path && path[0]) + { + StreamString packet; + packet.PutCString("QSetSTDOUT:"); + packet.PutBytesAsRawHex8(path, strlen(path)); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int +GDBRemoteCommunicationClient::SetSTDERR (char const *path) +{ + if (path && path[0]) + { + StreamString packet; + packet.PutCString("QSetSTDERR:"); + packet.PutBytesAsRawHex8(path, strlen(path)); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int +GDBRemoteCommunicationClient::SetWorkingDir (char const *path) +{ + if (path && path[0]) + { + StreamString packet; + packet.PutCString("QSetWorkingDir:"); + packet.PutBytesAsRawHex8(path, strlen(path)); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + } + return -1; +} + +int +GDBRemoteCommunicationClient::SetDisableASLR (bool enable) +{ + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof (packet), "QSetDisableASLR:%i", enable ? 1 : 0); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + if (response.IsOKResponse()) + return 0; + uint8_t error = response.GetError(); + if (error) + return error; + } + return -1; +} + +bool +GDBRemoteCommunicationClient::DecodeProcessInfoResponse (StringExtractorGDBRemote &response, ProcessInstanceInfo &process_info) +{ + if (response.IsNormalResponse()) + { + std::string name; + std::string value; + StringExtractor extractor; + + while (response.GetNameColonValue(name, value)) + { + if (name.compare("pid") == 0) + { + process_info.SetProcessID (Args::StringToUInt32 (value.c_str(), LLDB_INVALID_PROCESS_ID, 0)); + } + else if (name.compare("ppid") == 0) + { + process_info.SetParentProcessID (Args::StringToUInt32 (value.c_str(), LLDB_INVALID_PROCESS_ID, 0)); + } + else if (name.compare("uid") == 0) + { + process_info.SetUserID (Args::StringToUInt32 (value.c_str(), UINT32_MAX, 0)); + } + else if (name.compare("euid") == 0) + { + process_info.SetEffectiveUserID (Args::StringToUInt32 (value.c_str(), UINT32_MAX, 0)); + } + else if (name.compare("gid") == 0) + { + process_info.SetGroupID (Args::StringToUInt32 (value.c_str(), UINT32_MAX, 0)); + } + else if (name.compare("egid") == 0) + { + process_info.SetEffectiveGroupID (Args::StringToUInt32 (value.c_str(), UINT32_MAX, 0)); + } + else if (name.compare("triple") == 0) + { + // The triple comes as ASCII hex bytes since it contains '-' chars + extractor.GetStringRef().swap(value); + extractor.SetFilePos(0); + extractor.GetHexByteString (value); + process_info.GetArchitecture ().SetTriple (value.c_str()); + } + else if (name.compare("name") == 0) + { + StringExtractor extractor; + // The process name from ASCII hex bytes since we can't + // control the characters in a process name + extractor.GetStringRef().swap(value); + extractor.SetFilePos(0); + extractor.GetHexByteString (value); + process_info.GetExecutableFile().SetFile (value.c_str(), false); + } + } + + if (process_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + return true; + } + return false; +} + +bool +GDBRemoteCommunicationClient::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.Clear(); + + if (m_supports_qProcessInfoPID) + { + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof (packet), "qProcessInfoPID:%" PRIu64, pid); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + return DecodeProcessInfoResponse (response, process_info); + } + else + { + m_supports_qProcessInfoPID = false; + return false; + } + } + return false; +} + +bool +GDBRemoteCommunicationClient::GetCurrentProcessInfo () +{ + if (m_qProcessInfo_is_valid == eLazyBoolYes) + return true; + if (m_qProcessInfo_is_valid == eLazyBoolNo) + return false; + + GetHostInfo (); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse ("qProcessInfo", response, false)) + { + if (response.IsNormalResponse()) + { + std::string name; + std::string value; + uint32_t cpu = LLDB_INVALID_CPUTYPE; + uint32_t sub = 0; + std::string arch_name; + std::string os_name; + std::string vendor_name; + std::string triple; + uint32_t pointer_byte_size = 0; + StringExtractor extractor; + ByteOrder byte_order = eByteOrderInvalid; + uint32_t num_keys_decoded = 0; + while (response.GetNameColonValue(name, value)) + { + if (name.compare("cputype") == 0) + { + cpu = Args::StringToUInt32 (value.c_str(), LLDB_INVALID_CPUTYPE, 16); + if (cpu != LLDB_INVALID_CPUTYPE) + ++num_keys_decoded; + } + else if (name.compare("cpusubtype") == 0) + { + sub = Args::StringToUInt32 (value.c_str(), 0, 16); + if (sub != 0) + ++num_keys_decoded; + } + else if (name.compare("ostype") == 0) + { + os_name.swap (value); + ++num_keys_decoded; + } + else if (name.compare("vendor") == 0) + { + vendor_name.swap(value); + ++num_keys_decoded; + } + else if (name.compare("endian") == 0) + { + ++num_keys_decoded; + if (value.compare("little") == 0) + byte_order = eByteOrderLittle; + else if (value.compare("big") == 0) + byte_order = eByteOrderBig; + else if (value.compare("pdp") == 0) + byte_order = eByteOrderPDP; + else + --num_keys_decoded; + } + else if (name.compare("ptrsize") == 0) + { + pointer_byte_size = Args::StringToUInt32 (value.c_str(), 0, 16); + if (pointer_byte_size != 0) + ++num_keys_decoded; + } + } + if (num_keys_decoded > 0) + m_qProcessInfo_is_valid = eLazyBoolYes; + if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && !vendor_name.empty()) + { + m_process_arch.SetArchitecture (eArchTypeMachO, cpu, sub); + if (pointer_byte_size) + { + assert (pointer_byte_size == m_process_arch.GetAddressByteSize()); + } + m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); + m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name)); + return true; + } + } + } + else + { + m_qProcessInfo_is_valid = eLazyBoolNo; + } + + return false; +} + + +uint32_t +GDBRemoteCommunicationClient::FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) +{ + process_infos.Clear(); + + if (m_supports_qfProcessInfo) + { + StreamString packet; + packet.PutCString ("qfProcessInfo"); + if (!match_info.MatchAllProcesses()) + { + packet.PutChar (':'); + const char *name = match_info.GetProcessInfo().GetName(); + bool has_name_match = false; + if (name && name[0]) + { + has_name_match = true; + NameMatchType name_match_type = match_info.GetNameMatchType(); + switch (name_match_type) + { + case eNameMatchIgnore: + has_name_match = false; + break; + + case eNameMatchEquals: + packet.PutCString ("name_match:equals;"); + break; + + case eNameMatchContains: + packet.PutCString ("name_match:contains;"); + break; + + case eNameMatchStartsWith: + packet.PutCString ("name_match:starts_with;"); + break; + + case eNameMatchEndsWith: + packet.PutCString ("name_match:ends_with;"); + break; + + case eNameMatchRegularExpression: + packet.PutCString ("name_match:regex;"); + break; + } + if (has_name_match) + { + packet.PutCString ("name:"); + packet.PutBytesAsRawHex8(name, ::strlen(name)); + packet.PutChar (';'); + } + } + + if (match_info.GetProcessInfo().ProcessIDIsValid()) + packet.Printf("pid:%" PRIu64 ";",match_info.GetProcessInfo().GetProcessID()); + if (match_info.GetProcessInfo().ParentProcessIDIsValid()) + packet.Printf("parent_pid:%" PRIu64 ";",match_info.GetProcessInfo().GetParentProcessID()); + if (match_info.GetProcessInfo().UserIDIsValid()) + packet.Printf("uid:%u;",match_info.GetProcessInfo().GetUserID()); + if (match_info.GetProcessInfo().GroupIDIsValid()) + packet.Printf("gid:%u;",match_info.GetProcessInfo().GetGroupID()); + if (match_info.GetProcessInfo().EffectiveUserIDIsValid()) + packet.Printf("euid:%u;",match_info.GetProcessInfo().GetEffectiveUserID()); + if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) + packet.Printf("egid:%u;",match_info.GetProcessInfo().GetEffectiveGroupID()); + if (match_info.GetProcessInfo().EffectiveGroupIDIsValid()) + packet.Printf("all_users:%u;",match_info.GetMatchAllUsers() ? 1 : 0); + if (match_info.GetProcessInfo().GetArchitecture().IsValid()) + { + const ArchSpec &match_arch = match_info.GetProcessInfo().GetArchitecture(); + const llvm::Triple &triple = match_arch.GetTriple(); + packet.PutCString("triple:"); + packet.PutCStringAsRawHex8(triple.getTriple().c_str()); + packet.PutChar (';'); + } + } + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false)) + { + do + { + ProcessInstanceInfo process_info; + if (!DecodeProcessInfoResponse (response, process_info)) + break; + process_infos.Append(process_info); + response.GetStringRef().clear(); + response.SetFilePos(0); + } while (SendPacketAndWaitForResponse ("qsProcessInfo", strlen ("qsProcessInfo"), response, false)); + } + else + { + m_supports_qfProcessInfo = false; + return 0; + } + } + return process_infos.GetSize(); + +} + +bool +GDBRemoteCommunicationClient::GetUserName (uint32_t uid, std::string &name) +{ + if (m_supports_qUserName) + { + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof (packet), "qUserName:%i", uid); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + if (response.IsNormalResponse()) + { + // Make sure we parsed the right number of characters. The response is + // the hex encoded user name and should make up the entire packet. + // If there are any non-hex ASCII bytes, the length won't match below.. + if (response.GetHexByteString (name) * 2 == response.GetStringRef().size()) + return true; + } + } + else + { + m_supports_qUserName = false; + return false; + } + } + return false; + +} + +bool +GDBRemoteCommunicationClient::GetGroupName (uint32_t gid, std::string &name) +{ + if (m_supports_qGroupName) + { + char packet[32]; + const int packet_len = ::snprintf (packet, sizeof (packet), "qGroupName:%i", gid); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse (packet, packet_len, response, false)) + { + if (response.IsNormalResponse()) + { + // Make sure we parsed the right number of characters. The response is + // the hex encoded group name and should make up the entire packet. + // If there are any non-hex ASCII bytes, the length won't match below.. + if (response.GetHexByteString (name) * 2 == response.GetStringRef().size()) + return true; + } + } + else + { + m_supports_qGroupName = false; + return false; + } + } + return false; +} + +void +GDBRemoteCommunicationClient::TestPacketSpeed (const uint32_t num_packets) +{ + uint32_t i; + TimeValue start_time, end_time; + uint64_t total_time_nsec; + float packets_per_second; + if (SendSpeedTestPacket (0, 0)) + { + for (uint32_t send_size = 0; send_size <= 1024; send_size *= 2) + { + for (uint32_t recv_size = 0; recv_size <= 1024; recv_size *= 2) + { + start_time = TimeValue::Now(); + for (i=0; i<num_packets; ++i) + { + SendSpeedTestPacket (send_size, recv_size); + } + end_time = TimeValue::Now(); + total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970(); + packets_per_second = (((float)num_packets)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec; + printf ("%u qSpeedTest(send=%-5u, recv=%-5u) in %" PRIu64 ".%9.9" PRIu64 " sec for %f packets/sec.\n", + num_packets, + send_size, + recv_size, + total_time_nsec / TimeValue::NanoSecPerSec, + total_time_nsec % TimeValue::NanoSecPerSec, + packets_per_second); + if (recv_size == 0) + recv_size = 32; + } + if (send_size == 0) + send_size = 32; + } + } + else + { + start_time = TimeValue::Now(); + for (i=0; i<num_packets; ++i) + { + GetCurrentProcessID (); + } + end_time = TimeValue::Now(); + total_time_nsec = end_time.GetAsNanoSecondsSinceJan1_1970() - start_time.GetAsNanoSecondsSinceJan1_1970(); + packets_per_second = (((float)num_packets)/(float)total_time_nsec) * (float)TimeValue::NanoSecPerSec; + printf ("%u 'qC' packets packets in 0x%" PRIu64 "%9.9" PRIu64 " sec for %f packets/sec.\n", + num_packets, + total_time_nsec / TimeValue::NanoSecPerSec, + total_time_nsec % TimeValue::NanoSecPerSec, + packets_per_second); + } +} + +bool +GDBRemoteCommunicationClient::SendSpeedTestPacket (uint32_t send_size, uint32_t recv_size) +{ + StreamString packet; + packet.Printf ("qSpeedTest:response_size:%i;data:", recv_size); + uint32_t bytes_left = send_size; + while (bytes_left > 0) + { + if (bytes_left >= 26) + { + packet.PutCString("abcdefghijklmnopqrstuvwxyz"); + bytes_left -= 26; + } + else + { + packet.Printf ("%*.*s;", bytes_left, bytes_left, "abcdefghijklmnopqrstuvwxyz"); + bytes_left = 0; + } + } + + StringExtractorGDBRemote response; + return SendPacketAndWaitForResponse (packet.GetData(), packet.GetSize(), response, false) > 0; + return false; +} + +uint16_t +GDBRemoteCommunicationClient::LaunchGDBserverAndGetPort () +{ + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qLaunchGDBServer", strlen("qLaunchGDBServer"), response, false)) + { + std::string name; + std::string value; + uint16_t port = 0; + //lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + while (response.GetNameColonValue(name, value)) + { + if (name.size() == 4 && name.compare("port") == 0) + port = Args::StringToUInt32(value.c_str(), 0, 0); +// if (name.size() == 3 && name.compare("pid") == 0) +// pid = Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0); + } + return port; + } + return 0; +} + +bool +GDBRemoteCommunicationClient::SetCurrentThread (uint64_t tid) +{ + if (m_curr_tid == tid) + return true; + + char packet[32]; + int packet_len; + if (tid == UINT64_MAX) + packet_len = ::snprintf (packet, sizeof(packet), "Hg-1"); + else + packet_len = ::snprintf (packet, sizeof(packet), "Hg%" PRIx64, tid); + assert (packet_len + 1 < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.IsOKResponse()) + { + m_curr_tid = tid; + return true; + } + } + return false; +} + +bool +GDBRemoteCommunicationClient::SetCurrentThreadForRun (uint64_t tid) +{ + if (m_curr_tid_run == tid) + return true; + + char packet[32]; + int packet_len; + if (tid == UINT64_MAX) + packet_len = ::snprintf (packet, sizeof(packet), "Hc-1"); + else + packet_len = ::snprintf (packet, sizeof(packet), "Hc%" PRIx64, tid); + + assert (packet_len + 1 < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.IsOKResponse()) + { + m_curr_tid_run = tid; + return true; + } + } + return false; +} + +bool +GDBRemoteCommunicationClient::GetStopReply (StringExtractorGDBRemote &response) +{ + if (SendPacketAndWaitForResponse("?", 1, response, false)) + return response.IsNormalResponse(); + return false; +} + +bool +GDBRemoteCommunicationClient::GetThreadStopInfo (lldb::tid_t tid, StringExtractorGDBRemote &response) +{ + if (m_supports_qThreadStopInfo) + { + char packet[256]; + int packet_len = ::snprintf(packet, sizeof(packet), "qThreadStopInfo%" PRIx64, tid); + assert (packet_len < (int)sizeof(packet)); + if (SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.IsNormalResponse()) + return true; + else + return false; + } + else + { + m_supports_qThreadStopInfo = false; + } + } +// if (SetCurrentThread (tid)) +// return GetStopReply (response); + return false; +} + + +uint8_t +GDBRemoteCommunicationClient::SendGDBStoppointTypePacket (GDBStoppointType type, bool insert, addr_t addr, uint32_t length) +{ + switch (type) + { + case eBreakpointSoftware: if (!m_supports_z0) return UINT8_MAX; break; + case eBreakpointHardware: if (!m_supports_z1) return UINT8_MAX; break; + case eWatchpointWrite: if (!m_supports_z2) return UINT8_MAX; break; + case eWatchpointRead: if (!m_supports_z3) return UINT8_MAX; break; + case eWatchpointReadWrite: if (!m_supports_z4) return UINT8_MAX; break; + } + + char packet[64]; + const int packet_len = ::snprintf (packet, + sizeof(packet), + "%c%i,%" PRIx64 ",%x", + insert ? 'Z' : 'z', + type, + addr, + length); + + assert (packet_len + 1 < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(packet, packet_len, response, true)) + { + if (response.IsOKResponse()) + return 0; + else if (response.IsErrorResponse()) + return response.GetError(); + } + else + { + switch (type) + { + case eBreakpointSoftware: m_supports_z0 = false; break; + case eBreakpointHardware: m_supports_z1 = false; break; + case eWatchpointWrite: m_supports_z2 = false; break; + case eWatchpointRead: m_supports_z3 = false; break; + case eWatchpointReadWrite: m_supports_z4 = false; break; + } + } + + return UINT8_MAX; +} + +size_t +GDBRemoteCommunicationClient::GetCurrentThreadIDs (std::vector<lldb::tid_t> &thread_ids, + bool &sequence_mutex_unavailable) +{ + Mutex::Locker locker; + thread_ids.clear(); + + if (GetSequenceMutex (locker, "ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex")) + { + sequence_mutex_unavailable = false; + StringExtractorGDBRemote response; + + for (SendPacketNoLock ("qfThreadInfo", strlen("qfThreadInfo")) && WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ()); + response.IsNormalResponse(); + SendPacketNoLock ("qsThreadInfo", strlen("qsThreadInfo")) && WaitForPacketWithTimeoutMicroSecondsNoLock (response, GetPacketTimeoutInMicroSeconds ())) + { + char ch = response.GetChar(); + if (ch == 'l') + break; + if (ch == 'm') + { + do + { + tid_t tid = response.GetHexMaxU64(false, LLDB_INVALID_THREAD_ID); + + if (tid != LLDB_INVALID_THREAD_ID) + { + thread_ids.push_back (tid); + } + ch = response.GetChar(); // Skip the command separator + } while (ch == ','); // Make sure we got a comma separator + } + } + } + else + { +#if defined (LLDB_CONFIGURATION_DEBUG) + // assert(!"ProcessGDBRemote::UpdateThreadList() failed due to not getting the sequence mutex"); +#else + Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); + if (log) + log->Printf("error: failed to get packet sequence mutex, not sending packet 'qfThreadInfo'"); +#endif + sequence_mutex_unavailable = true; + } + return thread_ids.size(); +} + +lldb::addr_t +GDBRemoteCommunicationClient::GetShlibInfoAddr() +{ + if (!IsRunning()) + { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qShlibInfoAddr", ::strlen ("qShlibInfoAddr"), response, false)) + { + if (response.IsNormalResponse()) + return response.GetHexMaxU64(false, LLDB_INVALID_ADDRESS); + } + } + return LLDB_INVALID_ADDRESS; +} + diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h new file mode 100644 index 000000000000..5bb8387b9094 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -0,0 +1,430 @@ +//===-- GDBRemoteCommunicationClient.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationClient_h_ +#define liblldb_GDBRemoteCommunicationClient_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Target/Process.h" + +#include "GDBRemoteCommunication.h" + +typedef enum +{ + eBreakpointSoftware = 0, + eBreakpointHardware, + eWatchpointWrite, + eWatchpointRead, + eWatchpointReadWrite +} GDBStoppointType; + +class GDBRemoteCommunicationClient : public GDBRemoteCommunication +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteCommunicationClient(bool is_platform); + + virtual + ~GDBRemoteCommunicationClient(); + + //------------------------------------------------------------------ + // After connecting, send the handshake to the server to make sure + // we are communicating with it. + //------------------------------------------------------------------ + bool + HandshakeWithServer (lldb_private::Error *error_ptr); + + size_t + SendPacketAndWaitForResponse (const char *send_payload, + StringExtractorGDBRemote &response, + bool send_async); + + size_t + SendPacketAndWaitForResponse (const char *send_payload, + size_t send_length, + StringExtractorGDBRemote &response, + bool send_async); + + lldb::StateType + SendContinuePacketAndWaitForResponse (ProcessGDBRemote *process, + const char *packet_payload, + size_t packet_length, + StringExtractorGDBRemote &response); + + virtual bool + GetThreadSuffixSupported (); + + void + QueryNoAckModeSupported (); + + void + GetListThreadsInStopReplySupported (); + + bool + SendAsyncSignal (int signo); + + bool + SendInterrupt (lldb_private::Mutex::Locker &locker, + uint32_t seconds_to_wait_for_stop, + bool &timed_out); + + lldb::pid_t + GetCurrentProcessID (); + + bool + GetLaunchSuccess (std::string &error_str); + + uint16_t + LaunchGDBserverAndGetPort (); + + //------------------------------------------------------------------ + /// Sends a GDB remote protocol 'A' packet that delivers program + /// arguments to the remote server. + /// + /// @param[in] argv + /// A NULL terminated array of const C strings to use as the + /// arguments. + /// + /// @return + /// Zero if the response was "OK", a positive value if the + /// the response was "Exx" where xx are two hex digits, or + /// -1 if the call is unsupported or any other unexpected + /// response was received. + //------------------------------------------------------------------ + int + SendArgumentsPacket (char const *argv[]); + + //------------------------------------------------------------------ + /// Sends a "QEnvironment:NAME=VALUE" packet that will build up the + /// environment that will get used when launching an application + /// in conjunction with the 'A' packet. This function can be called + /// multiple times in a row in order to pass on the desired + /// environment that the inferior should be launched with. + /// + /// @param[in] name_equal_value + /// A NULL terminated C string that contains a single environment + /// in the format "NAME=VALUE". + /// + /// @return + /// Zero if the response was "OK", a positive value if the + /// the response was "Exx" where xx are two hex digits, or + /// -1 if the call is unsupported or any other unexpected + /// response was received. + //------------------------------------------------------------------ + int + SendEnvironmentPacket (char const *name_equal_value); + + int + SendLaunchArchPacket (const char *arch); + //------------------------------------------------------------------ + /// Sends a "vAttach:PID" where PID is in hex. + /// + /// @param[in] pid + /// A process ID for the remote gdb server to attach to. + /// + /// @param[out] response + /// The response received from the gdb server. If the return + /// value is zero, \a response will contain a stop reply + /// packet. + /// + /// @return + /// Zero if the attach was successful, or an error indicating + /// an error code. + //------------------------------------------------------------------ + int + SendAttach (lldb::pid_t pid, + StringExtractorGDBRemote& response); + + + //------------------------------------------------------------------ + /// Sets the path to use for stdin/out/err for a process + /// that will be launched with the 'A' packet. + /// + /// @param[in] path + /// The path to use for stdin/out/err + /// + /// @return + /// Zero if the for success, or an error code for failure. + //------------------------------------------------------------------ + int + SetSTDIN (char const *path); + int + SetSTDOUT (char const *path); + int + SetSTDERR (char const *path); + + //------------------------------------------------------------------ + /// Sets the disable ASLR flag to \a enable for a process that will + /// be launched with the 'A' packet. + /// + /// @param[in] enable + /// A boolean value indicating wether to disable ASLR or not. + /// + /// @return + /// Zero if the for success, or an error code for failure. + //------------------------------------------------------------------ + int + SetDisableASLR (bool enable); + + //------------------------------------------------------------------ + /// Sets the working directory to \a path for a process that will + /// be launched with the 'A' packet. + /// + /// @param[in] path + /// The path to a directory to use when launching our processs + /// + /// @return + /// Zero if the for success, or an error code for failure. + //------------------------------------------------------------------ + int + SetWorkingDir (char const *path); + + lldb::addr_t + AllocateMemory (size_t size, uint32_t permissions); + + bool + DeallocateMemory (lldb::addr_t addr); + + lldb_private::Error + Detach (bool keep_stopped); + + lldb_private::Error + GetMemoryRegionInfo (lldb::addr_t addr, + lldb_private::MemoryRegionInfo &range_info); + + lldb_private::Error + GetWatchpointSupportInfo (uint32_t &num); + + lldb_private::Error + GetWatchpointSupportInfo (uint32_t &num, bool& after); + + lldb_private::Error + GetWatchpointsTriggerAfterInstruction (bool &after); + + const lldb_private::ArchSpec & + GetHostArchitecture (); + + const lldb_private::ArchSpec & + GetProcessArchitecture (); + + bool + GetVContSupported (char flavor); + + bool + GetVAttachOrWaitSupported (); + + bool + GetSyncThreadStateSupported(); + + void + ResetDiscoverableSettings(); + + bool + GetHostInfo (bool force = false); + + bool + GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update); + + bool + GetOSBuildString (std::string &s); + + bool + GetOSKernelDescription (std::string &s); + + lldb_private::ArchSpec + GetSystemArchitecture (); + + bool + GetHostname (std::string &s); + + lldb::addr_t + GetShlibInfoAddr(); + + bool + GetSupportsThreadSuffix (); + + bool + GetProcessInfo (lldb::pid_t pid, + lldb_private::ProcessInstanceInfo &process_info); + + uint32_t + FindProcesses (const lldb_private::ProcessInstanceInfoMatch &process_match_info, + lldb_private::ProcessInstanceInfoList &process_infos); + + bool + GetUserName (uint32_t uid, std::string &name); + + bool + GetGroupName (uint32_t gid, std::string &name); + + bool + HasFullVContSupport () + { + return GetVContSupported ('A'); + } + + bool + HasAnyVContSupport () + { + return GetVContSupported ('a'); + } + + bool + GetStopReply (StringExtractorGDBRemote &response); + + bool + GetThreadStopInfo (lldb::tid_t tid, + StringExtractorGDBRemote &response); + + bool + SupportsGDBStoppointPacket (GDBStoppointType type) + { + switch (type) + { + case eBreakpointSoftware: return m_supports_z0; + case eBreakpointHardware: return m_supports_z1; + case eWatchpointWrite: return m_supports_z2; + case eWatchpointRead: return m_supports_z3; + case eWatchpointReadWrite: return m_supports_z4; + } + return false; + } + uint8_t + SendGDBStoppointTypePacket (GDBStoppointType type, // Type of breakpoint or watchpoint + bool insert, // Insert or remove? + lldb::addr_t addr, // Address of breakpoint or watchpoint + uint32_t length); // Byte Size of breakpoint or watchpoint + + void + TestPacketSpeed (const uint32_t num_packets); + + // This packet is for testing the speed of the interface only. Both + // the client and server need to support it, but this allows us to + // measure the packet speed without any other work being done on the + // other end and avoids any of that work affecting the packet send + // and response times. + bool + SendSpeedTestPacket (uint32_t send_size, + uint32_t recv_size); + + bool + SetCurrentThread (uint64_t tid); + + bool + SetCurrentThreadForRun (uint64_t tid); + + lldb_private::LazyBool + SupportsAllocDeallocMemory () // const + { + // Uncomment this to have lldb pretend the debug server doesn't respond to alloc/dealloc memory packets. + // m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo; + return m_supports_alloc_dealloc_memory; + } + + size_t + GetCurrentThreadIDs (std::vector<lldb::tid_t> &thread_ids, + bool &sequence_mutex_unavailable); + + bool + GetInterruptWasSent () const + { + return m_interrupt_sent; + } + + std::string + HarmonizeThreadIdsForProfileData (ProcessGDBRemote *process, + StringExtractorGDBRemote &inputStringExtractor); + +protected: + + bool + GetCurrentProcessInfo (); + + //------------------------------------------------------------------ + // Classes that inherit from GDBRemoteCommunicationClient can see and modify these + //------------------------------------------------------------------ + lldb_private::LazyBool m_supports_not_sending_acks; + lldb_private::LazyBool m_supports_thread_suffix; + lldb_private::LazyBool m_supports_threads_in_stop_reply; + lldb_private::LazyBool m_supports_vCont_all; + lldb_private::LazyBool m_supports_vCont_any; + lldb_private::LazyBool m_supports_vCont_c; + lldb_private::LazyBool m_supports_vCont_C; + lldb_private::LazyBool m_supports_vCont_s; + lldb_private::LazyBool m_supports_vCont_S; + lldb_private::LazyBool m_qHostInfo_is_valid; + lldb_private::LazyBool m_qProcessInfo_is_valid; + lldb_private::LazyBool m_supports_alloc_dealloc_memory; + lldb_private::LazyBool m_supports_memory_region_info; + lldb_private::LazyBool m_supports_watchpoint_support_info; + lldb_private::LazyBool m_supports_detach_stay_stopped; + lldb_private::LazyBool m_watchpoints_trigger_after_instruction; + lldb_private::LazyBool m_attach_or_wait_reply; + lldb_private::LazyBool m_prepare_for_reg_writing_reply; + + bool + m_supports_qProcessInfoPID:1, + m_supports_qfProcessInfo:1, + m_supports_qUserName:1, + m_supports_qGroupName:1, + m_supports_qThreadStopInfo:1, + m_supports_z0:1, + m_supports_z1:1, + m_supports_z2:1, + m_supports_z3:1, + m_supports_z4:1; + + + lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations + lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for continue, step, etc + + + uint32_t m_num_supported_hardware_watchpoints; + + // If we need to send a packet while the target is running, the m_async_XXX + // member variables take care of making this happen. + lldb_private::Mutex m_async_mutex; + lldb_private::Predicate<bool> m_async_packet_predicate; + std::string m_async_packet; + StringExtractorGDBRemote m_async_response; + int m_async_signal; // We were asked to deliver a signal to the inferior process. + bool m_interrupt_sent; + std::string m_partial_profile_data; + std::map<uint64_t, uint32_t> m_thread_id_to_used_usec_map; + + lldb_private::ArchSpec m_host_arch; + lldb_private::ArchSpec m_process_arch; + uint32_t m_os_version_major; + uint32_t m_os_version_minor; + uint32_t m_os_version_update; + std::string m_os_build; + std::string m_os_kernel; + std::string m_hostname; + + bool + DecodeProcessInfoResponse (StringExtractorGDBRemote &response, + lldb_private::ProcessInstanceInfo &process_info); +private: + //------------------------------------------------------------------ + // For GDBRemoteCommunicationClient only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationClient); +}; + +#endif // liblldb_GDBRemoteCommunicationClient_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp new file mode 100644 index 000000000000..3a14e9fe759a --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -0,0 +1,839 @@ +//===-- GDBRemoteCommunicationServer.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "GDBRemoteCommunicationServer.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/Triple.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Target/Process.h" + +// Project includes +#include "Utility/StringExtractorGDBRemote.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// GDBRemoteCommunicationServer constructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) : + GDBRemoteCommunication ("gdb-remote.server", "gdb-remote.server.rx_packet", is_platform), + m_async_thread (LLDB_INVALID_HOST_THREAD), + m_process_launch_info (), + m_process_launch_error (), + m_proc_infos (), + m_proc_infos_index (0), + m_lo_port_num (0), + m_hi_port_num (0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() +{ +} + + +//void * +//GDBRemoteCommunicationServer::AsyncThread (void *arg) +//{ +// GDBRemoteCommunicationServer *server = (GDBRemoteCommunicationServer*) arg; +// +// Log *log;// (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); +// if (log) +// log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread starting...", __FUNCTION__, arg, process->GetID()); +// +// StringExtractorGDBRemote packet; +// +// while () +// { +// if (packet. +// } +// +// if (log) +// log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %i) thread exiting...", __FUNCTION__, arg, process->GetID()); +// +// process->m_async_thread = LLDB_INVALID_HOST_THREAD; +// return NULL; +//} +// +bool +GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, + Error &error, + bool &interrupt, + bool &quit) +{ + StringExtractorGDBRemote packet; + if (WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec)) + { + const StringExtractorGDBRemote::ServerPacketType packet_type = packet.GetServerPacketType (); + switch (packet_type) + { + case StringExtractorGDBRemote::eServerPacketType_nack: + case StringExtractorGDBRemote::eServerPacketType_ack: + break; + + case StringExtractorGDBRemote::eServerPacketType_invalid: + error.SetErrorString("invalid packet"); + quit = true; + break; + + case StringExtractorGDBRemote::eServerPacketType_interrupt: + error.SetErrorString("interrupt received"); + interrupt = true; + break; + + case StringExtractorGDBRemote::eServerPacketType_unimplemented: + return SendUnimplementedResponse (packet.GetStringRef().c_str()) > 0; + + case StringExtractorGDBRemote::eServerPacketType_A: + return Handle_A (packet); + + case StringExtractorGDBRemote::eServerPacketType_qfProcessInfo: + return Handle_qfProcessInfo (packet); + + case StringExtractorGDBRemote::eServerPacketType_qsProcessInfo: + return Handle_qsProcessInfo (packet); + + case StringExtractorGDBRemote::eServerPacketType_qC: + return Handle_qC (packet); + + case StringExtractorGDBRemote::eServerPacketType_qHostInfo: + return Handle_qHostInfo (packet); + + case StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer: + return Handle_qLaunchGDBServer (packet); + + case StringExtractorGDBRemote::eServerPacketType_qLaunchSuccess: + return Handle_qLaunchSuccess (packet); + + case StringExtractorGDBRemote::eServerPacketType_qGroupName: + return Handle_qGroupName (packet); + + case StringExtractorGDBRemote::eServerPacketType_qProcessInfoPID: + return Handle_qProcessInfoPID (packet); + + case StringExtractorGDBRemote::eServerPacketType_qSpeedTest: + return Handle_qSpeedTest (packet); + + case StringExtractorGDBRemote::eServerPacketType_qUserName: + return Handle_qUserName (packet); + + case StringExtractorGDBRemote::eServerPacketType_QEnvironment: + return Handle_QEnvironment (packet); + + case StringExtractorGDBRemote::eServerPacketType_QSetDisableASLR: + return Handle_QSetDisableASLR (packet); + + case StringExtractorGDBRemote::eServerPacketType_QSetSTDIN: + return Handle_QSetSTDIN (packet); + + case StringExtractorGDBRemote::eServerPacketType_QSetSTDOUT: + return Handle_QSetSTDOUT (packet); + + case StringExtractorGDBRemote::eServerPacketType_QSetSTDERR: + return Handle_QSetSTDERR (packet); + + case StringExtractorGDBRemote::eServerPacketType_QSetWorkingDir: + return Handle_QSetWorkingDir (packet); + + case StringExtractorGDBRemote::eServerPacketType_QStartNoAckMode: + return Handle_QStartNoAckMode (packet); + } + return true; + } + else + { + if (!IsConnected()) + error.SetErrorString("lost connection"); + else + error.SetErrorString("timeout"); + } + + return false; +} + +size_t +GDBRemoteCommunicationServer::SendUnimplementedResponse (const char *) +{ + // TODO: Log the packet we aren't handling... + return SendPacketNoLock ("", 0); +} + +size_t +GDBRemoteCommunicationServer::SendErrorResponse (uint8_t err) +{ + char packet[16]; + int packet_len = ::snprintf (packet, sizeof(packet), "E%2.2x", err); + assert (packet_len < (int)sizeof(packet)); + return SendPacketNoLock (packet, packet_len); +} + + +size_t +GDBRemoteCommunicationServer::SendOKResponse () +{ + return SendPacketNoLock ("OK", 2); +} + +bool +GDBRemoteCommunicationServer::HandshakeWithClient(Error *error_ptr) +{ + return GetAck(); +} + +bool +GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet) +{ + StreamString response; + + // $cputype:16777223;cpusubtype:3;ostype:Darwin;vendor:apple;endian:little;ptrsize:8;#00 + + ArchSpec host_arch (Host::GetArchitecture ()); + const llvm::Triple &host_triple = host_arch.GetTriple(); + response.PutCString("triple:"); + response.PutCStringAsRawHex8(host_triple.getTriple().c_str()); + response.Printf (";ptrsize:%u;",host_arch.GetAddressByteSize()); + + uint32_t cpu = host_arch.GetMachOCPUType(); + uint32_t sub = host_arch.GetMachOCPUSubType(); + if (cpu != LLDB_INVALID_CPUTYPE) + response.Printf ("cputype:%u;", cpu); + if (sub != LLDB_INVALID_CPUTYPE) + response.Printf ("cpusubtype:%u;", sub); + + if (cpu == ArchSpec::kCore_arm_any) + response.Printf("watchpoint_exceptions_received:before;"); // On armv7 we use "synchronous" watchpoints which means the exception is delivered before the instruction executes. + else + response.Printf("watchpoint_exceptions_received:after;"); + + switch (lldb::endian::InlHostByteOrder()) + { + case eByteOrderBig: response.PutCString ("endian:big;"); break; + case eByteOrderLittle: response.PutCString ("endian:little;"); break; + case eByteOrderPDP: response.PutCString ("endian:pdp;"); break; + default: response.PutCString ("endian:unknown;"); break; + } + + uint32_t major = UINT32_MAX; + uint32_t minor = UINT32_MAX; + uint32_t update = UINT32_MAX; + if (Host::GetOSVersion (major, minor, update)) + { + if (major != UINT32_MAX) + { + response.Printf("os_version:%u", major); + if (minor != UINT32_MAX) + { + response.Printf(".%u", minor); + if (update != UINT32_MAX) + response.Printf(".%u", update); + } + response.PutChar(';'); + } + } + + std::string s; + if (Host::GetOSBuildString (s)) + { + response.PutCString ("os_build:"); + response.PutCStringAsRawHex8(s.c_str()); + response.PutChar(';'); + } + if (Host::GetOSKernelDescription (s)) + { + response.PutCString ("os_kernel:"); + response.PutCStringAsRawHex8(s.c_str()); + response.PutChar(';'); + } + if (Host::GetHostname (s)) + { + response.PutCString ("hostname:"); + response.PutCStringAsRawHex8(s.c_str()); + response.PutChar(';'); + } + + return SendPacketNoLock (response.GetData(), response.GetSize()) > 0; +} + +static void +CreateProcessInfoResponse (const ProcessInstanceInfo &proc_info, StreamString &response) +{ + response.Printf ("pid:%" PRIu64 ";ppid:%" PRIu64 ";uid:%i;gid:%i;euid:%i;egid:%i;", + proc_info.GetProcessID(), + proc_info.GetParentProcessID(), + proc_info.GetUserID(), + proc_info.GetGroupID(), + proc_info.GetEffectiveUserID(), + proc_info.GetEffectiveGroupID()); + response.PutCString ("name:"); + response.PutCStringAsRawHex8(proc_info.GetName()); + response.PutChar(';'); + const ArchSpec &proc_arch = proc_info.GetArchitecture(); + if (proc_arch.IsValid()) + { + const llvm::Triple &proc_triple = proc_arch.GetTriple(); + response.PutCString("triple:"); + response.PutCStringAsRawHex8(proc_triple.getTriple().c_str()); + response.PutChar(';'); + } +} + +bool +GDBRemoteCommunicationServer::Handle_qProcessInfoPID (StringExtractorGDBRemote &packet) +{ + // Packet format: "qProcessInfoPID:%i" where %i is the pid + packet.SetFilePos(::strlen ("qProcessInfoPID:")); + lldb::pid_t pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID); + if (pid != LLDB_INVALID_PROCESS_ID) + { + ProcessInstanceInfo proc_info; + if (Host::GetProcessInfo(pid, proc_info)) + { + StreamString response; + CreateProcessInfoResponse (proc_info, response); + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + } + return SendErrorResponse (1); +} + +bool +GDBRemoteCommunicationServer::Handle_qfProcessInfo (StringExtractorGDBRemote &packet) +{ + m_proc_infos_index = 0; + m_proc_infos.Clear(); + + ProcessInstanceInfoMatch match_info; + packet.SetFilePos(::strlen ("qfProcessInfo")); + if (packet.GetChar() == ':') + { + + std::string key; + std::string value; + while (packet.GetNameColonValue(key, value)) + { + bool success = true; + if (key.compare("name") == 0) + { + StringExtractor extractor; + extractor.GetStringRef().swap(value); + extractor.GetHexByteString (value); + match_info.GetProcessInfo().GetExecutableFile().SetFile(value.c_str(), false); + } + else if (key.compare("name_match") == 0) + { + if (value.compare("equals") == 0) + { + match_info.SetNameMatchType (eNameMatchEquals); + } + else if (value.compare("starts_with") == 0) + { + match_info.SetNameMatchType (eNameMatchStartsWith); + } + else if (value.compare("ends_with") == 0) + { + match_info.SetNameMatchType (eNameMatchEndsWith); + } + else if (value.compare("contains") == 0) + { + match_info.SetNameMatchType (eNameMatchContains); + } + else if (value.compare("regex") == 0) + { + match_info.SetNameMatchType (eNameMatchRegularExpression); + } + else + { + success = false; + } + } + else if (key.compare("pid") == 0) + { + match_info.GetProcessInfo().SetProcessID (Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success)); + } + else if (key.compare("parent_pid") == 0) + { + match_info.GetProcessInfo().SetParentProcessID (Args::StringToUInt32(value.c_str(), LLDB_INVALID_PROCESS_ID, 0, &success)); + } + else if (key.compare("uid") == 0) + { + match_info.GetProcessInfo().SetUserID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success)); + } + else if (key.compare("gid") == 0) + { + match_info.GetProcessInfo().SetGroupID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success)); + } + else if (key.compare("euid") == 0) + { + match_info.GetProcessInfo().SetEffectiveUserID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success)); + } + else if (key.compare("egid") == 0) + { + match_info.GetProcessInfo().SetEffectiveGroupID (Args::StringToUInt32(value.c_str(), UINT32_MAX, 0, &success)); + } + else if (key.compare("all_users") == 0) + { + match_info.SetMatchAllUsers(Args::StringToBoolean(value.c_str(), false, &success)); + } + else if (key.compare("triple") == 0) + { + match_info.GetProcessInfo().GetArchitecture().SetTriple (value.c_str(), NULL); + } + else + { + success = false; + } + + if (!success) + return SendErrorResponse (2); + } + } + + if (Host::FindProcesses (match_info, m_proc_infos)) + { + // We found something, return the first item by calling the get + // subsequent process info packet handler... + return Handle_qsProcessInfo (packet); + } + return SendErrorResponse (3); +} + +bool +GDBRemoteCommunicationServer::Handle_qsProcessInfo (StringExtractorGDBRemote &packet) +{ + if (m_proc_infos_index < m_proc_infos.GetSize()) + { + StreamString response; + CreateProcessInfoResponse (m_proc_infos.GetProcessInfoAtIndex(m_proc_infos_index), response); + ++m_proc_infos_index; + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + return SendErrorResponse (4); +} + +bool +GDBRemoteCommunicationServer::Handle_qUserName (StringExtractorGDBRemote &packet) +{ + // Packet format: "qUserName:%i" where %i is the uid + packet.SetFilePos(::strlen ("qUserName:")); + uint32_t uid = packet.GetU32 (UINT32_MAX); + if (uid != UINT32_MAX) + { + std::string name; + if (Host::GetUserName (uid, name)) + { + StreamString response; + response.PutCStringAsRawHex8 (name.c_str()); + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + } + return SendErrorResponse (5); + +} + +bool +GDBRemoteCommunicationServer::Handle_qGroupName (StringExtractorGDBRemote &packet) +{ + // Packet format: "qGroupName:%i" where %i is the gid + packet.SetFilePos(::strlen ("qGroupName:")); + uint32_t gid = packet.GetU32 (UINT32_MAX); + if (gid != UINT32_MAX) + { + std::string name; + if (Host::GetGroupName (gid, name)) + { + StreamString response; + response.PutCStringAsRawHex8 (name.c_str()); + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + } + return SendErrorResponse (6); +} + +bool +GDBRemoteCommunicationServer::Handle_qSpeedTest (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("qSpeedTest:")); + + std::string key; + std::string value; + bool success = packet.GetNameColonValue(key, value); + if (success && key.compare("response_size") == 0) + { + uint32_t response_size = Args::StringToUInt32(value.c_str(), 0, 0, &success); + if (success) + { + if (response_size == 0) + return SendOKResponse(); + StreamString response; + uint32_t bytes_left = response_size; + response.PutCString("data:"); + while (bytes_left > 0) + { + if (bytes_left >= 26) + { + response.PutCString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + bytes_left -= 26; + } + else + { + response.Printf ("%*.*s;", bytes_left, bytes_left, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + bytes_left = 0; + } + } + return SendPacketNoLock (response.GetData(), response.GetSize()); + } + } + return SendErrorResponse (7); +} + + +static void * +AcceptPortFromInferior (void *arg) +{ + const char *connect_url = (const char *)arg; + ConnectionFileDescriptor file_conn; + Error error; + if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess) + { + char pid_str[256]; + ::memset (pid_str, 0, sizeof(pid_str)); + ConnectionStatus status; + const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL); + if (pid_str_len > 0) + { + int pid = atoi (pid_str); + return (void *)(intptr_t)pid; + } + } + return NULL; +} +// +//static bool +//WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) +//{ +// const int time_delta_usecs = 100000; +// const int num_retries = timeout_in_seconds/time_delta_usecs; +// for (int i=0; i<num_retries; i++) +// { +// struct proc_bsdinfo bsd_info; +// int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO, +// (uint64_t) 0, +// &bsd_info, +// PROC_PIDTBSDINFO_SIZE); +// +// switch (error) +// { +// case EINVAL: +// case ENOTSUP: +// case ESRCH: +// case EPERM: +// return false; +// +// default: +// break; +// +// case 0: +// if (bsd_info.pbi_status == SSTOP) +// return true; +// } +// ::usleep (time_delta_usecs); +// } +// return false; +//} + +bool +GDBRemoteCommunicationServer::Handle_A (StringExtractorGDBRemote &packet) +{ + // The 'A' packet is the most over designed packet ever here with + // redundant argument indexes, redundant argument lengths and needed hex + // encoded argument string values. Really all that is needed is a comma + // separated hex encoded argument value list, but we will stay true to the + // documented version of the 'A' packet here... + + packet.SetFilePos(1); // Skip the 'A' + bool success = true; + while (success && packet.GetBytesLeft() > 0) + { + // Decode the decimal argument string length. This length is the + // number of hex nibbles in the argument string value. + const uint32_t arg_len = packet.GetU32(UINT32_MAX); + if (arg_len == UINT32_MAX) + success = false; + else + { + // Make sure the argument hex string length is followed by a comma + if (packet.GetChar() != ',') + success = false; + else + { + // Decode the argument index. We ignore this really becuase + // who would really send down the arguments in a random order??? + const uint32_t arg_idx = packet.GetU32(UINT32_MAX); + if (arg_idx == UINT32_MAX) + success = false; + else + { + // Make sure the argument index is followed by a comma + if (packet.GetChar() != ',') + success = false; + else + { + // Decode the argument string value from hex bytes + // back into a UTF8 string and make sure the length + // matches the one supplied in the packet + std::string arg; + if (packet.GetHexByteString(arg) != (arg_len / 2)) + success = false; + else + { + // If there are any bytes lft + if (packet.GetBytesLeft()) + { + if (packet.GetChar() != ',') + success = false; + } + + if (success) + { + if (arg_idx == 0) + m_process_launch_info.GetExecutableFile().SetFile(arg.c_str(), false); + m_process_launch_info.GetArguments().AppendArgument(arg.c_str()); + } + } + } + } + } + } + } + + if (success) + { + m_process_launch_info.GetFlags().Set (eLaunchFlagDebug); + m_process_launch_error = Host::LaunchProcess (m_process_launch_info); + if (m_process_launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) + { + return SendOKResponse (); + } + } + return SendErrorResponse (8); +} + +bool +GDBRemoteCommunicationServer::Handle_qC (StringExtractorGDBRemote &packet) +{ + lldb::pid_t pid = m_process_launch_info.GetProcessID(); + StreamString response; + response.Printf("QC%" PRIx64, pid); + if (m_is_platform) + { + // If we launch a process and this GDB server is acting as a platform, + // then we need to clear the process launch state so we can start + // launching another process. In order to launch a process a bunch or + // packets need to be sent: environment packets, working directory, + // disable ASLR, and many more settings. When we launch a process we + // then need to know when to clear this information. Currently we are + // selecting the 'qC' packet as that packet which seems to make the most + // sense. + if (pid != LLDB_INVALID_PROCESS_ID) + { + m_process_launch_info.Clear(); + } + } + return SendPacketNoLock (response.GetData(), response.GetSize()); +} + +bool +GDBRemoteCommunicationServer::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) +{ + // Spawn a local debugserver as a platform so we can then attach or launch + // a process... + + if (m_is_platform) + { + // Sleep and wait a bit for debugserver to start to listen... + ConnectionFileDescriptor file_conn; + char connect_url[PATH_MAX]; + Error error; + char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX"; + if (::mktemp (unix_socket_name) == NULL) + { + error.SetErrorString ("failed to make temporary path for a unix socket"); + } + else + { + ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name); + // Spawn a new thread to accept the port that gets bound after + // binding to port 0 (zero). + lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name, + AcceptPortFromInferior, + connect_url, + &error); + + if (IS_VALID_LLDB_HOST_THREAD(accept_thread)) + { + // Spawn a debugserver and try to get + ProcessLaunchInfo debugserver_launch_info; + error = StartDebugserverProcess ("localhost:0", + unix_socket_name, + debugserver_launch_info); + + lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); + if (error.Success()) + { + bool success = false; + + thread_result_t accept_thread_result = NULL; + if (Host::ThreadJoin (accept_thread, &accept_thread_result, &error)) + { + if (accept_thread_result) + { + uint16_t port = (intptr_t)accept_thread_result; + char response[256]; + const int response_len = ::snprintf (response, sizeof(response), "pid:%" PRIu64 ";port:%u;", debugserver_pid, port); + assert (response_len < (int)sizeof(response)); + //m_port_to_pid_map[port] = debugserver_launch_info.GetProcessID(); + success = SendPacketNoLock (response, response_len) > 0; + } + } + ::unlink (unix_socket_name); + + if (!success) + { + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + ::kill (debugserver_pid, SIGINT); + } + return success; + } + } + } + } + return SendErrorResponse (13); +} + +bool +GDBRemoteCommunicationServer::Handle_qLaunchSuccess (StringExtractorGDBRemote &packet) +{ + if (m_process_launch_error.Success()) + return SendOKResponse(); + StreamString response; + response.PutChar('E'); + response.PutCString(m_process_launch_error.AsCString("<unknown error>")); + return SendPacketNoLock (response.GetData(), response.GetSize()); +} + +bool +GDBRemoteCommunicationServer::Handle_QEnvironment (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QEnvironment:")); + const uint32_t bytes_left = packet.GetBytesLeft(); + if (bytes_left > 0) + { + m_process_launch_info.GetEnvironmentEntries ().AppendArgument (packet.Peek()); + return SendOKResponse (); + } + return SendErrorResponse (9); +} + +bool +GDBRemoteCommunicationServer::Handle_QSetDisableASLR (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetDisableASLR:")); + if (packet.GetU32(0)) + m_process_launch_info.GetFlags().Set (eLaunchFlagDisableASLR); + else + m_process_launch_info.GetFlags().Clear (eLaunchFlagDisableASLR); + return SendOKResponse (); +} + +bool +GDBRemoteCommunicationServer::Handle_QSetWorkingDir (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetWorkingDir:")); + std::string path; + packet.GetHexByteString(path); + m_process_launch_info.SwapWorkingDirectory (path); + return SendOKResponse (); +} + +bool +GDBRemoteCommunicationServer::Handle_QSetSTDIN (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetSTDIN:")); + ProcessLaunchInfo::FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = false; + const bool write = true; + if (file_action.Open(STDIN_FILENO, path.c_str(), read, write)) + { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse (); + } + return SendErrorResponse (10); +} + +bool +GDBRemoteCommunicationServer::Handle_QSetSTDOUT (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetSTDOUT:")); + ProcessLaunchInfo::FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = true; + const bool write = false; + if (file_action.Open(STDOUT_FILENO, path.c_str(), read, write)) + { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse (); + } + return SendErrorResponse (11); +} + +bool +GDBRemoteCommunicationServer::Handle_QSetSTDERR (StringExtractorGDBRemote &packet) +{ + packet.SetFilePos(::strlen ("QSetSTDERR:")); + ProcessLaunchInfo::FileAction file_action; + std::string path; + packet.GetHexByteString(path); + const bool read = true; + const bool write = false; + if (file_action.Open(STDERR_FILENO, path.c_str(), read, write)) + { + m_process_launch_info.AppendFileAction(file_action); + return SendOKResponse (); + } + return SendErrorResponse (12); +} + +bool +GDBRemoteCommunicationServer::Handle_QStartNoAckMode (StringExtractorGDBRemote &packet) +{ + // Send response first before changing m_send_acks to we ack this packet + SendOKResponse (); + m_send_acks = false; + return true; +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h new file mode 100644 index 000000000000..cce0e4e64c1e --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -0,0 +1,147 @@ +//===-- GDBRemoteCommunicationServer.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_GDBRemoteCommunicationServer_h_ +#define liblldb_GDBRemoteCommunicationServer_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Process.h" + +#include "GDBRemoteCommunication.h" + +class ProcessGDBRemote; +class StringExtractorGDBRemote; + +class GDBRemoteCommunicationServer : public GDBRemoteCommunication +{ +public: + enum + { + eBroadcastBitRunPacketSent = kLoUserBroadcastBit + }; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteCommunicationServer(bool is_platform); + + virtual + ~GDBRemoteCommunicationServer(); + + bool + GetPacketAndSendResponse (uint32_t timeout_usec, + lldb_private::Error &error, + bool &interrupt, + bool &quit); + + virtual bool + GetThreadSuffixSupported () + { + return true; + } + + // After connecting, do a little handshake with the client to make sure + // we are at least communicating + bool + HandshakeWithClient (lldb_private::Error *error_ptr); + + // Set both ports to zero to let the platform automatically bind to + // a port chosen by the OS. + void + SetPortRange (uint16_t lo_port_num, uint16_t hi_port_num) + { + m_lo_port_num = lo_port_num; + m_hi_port_num = hi_port_num; + } + +protected: + //typedef std::map<uint16_t, lldb::pid_t> PortToPIDMap; + + lldb::thread_t m_async_thread; + lldb_private::ProcessLaunchInfo m_process_launch_info; + lldb_private::Error m_process_launch_error; + lldb_private::ProcessInstanceInfoList m_proc_infos; + uint32_t m_proc_infos_index; + uint16_t m_lo_port_num; + uint16_t m_hi_port_num; + //PortToPIDMap m_port_to_pid_map; + + size_t + SendUnimplementedResponse (const char *packet); + + size_t + SendErrorResponse (uint8_t error); + + size_t + SendOKResponse (); + + bool + Handle_A (StringExtractorGDBRemote &packet); + + bool + Handle_qLaunchSuccess (StringExtractorGDBRemote &packet); + + bool + Handle_qHostInfo (StringExtractorGDBRemote &packet); + + bool + Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet); + + bool + Handle_qProcessInfoPID (StringExtractorGDBRemote &packet); + + bool + Handle_qfProcessInfo (StringExtractorGDBRemote &packet); + + bool + Handle_qsProcessInfo (StringExtractorGDBRemote &packet); + + bool + Handle_qC (StringExtractorGDBRemote &packet); + + bool + Handle_qUserName (StringExtractorGDBRemote &packet); + + bool + Handle_qGroupName (StringExtractorGDBRemote &packet); + + bool + Handle_qSpeedTest (StringExtractorGDBRemote &packet); + + bool + Handle_QEnvironment (StringExtractorGDBRemote &packet); + + bool + Handle_QSetDisableASLR (StringExtractorGDBRemote &packet); + + bool + Handle_QSetWorkingDir (StringExtractorGDBRemote &packet); + + bool + Handle_QStartNoAckMode (StringExtractorGDBRemote &packet); + + bool + Handle_QSetSTDIN (StringExtractorGDBRemote &packet); + + bool + Handle_QSetSTDOUT (StringExtractorGDBRemote &packet); + + bool + Handle_QSetSTDERR (StringExtractorGDBRemote &packet); + +private: + //------------------------------------------------------------------ + // For GDBRemoteCommunicationServer only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteCommunicationServer); +}; + +#endif // liblldb_GDBRemoteCommunicationServer_h_ diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp new file mode 100644 index 000000000000..b1612a5f3c2f --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp @@ -0,0 +1,971 @@ +//===-- GDBRemoteRegisterContext.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "GDBRemoteRegisterContext.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/Utils.h" +// Project includes +#include "Utility/StringExtractorGDBRemote.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "ThreadGDBRemote.h" +#include "Utility/ARM_GCC_Registers.h" +#include "Utility/ARM_DWARF_Registers.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// GDBRemoteRegisterContext constructor +//---------------------------------------------------------------------- +GDBRemoteRegisterContext::GDBRemoteRegisterContext +( + ThreadGDBRemote &thread, + uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfo ®_info, + bool read_all_at_once +) : + RegisterContext (thread, concrete_frame_idx), + m_reg_info (reg_info), + m_reg_valid (), + m_reg_data (), + m_read_all_at_once (read_all_at_once) +{ + // Resize our vector of bools to contain one bool for every register. + // We will use these boolean values to know when a register value + // is valid in m_reg_data. + m_reg_valid.resize (reg_info.GetNumRegisters()); + + // Make a heap based buffer that is big enough to store all registers + DataBufferSP reg_data_sp(new DataBufferHeap (reg_info.GetRegisterDataByteSize(), 0)); + m_reg_data.SetData (reg_data_sp); + +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +GDBRemoteRegisterContext::~GDBRemoteRegisterContext() +{ +} + +void +GDBRemoteRegisterContext::InvalidateAllRegisters () +{ + SetAllRegisterValid (false); +} + +void +GDBRemoteRegisterContext::SetAllRegisterValid (bool b) +{ + std::vector<bool>::iterator pos, end = m_reg_valid.end(); + for (pos = m_reg_valid.begin(); pos != end; ++pos) + *pos = b; +} + +size_t +GDBRemoteRegisterContext::GetRegisterCount () +{ + return m_reg_info.GetNumRegisters (); +} + +const RegisterInfo * +GDBRemoteRegisterContext::GetRegisterInfoAtIndex (size_t reg) +{ + return m_reg_info.GetRegisterInfoAtIndex (reg); +} + +size_t +GDBRemoteRegisterContext::GetRegisterSetCount () +{ + return m_reg_info.GetNumRegisterSets (); +} + + + +const RegisterSet * +GDBRemoteRegisterContext::GetRegisterSet (size_t reg_set) +{ + return m_reg_info.GetRegisterSet (reg_set); +} + + + +bool +GDBRemoteRegisterContext::ReadRegister (const RegisterInfo *reg_info, RegisterValue &value) +{ + // Read the register + if (ReadRegisterBytes (reg_info, m_reg_data)) + { + const bool partial_data_ok = false; + Error error (value.SetValueFromData(reg_info, m_reg_data, reg_info->byte_offset, partial_data_ok)); + return error.Success(); + } + return false; +} + +bool +GDBRemoteRegisterContext::PrivateSetRegisterValue (uint32_t reg, StringExtractor &response) +{ + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg); + if (reg_info == NULL) + return false; + + // Invalidate if needed + InvalidateIfNeeded(false); + + const uint32_t reg_byte_size = reg_info->byte_size; + const size_t bytes_copied = response.GetHexBytes (const_cast<uint8_t*>(m_reg_data.PeekData(reg_info->byte_offset, reg_byte_size)), reg_byte_size, '\xcc'); + bool success = bytes_copied == reg_byte_size; + if (success) + { + SetRegisterIsValid(reg, true); + } + else if (bytes_copied > 0) + { + // Only set register is valid to false if we copied some bytes, else + // leave it as it was. + SetRegisterIsValid(reg, false); + } + return success; +} + +// Helper function for GDBRemoteRegisterContext::ReadRegisterBytes(). +bool +GDBRemoteRegisterContext::GetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, + GDBRemoteCommunicationClient &gdb_comm) +{ + char packet[64]; + StringExtractorGDBRemote response; + int packet_len = 0; + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + if (gdb_comm.GetThreadSuffixSupported()) + packet_len = ::snprintf (packet, sizeof(packet), "p%x;thread:%4.4" PRIx64 ";", reg, m_thread.GetProtocolID()); + else + packet_len = ::snprintf (packet, sizeof(packet), "p%x", reg); + assert (packet_len < ((int)sizeof(packet) - 1)); + if (gdb_comm.SendPacketAndWaitForResponse(packet, response, false)) + return PrivateSetRegisterValue (reg, response); + + return false; +} +bool +GDBRemoteRegisterContext::ReadRegisterBytes (const RegisterInfo *reg_info, DataExtractor &data) +{ + ExecutionContext exe_ctx (CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == NULL || thread == NULL) + return false; + + GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote()); + + InvalidateIfNeeded(false); + + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + if (!GetRegisterIsValid(reg)) + { + Mutex::Locker locker; + if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for read register.")) + { + const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported(); + ProcessSP process_sp (m_thread.GetProcess()); + if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID())) + { + char packet[64]; + StringExtractorGDBRemote response; + int packet_len = 0; + if (m_read_all_at_once) + { + // Get all registers in one packet + if (thread_suffix_supported) + packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64 ";", m_thread.GetProtocolID()); + else + packet_len = ::snprintf (packet, sizeof(packet), "g"); + assert (packet_len < ((int)sizeof(packet) - 1)); + if (gdb_comm.SendPacketAndWaitForResponse(packet, response, false)) + { + if (response.IsNormalResponse()) + if (response.GetHexBytes ((void *)m_reg_data.GetDataStart(), m_reg_data.GetByteSize(), '\xcc') == m_reg_data.GetByteSize()) + SetAllRegisterValid (true); + } + } + else if (reg_info->value_regs) + { + // Process this composite register request by delegating to the constituent + // primordial registers. + + // Index of the primordial register. + bool success = true; + for (uint32_t idx = 0; success; ++idx) + { + const uint32_t prim_reg = reg_info->value_regs[idx]; + if (prim_reg == LLDB_INVALID_REGNUM) + break; + // We have a valid primordial regsiter as our constituent. + // Grab the corresponding register info. + const RegisterInfo *prim_reg_info = GetRegisterInfoAtIndex(prim_reg); + if (prim_reg_info == NULL) + success = false; + else + { + // Read the containing register if it hasn't already been read + if (!GetRegisterIsValid(prim_reg)) + success = GetPrimordialRegister(prim_reg_info, gdb_comm); + } + } + + if (success) + { + // If we reach this point, all primordial register requests have succeeded. + // Validate this composite register. + SetRegisterIsValid (reg_info, true); + } + } + else + { + // Get each register individually + GetPrimordialRegister(reg_info, gdb_comm); + } + } + } + else + { +#if LLDB_CONFIGURATION_DEBUG + StreamString strm; + gdb_comm.DumpHistory(strm); + Host::SetCrashDescription (strm.GetData()); + assert (!"Didn't get sequence mutex for read register."); +#else + Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS)); + if (log) + { + if (log->GetVerbose()) + { + StreamString strm; + gdb_comm.DumpHistory(strm); + log->Printf("error: failed to get packet sequence mutex, not sending read register for \"%s\":\n%s", reg_info->name, strm.GetData()); + } + else + { + log->Printf("error: failed to get packet sequence mutex, not sending read register for \"%s\"", reg_info->name); + } + } +#endif + } + + // Make sure we got a valid register value after reading it + if (!GetRegisterIsValid(reg)) + return false; + } + + if (&data != &m_reg_data) + { + // If we aren't extracting into our own buffer (which + // only happens when this function is called from + // ReadRegisterValue(uint32_t, Scalar&)) then + // we transfer bytes from our buffer into the data + // buffer that was passed in + data.SetByteOrder (m_reg_data.GetByteOrder()); + data.SetData (m_reg_data, reg_info->byte_offset, reg_info->byte_size); + } + return true; +} + +bool +GDBRemoteRegisterContext::WriteRegister (const RegisterInfo *reg_info, + const RegisterValue &value) +{ + DataExtractor data; + if (value.GetData (data)) + return WriteRegisterBytes (reg_info, data, 0); + return false; +} + +// Helper function for GDBRemoteRegisterContext::WriteRegisterBytes(). +bool +GDBRemoteRegisterContext::SetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, + GDBRemoteCommunicationClient &gdb_comm) +{ + StreamString packet; + StringExtractorGDBRemote response; + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + packet.Printf ("P%x=", reg); + packet.PutBytesAsRawHex8 (m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size), + reg_info->byte_size, + lldb::endian::InlHostByteOrder(), + lldb::endian::InlHostByteOrder()); + + if (gdb_comm.GetThreadSuffixSupported()) + packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID()); + + // Invalidate just this register + SetRegisterIsValid(reg, false); + if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), + packet.GetString().size(), + response, + false)) + { + if (response.IsOKResponse()) + return true; + } + return false; +} + +void +GDBRemoteRegisterContext::SyncThreadState(Process *process) +{ + // NB. We assume our caller has locked the sequence mutex. + + GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *) process)->GetGDBRemote()); + if (!gdb_comm.GetSyncThreadStateSupported()) + return; + + StreamString packet; + StringExtractorGDBRemote response; + packet.Printf ("QSyncThreadState:%4.4" PRIx64 ";", m_thread.GetProtocolID()); + if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), + packet.GetString().size(), + response, + false)) + { + if (response.IsOKResponse()) + InvalidateAllRegisters(); + } +} + +bool +GDBRemoteRegisterContext::WriteRegisterBytes (const lldb_private::RegisterInfo *reg_info, DataExtractor &data, uint32_t data_offset) +{ + ExecutionContext exe_ctx (CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == NULL || thread == NULL) + return false; + + GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote()); +// FIXME: This check isn't right because IsRunning checks the Public state, but this +// is work you need to do - for instance in ShouldStop & friends - before the public +// state has been changed. +// if (gdb_comm.IsRunning()) +// return false; + + // Grab a pointer to where we are going to put this register + uint8_t *dst = const_cast<uint8_t*>(m_reg_data.PeekData(reg_info->byte_offset, reg_info->byte_size)); + + if (dst == NULL) + return false; + + + if (data.CopyByteOrderedData (data_offset, // src offset + reg_info->byte_size, // src length + dst, // dst + reg_info->byte_size, // dst length + m_reg_data.GetByteOrder())) // dst byte order + { + Mutex::Locker locker; + if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for write register.")) + { + const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported(); + ProcessSP process_sp (m_thread.GetProcess()); + if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID())) + { + StreamString packet; + StringExtractorGDBRemote response; + + if (m_read_all_at_once) + { + // Set all registers in one packet + packet.PutChar ('G'); + packet.PutBytesAsRawHex8 (m_reg_data.GetDataStart(), + m_reg_data.GetByteSize(), + lldb::endian::InlHostByteOrder(), + lldb::endian::InlHostByteOrder()); + + if (thread_suffix_supported) + packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID()); + + // Invalidate all register values + InvalidateIfNeeded (true); + + if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), + packet.GetString().size(), + response, + false)) + { + SetAllRegisterValid (false); + if (response.IsOKResponse()) + { + return true; + } + } + } + else + { + bool success = true; + + if (reg_info->value_regs) + { + // This register is part of another register. In this case we read the actual + // register data for any "value_regs", and once all that data is read, we will + // have enough data in our register context bytes for the value of this register + + // Invalidate this composite register first. + + for (uint32_t idx = 0; success; ++idx) + { + const uint32_t reg = reg_info->value_regs[idx]; + if (reg == LLDB_INVALID_REGNUM) + break; + // We have a valid primordial regsiter as our constituent. + // Grab the corresponding register info. + const RegisterInfo *value_reg_info = GetRegisterInfoAtIndex(reg); + if (value_reg_info == NULL) + success = false; + else + success = SetPrimordialRegister(value_reg_info, gdb_comm); + } + } + else + { + // This is an actual register, write it + success = SetPrimordialRegister(reg_info, gdb_comm); + } + + // Check if writing this register will invalidate any other register values? + // If so, invalidate them + if (reg_info->invalidate_regs) + { + for (uint32_t idx = 0, reg = reg_info->invalidate_regs[0]; + reg != LLDB_INVALID_REGNUM; + reg = reg_info->invalidate_regs[++idx]) + { + SetRegisterIsValid(reg, false); + } + } + + return success; + } + } + } + else + { + Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS)); + if (log) + { + if (log->GetVerbose()) + { + StreamString strm; + gdb_comm.DumpHistory(strm); + log->Printf("error: failed to get packet sequence mutex, not sending write register for \"%s\":\n%s", reg_info->name, strm.GetData()); + } + else + log->Printf("error: failed to get packet sequence mutex, not sending write register for \"%s\"", reg_info->name); + } + } + } + return false; +} + + +bool +GDBRemoteRegisterContext::ReadAllRegisterValues (lldb::DataBufferSP &data_sp) +{ + ExecutionContext exe_ctx (CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == NULL || thread == NULL) + return false; + + GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote()); + + StringExtractorGDBRemote response; + + Mutex::Locker locker; + if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for read all registers.")) + { + SyncThreadState(process); + + char packet[32]; + const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported(); + ProcessSP process_sp (m_thread.GetProcess()); + if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID())) + { + int packet_len = 0; + if (thread_suffix_supported) + packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64, m_thread.GetProtocolID()); + else + packet_len = ::snprintf (packet, sizeof(packet), "g"); + assert (packet_len < ((int)sizeof(packet) - 1)); + + if (gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + if (response.IsErrorResponse()) + return false; + + std::string &response_str = response.GetStringRef(); + if (isxdigit(response_str[0])) + { + response_str.insert(0, 1, 'G'); + if (thread_suffix_supported) + { + char thread_id_cstr[64]; + ::snprintf (thread_id_cstr, sizeof(thread_id_cstr), ";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID()); + response_str.append (thread_id_cstr); + } + data_sp.reset (new DataBufferHeap (response_str.c_str(), response_str.size())); + return true; + } + } + } + } + else + { + Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS)); + if (log) + { + if (log->GetVerbose()) + { + StreamString strm; + gdb_comm.DumpHistory(strm); + log->Printf("error: failed to get packet sequence mutex, not sending read all registers:\n%s", strm.GetData()); + } + else + log->Printf("error: failed to get packet sequence mutex, not sending read all registers"); + } + } + + data_sp.reset(); + return false; +} + +bool +GDBRemoteRegisterContext::WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) +{ + if (!data_sp || data_sp->GetBytes() == NULL || data_sp->GetByteSize() == 0) + return false; + + ExecutionContext exe_ctx (CalculateThread()); + + Process *process = exe_ctx.GetProcessPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + if (process == NULL || thread == NULL) + return false; + + GDBRemoteCommunicationClient &gdb_comm (((ProcessGDBRemote *)process)->GetGDBRemote()); + + StringExtractorGDBRemote response; + Mutex::Locker locker; + if (gdb_comm.GetSequenceMutex (locker, "Didn't get sequence mutex for write all registers.")) + { + const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported(); + ProcessSP process_sp (m_thread.GetProcess()); + if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID())) + { + // The data_sp contains the entire G response packet including the + // G, and if the thread suffix is supported, it has the thread suffix + // as well. + const char *G_packet = (const char *)data_sp->GetBytes(); + size_t G_packet_len = data_sp->GetByteSize(); + if (gdb_comm.SendPacketAndWaitForResponse (G_packet, + G_packet_len, + response, + false)) + { + if (response.IsOKResponse()) + return true; + else if (response.IsErrorResponse()) + { + uint32_t num_restored = 0; + // We need to manually go through all of the registers and + // restore them manually + + response.GetStringRef().assign (G_packet, G_packet_len); + response.SetFilePos(1); // Skip the leading 'G' + DataBufferHeap buffer (m_reg_data.GetByteSize(), 0); + DataExtractor restore_data (buffer.GetBytes(), + buffer.GetByteSize(), + m_reg_data.GetByteOrder(), + m_reg_data.GetAddressByteSize()); + + const uint32_t bytes_extracted = response.GetHexBytes ((void *)restore_data.GetDataStart(), + restore_data.GetByteSize(), + '\xcc'); + + if (bytes_extracted < restore_data.GetByteSize()) + restore_data.SetData(restore_data.GetDataStart(), bytes_extracted, m_reg_data.GetByteOrder()); + + //ReadRegisterBytes (const RegisterInfo *reg_info, RegisterValue &value, DataExtractor &data) + const RegisterInfo *reg_info; + // We have to march the offset of each register along in the + // buffer to make sure we get the right offset. + uint32_t reg_byte_offset = 0; + for (uint32_t reg_idx=0; (reg_info = GetRegisterInfoAtIndex (reg_idx)) != NULL; ++reg_idx, reg_byte_offset += reg_info->byte_size) + { + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + // Skip composite registers. + if (reg_info->value_regs) + continue; + + // Only write down the registers that need to be written + // if we are going to be doing registers individually. + bool write_reg = true; + const uint32_t reg_byte_size = reg_info->byte_size; + + const char *restore_src = (const char *)restore_data.PeekData(reg_byte_offset, reg_byte_size); + if (restore_src) + { + if (GetRegisterIsValid(reg)) + { + const char *current_src = (const char *)m_reg_data.PeekData(reg_byte_offset, reg_byte_size); + if (current_src) + write_reg = memcmp (current_src, restore_src, reg_byte_size) != 0; + } + + if (write_reg) + { + StreamString packet; + packet.Printf ("P%x=", reg); + packet.PutBytesAsRawHex8 (restore_src, + reg_byte_size, + lldb::endian::InlHostByteOrder(), + lldb::endian::InlHostByteOrder()); + + if (thread_suffix_supported) + packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID()); + + SetRegisterIsValid(reg, false); + if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(), + packet.GetString().size(), + response, + false)) + { + if (response.IsOKResponse()) + ++num_restored; + } + } + } + } + return num_restored > 0; + } + } + } + } + else + { + Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_THREAD | GDBR_LOG_PACKETS)); + if (log) + { + if (log->GetVerbose()) + { + StreamString strm; + gdb_comm.DumpHistory(strm); + log->Printf("error: failed to get packet sequence mutex, not sending write all registers:\n%s", strm.GetData()); + } + else + log->Printf("error: failed to get packet sequence mutex, not sending write all registers"); + } + } + return false; +} + + +uint32_t +GDBRemoteRegisterContext::ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) +{ + return m_reg_info.ConvertRegisterKindToRegisterNumber (kind, num); +} + +void +GDBRemoteDynamicRegisterInfo::HardcodeARMRegisters(bool from_scratch) +{ + // For Advanced SIMD and VFP register mapping. + static uint32_t g_d0_regs[] = { 26, 27, LLDB_INVALID_REGNUM }; // (s0, s1) + static uint32_t g_d1_regs[] = { 28, 29, LLDB_INVALID_REGNUM }; // (s2, s3) + static uint32_t g_d2_regs[] = { 30, 31, LLDB_INVALID_REGNUM }; // (s4, s5) + static uint32_t g_d3_regs[] = { 32, 33, LLDB_INVALID_REGNUM }; // (s6, s7) + static uint32_t g_d4_regs[] = { 34, 35, LLDB_INVALID_REGNUM }; // (s8, s9) + static uint32_t g_d5_regs[] = { 36, 37, LLDB_INVALID_REGNUM }; // (s10, s11) + static uint32_t g_d6_regs[] = { 38, 39, LLDB_INVALID_REGNUM }; // (s12, s13) + static uint32_t g_d7_regs[] = { 40, 41, LLDB_INVALID_REGNUM }; // (s14, s15) + static uint32_t g_d8_regs[] = { 42, 43, LLDB_INVALID_REGNUM }; // (s16, s17) + static uint32_t g_d9_regs[] = { 44, 45, LLDB_INVALID_REGNUM }; // (s18, s19) + static uint32_t g_d10_regs[] = { 46, 47, LLDB_INVALID_REGNUM }; // (s20, s21) + static uint32_t g_d11_regs[] = { 48, 49, LLDB_INVALID_REGNUM }; // (s22, s23) + static uint32_t g_d12_regs[] = { 50, 51, LLDB_INVALID_REGNUM }; // (s24, s25) + static uint32_t g_d13_regs[] = { 52, 53, LLDB_INVALID_REGNUM }; // (s26, s27) + static uint32_t g_d14_regs[] = { 54, 55, LLDB_INVALID_REGNUM }; // (s28, s29) + static uint32_t g_d15_regs[] = { 56, 57, LLDB_INVALID_REGNUM }; // (s30, s31) + static uint32_t g_q0_regs[] = { 26, 27, 28, 29, LLDB_INVALID_REGNUM }; // (d0, d1) -> (s0, s1, s2, s3) + static uint32_t g_q1_regs[] = { 30, 31, 32, 33, LLDB_INVALID_REGNUM }; // (d2, d3) -> (s4, s5, s6, s7) + static uint32_t g_q2_regs[] = { 34, 35, 36, 37, LLDB_INVALID_REGNUM }; // (d4, d5) -> (s8, s9, s10, s11) + static uint32_t g_q3_regs[] = { 38, 39, 40, 41, LLDB_INVALID_REGNUM }; // (d6, d7) -> (s12, s13, s14, s15) + static uint32_t g_q4_regs[] = { 42, 43, 44, 45, LLDB_INVALID_REGNUM }; // (d8, d9) -> (s16, s17, s18, s19) + static uint32_t g_q5_regs[] = { 46, 47, 48, 49, LLDB_INVALID_REGNUM }; // (d10, d11) -> (s20, s21, s22, s23) + static uint32_t g_q6_regs[] = { 50, 51, 52, 53, LLDB_INVALID_REGNUM }; // (d12, d13) -> (s24, s25, s26, s27) + static uint32_t g_q7_regs[] = { 54, 55, 56, 57, LLDB_INVALID_REGNUM }; // (d14, d15) -> (s28, s29, s30, s31) + static uint32_t g_q8_regs[] = { 59, 60, LLDB_INVALID_REGNUM }; // (d16, d17) + static uint32_t g_q9_regs[] = { 61, 62, LLDB_INVALID_REGNUM }; // (d18, d19) + static uint32_t g_q10_regs[] = { 63, 64, LLDB_INVALID_REGNUM }; // (d20, d21) + static uint32_t g_q11_regs[] = { 65, 66, LLDB_INVALID_REGNUM }; // (d22, d23) + static uint32_t g_q12_regs[] = { 67, 68, LLDB_INVALID_REGNUM }; // (d24, d25) + static uint32_t g_q13_regs[] = { 69, 70, LLDB_INVALID_REGNUM }; // (d26, d27) + static uint32_t g_q14_regs[] = { 71, 72, LLDB_INVALID_REGNUM }; // (d28, d29) + static uint32_t g_q15_regs[] = { 73, 74, LLDB_INVALID_REGNUM }; // (d30, d31) + + // This is our array of composite registers, with each element coming from the above register mappings. + static uint32_t *g_composites[] = { + g_d0_regs, g_d1_regs, g_d2_regs, g_d3_regs, g_d4_regs, g_d5_regs, g_d6_regs, g_d7_regs, + g_d8_regs, g_d9_regs, g_d10_regs, g_d11_regs, g_d12_regs, g_d13_regs, g_d14_regs, g_d15_regs, + g_q0_regs, g_q1_regs, g_q2_regs, g_q3_regs, g_q4_regs, g_q5_regs, g_q6_regs, g_q7_regs, + g_q8_regs, g_q9_regs, g_q10_regs, g_q11_regs, g_q12_regs, g_q13_regs, g_q14_regs, g_q15_regs + }; + + static RegisterInfo g_register_infos[] = { +// NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB VALUE REGS INVALIDATE REGS +// ====== ====== === === ============= ============ =================== =================== ====================== === ==== ========== =============== + { "r0", "arg1", 4, 0, eEncodingUint, eFormatHex, { gcc_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1,0, 0 }, NULL, NULL}, + { "r1", "arg2", 4, 0, eEncodingUint, eFormatHex, { gcc_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2,1, 1 }, NULL, NULL}, + { "r2", "arg3", 4, 0, eEncodingUint, eFormatHex, { gcc_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3,2, 2 }, NULL, NULL}, + { "r3", "arg4", 4, 0, eEncodingUint, eFormatHex, { gcc_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4,3, 3 }, NULL, NULL}, + { "r4", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM, 4, 4 }, NULL, NULL}, + { "r5", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM, 5, 5 }, NULL, NULL}, + { "r6", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM, 6, 6 }, NULL, NULL}, + { "r7", "fp", 4, 0, eEncodingUint, eFormatHex, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, 7, 7 }, NULL, NULL}, + { "r8", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM, 8, 8 }, NULL, NULL}, + { "r9", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM, 9, 9 }, NULL, NULL}, + { "r10", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM, 10, 10 }, NULL, NULL}, + { "r11", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM, 11, 11 }, NULL, NULL}, + { "r12", NULL, 4, 0, eEncodingUint, eFormatHex, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM, 12, 12 }, NULL, NULL}, + { "sp", "r13", 4, 0, eEncodingUint, eFormatHex, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, 13, 13 }, NULL, NULL}, + { "lr", "r14", 4, 0, eEncodingUint, eFormatHex, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, 14, 14 }, NULL, NULL}, + { "pc", "r15", 4, 0, eEncodingUint, eFormatHex, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, 15, 15 }, NULL, NULL}, + { "f0", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 16, 16 }, NULL, NULL}, + { "f1", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 17, 17 }, NULL, NULL}, + { "f2", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 18, 18 }, NULL, NULL}, + { "f3", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 19, 19 }, NULL, NULL}, + { "f4", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 20, 20 }, NULL, NULL}, + { "f5", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 21, 21 }, NULL, NULL}, + { "f6", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 22, 22 }, NULL, NULL}, + { "f7", NULL, 12, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 23, 23 }, NULL, NULL}, + { "fps", NULL, 4, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 24, 24 }, NULL, NULL}, + { "cpsr","flags", 4, 0, eEncodingUint, eFormatHex, { gcc_cpsr, dwarf_cpsr, LLDB_INVALID_REGNUM, 25, 25 }, NULL, NULL}, + { "s0", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, 26, 26 }, NULL, NULL}, + { "s1", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, 27, 27 }, NULL, NULL}, + { "s2", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, 28, 28 }, NULL, NULL}, + { "s3", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, 29, 29 }, NULL, NULL}, + { "s4", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, 30, 30 }, NULL, NULL}, + { "s5", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, 31, 31 }, NULL, NULL}, + { "s6", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, 32, 32 }, NULL, NULL}, + { "s7", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, 33, 33 }, NULL, NULL}, + { "s8", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, 34, 34 }, NULL, NULL}, + { "s9", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, 35, 35 }, NULL, NULL}, + { "s10", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, 36, 36 }, NULL, NULL}, + { "s11", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, 37, 37 }, NULL, NULL}, + { "s12", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, 38, 38 }, NULL, NULL}, + { "s13", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, 39, 39 }, NULL, NULL}, + { "s14", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, 40, 40 }, NULL, NULL}, + { "s15", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, 41, 41 }, NULL, NULL}, + { "s16", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, 42, 42 }, NULL, NULL}, + { "s17", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, 43, 43 }, NULL, NULL}, + { "s18", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, 44, 44 }, NULL, NULL}, + { "s19", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, 45, 45 }, NULL, NULL}, + { "s20", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, 46, 46 }, NULL, NULL}, + { "s21", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, 47, 47 }, NULL, NULL}, + { "s22", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, 48, 48 }, NULL, NULL}, + { "s23", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, 49, 49 }, NULL, NULL}, + { "s24", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, 50, 50 }, NULL, NULL}, + { "s25", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, 51, 51 }, NULL, NULL}, + { "s26", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, 52, 52 }, NULL, NULL}, + { "s27", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, 53, 53 }, NULL, NULL}, + { "s28", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, 54, 54 }, NULL, NULL}, + { "s29", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, 55, 55 }, NULL, NULL}, + { "s30", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, 56, 56 }, NULL, NULL}, + { "s31", NULL, 4, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, 57, 57 }, NULL, NULL}, + { "fpscr",NULL, 4, 0, eEncodingUint, eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, 58, 58 }, NULL, NULL}, + { "d16", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, 59, 59 }, NULL, NULL}, + { "d17", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, 60, 60 }, NULL, NULL}, + { "d18", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, 61, 61 }, NULL, NULL}, + { "d19", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, 62, 62 }, NULL, NULL}, + { "d20", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, 63, 63 }, NULL, NULL}, + { "d21", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, 64, 64 }, NULL, NULL}, + { "d22", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, 65, 65 }, NULL, NULL}, + { "d23", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, 66, 66 }, NULL, NULL}, + { "d24", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, 67, 67 }, NULL, NULL}, + { "d25", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, 68, 68 }, NULL, NULL}, + { "d26", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, 69, 69 }, NULL, NULL}, + { "d27", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, 70, 70 }, NULL, NULL}, + { "d28", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, 71, 71 }, NULL, NULL}, + { "d29", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, 72, 72 }, NULL, NULL}, + { "d30", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, 73, 73 }, NULL, NULL}, + { "d31", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, 74, 74 }, NULL, NULL}, + { "d0", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, 75, 75 }, g_d0_regs, NULL}, + { "d1", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, 76, 76 }, g_d1_regs, NULL}, + { "d2", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, 77, 77 }, g_d2_regs, NULL}, + { "d3", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, 78, 78 }, g_d3_regs, NULL}, + { "d4", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, 79, 79 }, g_d4_regs, NULL}, + { "d5", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, 80, 80 }, g_d5_regs, NULL}, + { "d6", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, 81, 81 }, g_d6_regs, NULL}, + { "d7", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, 82, 82 }, g_d7_regs, NULL}, + { "d8", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, 83, 83 }, g_d8_regs, NULL}, + { "d9", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, 84, 84 }, g_d9_regs, NULL}, + { "d10", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, 85, 85 }, g_d10_regs, NULL}, + { "d11", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, 86, 86 }, g_d11_regs, NULL}, + { "d12", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, 87, 87 }, g_d12_regs, NULL}, + { "d13", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, 88, 88 }, g_d13_regs, NULL}, + { "d14", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, 89, 89 }, g_d14_regs, NULL}, + { "d15", NULL, 8, 0, eEncodingIEEE754, eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, 90, 90 }, g_d15_regs, NULL}, + { "q0", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q0, LLDB_INVALID_REGNUM, 91, 91 }, g_q0_regs, NULL}, + { "q1", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q1, LLDB_INVALID_REGNUM, 92, 92 }, g_q1_regs, NULL}, + { "q2", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q2, LLDB_INVALID_REGNUM, 93, 93 }, g_q2_regs, NULL}, + { "q3", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q3, LLDB_INVALID_REGNUM, 94, 94 }, g_q3_regs, NULL}, + { "q4", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q4, LLDB_INVALID_REGNUM, 95, 95 }, g_q4_regs, NULL}, + { "q5", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q5, LLDB_INVALID_REGNUM, 96, 96 }, g_q5_regs, NULL}, + { "q6", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q6, LLDB_INVALID_REGNUM, 97, 97 }, g_q6_regs, NULL}, + { "q7", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q7, LLDB_INVALID_REGNUM, 98, 98 }, g_q7_regs, NULL}, + { "q8", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q8, LLDB_INVALID_REGNUM, 99, 99 }, g_q8_regs, NULL}, + { "q9", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q9, LLDB_INVALID_REGNUM, 100, 100 }, g_q9_regs, NULL}, + { "q10", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q10, LLDB_INVALID_REGNUM, 101, 101 }, g_q10_regs, NULL}, + { "q11", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q11, LLDB_INVALID_REGNUM, 102, 102 }, g_q11_regs, NULL}, + { "q12", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q12, LLDB_INVALID_REGNUM, 103, 103 }, g_q12_regs, NULL}, + { "q13", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q13, LLDB_INVALID_REGNUM, 104, 104 }, g_q13_regs, NULL}, + { "q14", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q14, LLDB_INVALID_REGNUM, 105, 105 }, g_q14_regs, NULL}, + { "q15", NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM, dwarf_q15, LLDB_INVALID_REGNUM, 106, 106 }, g_q15_regs, NULL} + }; + + static const uint32_t num_registers = llvm::array_lengthof(g_register_infos); + static ConstString gpr_reg_set ("General Purpose Registers"); + static ConstString sfp_reg_set ("Software Floating Point Registers"); + static ConstString vfp_reg_set ("Floating Point Registers"); + size_t i; + if (from_scratch) + { + // Calculate the offsets of the registers + // Note that the layout of the "composite" registers (d0-d15 and q0-q15) which comes after the + // "primordial" registers is important. This enables us to calculate the offset of the composite + // register by using the offset of its first primordial register. For example, to calculate the + // offset of q0, use s0's offset. + if (g_register_infos[2].byte_offset == 0) + { + uint32_t byte_offset = 0; + for (i=0; i<num_registers; ++i) + { + // For primordial registers, increment the byte_offset by the byte_size to arrive at the + // byte_offset for the next register. Otherwise, we have a composite register whose + // offset can be calculated by consulting the offset of its first primordial register. + if (!g_register_infos[i].value_regs) + { + g_register_infos[i].byte_offset = byte_offset; + byte_offset += g_register_infos[i].byte_size; + } + else + { + const uint32_t first_primordial_reg = g_register_infos[i].value_regs[0]; + g_register_infos[i].byte_offset = g_register_infos[first_primordial_reg].byte_offset; + } + } + } + for (i=0; i<num_registers; ++i) + { + ConstString name; + ConstString alt_name; + if (g_register_infos[i].name && g_register_infos[i].name[0]) + name.SetCString(g_register_infos[i].name); + if (g_register_infos[i].alt_name && g_register_infos[i].alt_name[0]) + alt_name.SetCString(g_register_infos[i].alt_name); + + if (i <= 15 || i == 25) + AddRegister (g_register_infos[i], name, alt_name, gpr_reg_set); + else if (i <= 24) + AddRegister (g_register_infos[i], name, alt_name, sfp_reg_set); + else + AddRegister (g_register_infos[i], name, alt_name, vfp_reg_set); + } + } + else + { + // Add composite registers to our primordial registers, then. + const size_t num_composites = llvm::array_lengthof(g_composites); + const size_t num_dynamic_regs = GetNumRegisters(); + const size_t num_common_regs = num_registers - num_composites; + RegisterInfo *g_comp_register_infos = g_register_infos + num_common_regs; + + // First we need to validate that all registers that we already have match the non composite regs. + // If so, then we can add the registers, else we need to bail + bool match = true; + if (num_dynamic_regs == num_common_regs) + { + for (i=0; match && i<num_dynamic_regs; ++i) + { + // Make sure all register names match + if (m_regs[i].name && g_register_infos[i].name) + { + if (strcmp(m_regs[i].name, g_register_infos[i].name)) + { + match = false; + break; + } + } + + // Make sure all register byte sizes match + if (m_regs[i].byte_size != g_register_infos[i].byte_size) + { + match = false; + break; + } + } + } + else + { + // Wrong number of registers. + match = false; + } + // If "match" is true, then we can add extra registers. + if (match) + { + for (i=0; i<num_composites; ++i) + { + ConstString name; + ConstString alt_name; + const uint32_t first_primordial_reg = g_comp_register_infos[i].value_regs[0]; + const char *reg_name = g_register_infos[first_primordial_reg].name; + if (reg_name && reg_name[0]) + { + for (uint32_t j = 0; j < num_dynamic_regs; ++j) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(j); + // Find a matching primordial register info entry. + if (reg_info && reg_info->name && ::strcasecmp(reg_info->name, reg_name) == 0) + { + // The name matches the existing primordial entry. + // Find and assign the offset, and then add this composite register entry. + g_comp_register_infos[i].byte_offset = reg_info->byte_offset; + name.SetCString(g_comp_register_infos[i].name); + AddRegister(g_comp_register_infos[i], name, alt_name, vfp_reg_set); + } + } + } + } + } + } +} diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h new file mode 100644 index 000000000000..3110ddf8edf9 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h @@ -0,0 +1,311 @@ +//===-- GDBRemoteRegisterContext.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_GDBRemoteRegisterContext_h_ +#define lldb_GDBRemoteRegisterContext_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Target/RegisterContext.h" +#include "GDBRemoteCommunicationClient.h" + +class ThreadGDBRemote; +class ProcessGDBRemote; +class StringExtractor; + +class GDBRemoteDynamicRegisterInfo +{ +public: + GDBRemoteDynamicRegisterInfo () : + m_regs (), + m_sets (), + m_set_reg_nums (), + m_reg_names (), + m_reg_alt_names (), + m_set_names (), + m_reg_data_byte_size (0) + { + } + + ~GDBRemoteDynamicRegisterInfo () + { + } + + void + AddRegister (lldb_private::RegisterInfo reg_info, + lldb_private::ConstString ®_name, + lldb_private::ConstString ®_alt_name, + lldb_private::ConstString &set_name) + { + const uint32_t reg_num = (uint32_t)m_regs.size(); + m_reg_names.push_back (reg_name); + m_reg_alt_names.push_back (reg_alt_name); + reg_info.name = reg_name.AsCString(); + assert (reg_info.name); + reg_info.alt_name = reg_alt_name.AsCString(NULL); + uint32_t i; + if (reg_info.value_regs) + { + for (i=0; reg_info.value_regs[i] != LLDB_INVALID_REGNUM; ++i) + m_value_regs_map[reg_num].push_back(reg_info.value_regs[i]); + m_value_regs_map[reg_num].push_back(LLDB_INVALID_REGNUM); + reg_info.value_regs = m_value_regs_map[reg_num].data(); + } + if (reg_info.invalidate_regs) + { + for (i=0; reg_info.invalidate_regs[i] != LLDB_INVALID_REGNUM; ++i) + m_invalidate_regs_map[reg_num].push_back(reg_info.invalidate_regs[i]); + m_invalidate_regs_map[reg_num].push_back(LLDB_INVALID_REGNUM); + reg_info.invalidate_regs = m_invalidate_regs_map[reg_num].data(); + } + m_regs.push_back (reg_info); + uint32_t set = GetRegisterSetIndexByName (set_name); + assert (set < m_sets.size()); + assert (set < m_set_reg_nums.size()); + assert (set < m_set_names.size()); + m_set_reg_nums[set].push_back(reg_num); + size_t end_reg_offset = reg_info.byte_offset + reg_info.byte_size; + if (m_reg_data_byte_size < end_reg_offset) + m_reg_data_byte_size = end_reg_offset; + } + + void + Finalize () + { + for (uint32_t set = 0; set < m_sets.size(); ++set) + { + assert (m_sets.size() == m_set_reg_nums.size()); + m_sets[set].num_registers = m_set_reg_nums[set].size(); + m_sets[set].registers = &m_set_reg_nums[set][0]; + } + } + + size_t + GetNumRegisters() const + { + return m_regs.size(); + } + + size_t + GetNumRegisterSets() const + { + return m_sets.size(); + } + + size_t + GetRegisterDataByteSize() const + { + return m_reg_data_byte_size; + } + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (uint32_t i) const + { + if (i < m_regs.size()) + return &m_regs[i]; + return NULL; + } + + const lldb_private::RegisterSet * + GetRegisterSet (uint32_t i) const + { + if (i < m_sets.size()) + return &m_sets[i]; + return NULL; + } + + uint32_t + GetRegisterSetIndexByName (lldb_private::ConstString &set_name) + { + name_collection::iterator pos, end = m_set_names.end(); + for (pos = m_set_names.begin(); pos != end; ++pos) + { + if (*pos == set_name) + return static_cast<uint32_t>(std::distance (m_set_names.begin(), pos)); + } + + m_set_names.push_back(set_name); + m_set_reg_nums.resize(m_set_reg_nums.size()+1); + lldb_private::RegisterSet new_set = { set_name.AsCString(), NULL, 0, NULL }; + m_sets.push_back (new_set); + return static_cast<uint32_t>(m_sets.size() - 1); + } + + uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) const + { + reg_collection::const_iterator pos, end = m_regs.end(); + for (pos = m_regs.begin(); pos != end; ++pos) + { + if (pos->kinds[kind] == num) + return static_cast<uint32_t>(std::distance (m_regs.begin(), pos)); + } + + return LLDB_INVALID_REGNUM; + } + void + Clear() + { + m_regs.clear(); + m_sets.clear(); + m_set_reg_nums.clear(); + m_reg_names.clear(); + m_reg_alt_names.clear(); + m_set_names.clear(); + } + + void + HardcodeARMRegisters(bool from_scratch); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from GDBRemoteRegisterContext can see and modify these + //------------------------------------------------------------------ + typedef std::vector <lldb_private::RegisterInfo> reg_collection; + typedef std::vector <lldb_private::RegisterSet> set_collection; + typedef std::vector <uint32_t> reg_num_collection; + typedef std::vector <reg_num_collection> set_reg_num_collection; + typedef std::vector <lldb_private::ConstString> name_collection; + typedef std::map<uint32_t, reg_num_collection> reg_to_regs_map; + + reg_collection m_regs; + set_collection m_sets; + set_reg_num_collection m_set_reg_nums; + name_collection m_reg_names; + name_collection m_reg_alt_names; + name_collection m_set_names; + reg_to_regs_map m_value_regs_map; + reg_to_regs_map m_invalidate_regs_map; + size_t m_reg_data_byte_size; // The number of bytes required to store all registers +}; + +class GDBRemoteRegisterContext : public lldb_private::RegisterContext +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + GDBRemoteRegisterContext (ThreadGDBRemote &thread, + uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfo ®_info, + bool read_all_at_once); + + virtual + ~GDBRemoteRegisterContext (); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + InvalidateAllRegisters (); + + virtual size_t + GetRegisterCount (); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex (size_t reg); + + virtual size_t + GetRegisterSetCount (); + + virtual const lldb_private::RegisterSet * + GetRegisterSet (size_t reg_set); + + virtual bool + ReadRegister (const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister (const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp); + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num); + +protected: + friend class ThreadGDBRemote; + + bool + ReadRegisterBytes (const lldb_private::RegisterInfo *reg_info, + lldb_private::DataExtractor &data); + + bool + WriteRegisterBytes (const lldb_private::RegisterInfo *reg_info, + lldb_private::DataExtractor &data, + uint32_t data_offset); + + bool + PrivateSetRegisterValue (uint32_t reg, StringExtractor &response); + + void + SetAllRegisterValid (bool b); + + bool + GetRegisterIsValid (uint32_t reg) const + { +#if defined (LLDB_CONFIGURATION_DEBUG) + assert (reg < m_reg_valid.size()); +#endif + if (reg < m_reg_valid.size()) + return m_reg_valid[reg]; + return false; + } + + void + SetRegisterIsValid (const lldb_private::RegisterInfo *reg_info, bool valid) + { + if (reg_info) + return SetRegisterIsValid (reg_info->kinds[lldb::eRegisterKindLLDB], valid); + } + + void + SetRegisterIsValid (uint32_t reg, bool valid) + { +#if defined (LLDB_CONFIGURATION_DEBUG) + assert (reg < m_reg_valid.size()); +#endif + if (reg < m_reg_valid.size()) + m_reg_valid[reg] = valid; + } + + void + SyncThreadState(lldb_private::Process *process); // Assumes the sequence mutex has already been acquired. + + GDBRemoteDynamicRegisterInfo &m_reg_info; + std::vector<bool> m_reg_valid; + lldb_private::DataExtractor m_reg_data; + bool m_read_all_at_once; + +private: + // Helper function for ReadRegisterBytes(). + bool GetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, + GDBRemoteCommunicationClient &gdb_comm); + // Helper function for WriteRegisterBytes(). + bool SetPrimordialRegister(const lldb_private::RegisterInfo *reg_info, + GDBRemoteCommunicationClient &gdb_comm); + + //------------------------------------------------------------------ + // For GDBRemoteRegisterContext only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (GDBRemoteRegisterContext); +}; + +#endif // lldb_GDBRemoteRegisterContext_h_ diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp new file mode 100644 index 000000000000..d27207f121d3 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -0,0 +1,3291 @@ +//===-- ProcessGDBRemote.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include <errno.h> +#include <spawn.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/mman.h> // for mmap +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> + +// C++ Includes +#include <algorithm> +#include <map> + +// Other libraries and framework includes + +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Value.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Target/ThreadPlanCallFunction.h" +#include "lldb/Utility/PseudoTerminal.h" + +// Project includes +#include "lldb/Host/Host.h" +#include "Plugins/Process/Utility/InferiorCallPOSIX.h" +#include "Plugins/Process/Utility/StopInfoMachException.h" +#include "Plugins/Platform/MacOSX/PlatformRemoteiOS.h" +#include "Utility/StringExtractorGDBRemote.h" +#include "GDBRemoteRegisterContext.h" +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "ThreadGDBRemote.h" + + +namespace lldb +{ + // Provide a function that can easily dump the packet history if we know a + // ProcessGDBRemote * value (which we can get from logs or from debugging). + // We need the function in the lldb namespace so it makes it into the final + // executable since the LLDB shared library only exports stuff in the lldb + // namespace. This allows you to attach with a debugger and call this + // function and get the packet history dumped to a file. + void + DumpProcessGDBRemotePacketHistory (void *p, const char *path) + { + lldb_private::StreamFile strm; + lldb_private::Error error (strm.GetFile().Open(path, lldb_private::File::eOpenOptionWrite | lldb_private::File::eOpenOptionCanCreate)); + if (error.Success()) + ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory (strm); + } +} + +#define DEBUGSERVER_BASENAME "debugserver" +using namespace lldb; +using namespace lldb_private; + + +namespace { + + static PropertyDefinition + g_properties[] = + { + { "packet-timeout" , OptionValue::eTypeUInt64 , true , 1, NULL, NULL, "Specify the default packet timeout in seconds." }, + { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } + }; + + enum + { + ePropertyPacketTimeout + }; + + class PluginProperties : public Properties + { + public: + + static ConstString + GetSettingName () + { + return ProcessGDBRemote::GetPluginNameStatic(); + } + + PluginProperties() : + Properties () + { + m_collection_sp.reset (new OptionValueProperties(GetSettingName())); + m_collection_sp->Initialize(g_properties); + } + + virtual + ~PluginProperties() + { + } + + uint64_t + GetPacketTimeout() + { + const uint32_t idx = ePropertyPacketTimeout; + return m_collection_sp->GetPropertyAtIndexAsUInt64(NULL, idx, g_properties[idx].default_uint_value); + } + }; + + typedef std::shared_ptr<PluginProperties> ProcessKDPPropertiesSP; + + static const ProcessKDPPropertiesSP & + GetGlobalPluginProperties() + { + static ProcessKDPPropertiesSP g_settings_sp; + if (!g_settings_sp) + g_settings_sp.reset (new PluginProperties ()); + return g_settings_sp; + } + +} // anonymous namespace end + +static bool rand_initialized = false; + +// TODO Randomly assigning a port is unsafe. We should get an unused +// ephemeral port from the kernel and make sure we reserve it before passing +// it to debugserver. + +#if defined (__APPLE__) +#define LOW_PORT (IPPORT_RESERVED) +#define HIGH_PORT (IPPORT_HIFIRSTAUTO) +#else +#define LOW_PORT (1024u) +#define HIGH_PORT (49151u) +#endif + +static inline uint16_t +get_random_port () +{ + if (!rand_initialized) + { + time_t seed = time(NULL); + + rand_initialized = true; + srand(seed); + } + return (rand() % (HIGH_PORT - LOW_PORT)) + LOW_PORT; +} + + +lldb_private::ConstString +ProcessGDBRemote::GetPluginNameStatic() +{ + static ConstString g_name("gdb-remote"); + return g_name; +} + +const char * +ProcessGDBRemote::GetPluginDescriptionStatic() +{ + return "GDB Remote protocol based debugging plug-in."; +} + +void +ProcessGDBRemote::Terminate() +{ + PluginManager::UnregisterPlugin (ProcessGDBRemote::CreateInstance); +} + + +lldb::ProcessSP +ProcessGDBRemote::CreateInstance (Target &target, Listener &listener, const FileSpec *crash_file_path) +{ + lldb::ProcessSP process_sp; + if (crash_file_path == NULL) + process_sp.reset (new ProcessGDBRemote (target, listener)); + return process_sp; +} + +bool +ProcessGDBRemote::CanDebug (Target &target, bool plugin_specified_by_name) +{ + if (plugin_specified_by_name) + return true; + + // For now we are just making sure the file exists for a given module + Module *exe_module = target.GetExecutableModulePointer(); + if (exe_module) + { + ObjectFile *exe_objfile = exe_module->GetObjectFile(); + // We can't debug core files... + switch (exe_objfile->GetType()) + { + case ObjectFile::eTypeInvalid: + case ObjectFile::eTypeCoreFile: + case ObjectFile::eTypeDebugInfo: + case ObjectFile::eTypeObjectFile: + case ObjectFile::eTypeSharedLibrary: + case ObjectFile::eTypeStubLibrary: + return false; + case ObjectFile::eTypeExecutable: + case ObjectFile::eTypeDynamicLinker: + case ObjectFile::eTypeUnknown: + break; + } + return exe_module->GetFileSpec().Exists(); + } + // However, if there is no executable module, we return true since we might be preparing to attach. + return true; +} + +//---------------------------------------------------------------------- +// ProcessGDBRemote constructor +//---------------------------------------------------------------------- +ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : + Process (target, listener), + m_flags (0), + m_gdb_comm(false), + m_debugserver_pid (LLDB_INVALID_PROCESS_ID), + m_last_stop_packet (), + m_last_stop_packet_mutex (Mutex::eMutexTypeNormal), + m_register_info (), + m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"), + m_async_thread (LLDB_INVALID_HOST_THREAD), + m_async_thread_state(eAsyncThreadNotStarted), + m_async_thread_state_mutex(Mutex::eMutexTypeRecursive), + m_thread_ids (), + m_continue_c_tids (), + m_continue_C_tids (), + m_continue_s_tids (), + m_continue_S_tids (), + m_dispatch_queue_offsets_addr (LLDB_INVALID_ADDRESS), + m_max_memory_size (512), + m_addr_to_mmap_size (), + m_thread_create_bp_sp (), + m_waiting_for_attach (false), + m_destroy_tried_resuming (false), + m_command_sp () +{ + m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadShouldExit, "async thread should exit"); + m_async_broadcaster.SetEventName (eBroadcastBitAsyncContinue, "async thread continue"); + m_async_broadcaster.SetEventName (eBroadcastBitAsyncThreadDidExit, "async thread did exit"); + const uint64_t timeout_seconds = GetGlobalPluginProperties()->GetPacketTimeout(); + if (timeout_seconds > 0) + m_gdb_comm.SetPacketTimeout(timeout_seconds); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ProcessGDBRemote::~ProcessGDBRemote() +{ + // m_mach_process.UnregisterNotificationCallbacks (this); + Clear(); + // We need to call finalize on the process before destroying ourselves + // to make sure all of the broadcaster cleanup goes as planned. If we + // destruct this class, then Process::~Process() might have problems + // trying to fully destroy the broadcaster. + Finalize(); + + // The general Finalize is going to try to destroy the process and that SHOULD + // shut down the async thread. However, if we don't kill it it will get stranded and + // its connection will go away so when it wakes up it will crash. So kill it for sure here. + StopAsyncThread(); + KillDebugserverProcess(); +} + +//---------------------------------------------------------------------- +// PluginInterface +//---------------------------------------------------------------------- +ConstString +ProcessGDBRemote::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessGDBRemote::GetPluginVersion() +{ + return 1; +} + +void +ProcessGDBRemote::BuildDynamicRegisterInfo (bool force) +{ + if (!force && m_register_info.GetNumRegisters() > 0) + return; + + char packet[128]; + m_register_info.Clear(); + uint32_t reg_offset = 0; + uint32_t reg_num = 0; + for (StringExtractorGDBRemote::ResponseType response_type = StringExtractorGDBRemote::eResponse; + response_type == StringExtractorGDBRemote::eResponse; + ++reg_num) + { + const int packet_len = ::snprintf (packet, sizeof(packet), "qRegisterInfo%x", reg_num); + assert (packet_len < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false)) + { + response_type = response.GetResponseType(); + if (response_type == StringExtractorGDBRemote::eResponse) + { + std::string name; + std::string value; + ConstString reg_name; + ConstString alt_name; + ConstString set_name; + std::vector<uint32_t> value_regs; + std::vector<uint32_t> invalidate_regs; + RegisterInfo reg_info = { NULL, // Name + NULL, // Alt name + 0, // byte size + reg_offset, // offset + eEncodingUint, // encoding + eFormatHex, // formate + { + LLDB_INVALID_REGNUM, // GCC reg num + LLDB_INVALID_REGNUM, // DWARF reg num + LLDB_INVALID_REGNUM, // generic reg num + reg_num, // GDB reg num + reg_num // native register number + }, + NULL, + NULL + }; + + while (response.GetNameColonValue(name, value)) + { + if (name.compare("name") == 0) + { + reg_name.SetCString(value.c_str()); + } + else if (name.compare("alt-name") == 0) + { + alt_name.SetCString(value.c_str()); + } + else if (name.compare("bitsize") == 0) + { + reg_info.byte_size = Args::StringToUInt32(value.c_str(), 0, 0) / CHAR_BIT; + } + else if (name.compare("offset") == 0) + { + uint32_t offset = Args::StringToUInt32(value.c_str(), UINT32_MAX, 0); + if (reg_offset != offset) + { + reg_offset = offset; + } + } + else if (name.compare("encoding") == 0) + { + const Encoding encoding = Args::StringToEncoding (value.c_str()); + if (encoding != eEncodingInvalid) + reg_info.encoding = encoding; + } + else if (name.compare("format") == 0) + { + Format format = eFormatInvalid; + if (Args::StringToFormat (value.c_str(), format, NULL).Success()) + reg_info.format = format; + else if (value.compare("binary") == 0) + reg_info.format = eFormatBinary; + else if (value.compare("decimal") == 0) + reg_info.format = eFormatDecimal; + else if (value.compare("hex") == 0) + reg_info.format = eFormatHex; + else if (value.compare("float") == 0) + reg_info.format = eFormatFloat; + else if (value.compare("vector-sint8") == 0) + reg_info.format = eFormatVectorOfSInt8; + else if (value.compare("vector-uint8") == 0) + reg_info.format = eFormatVectorOfUInt8; + else if (value.compare("vector-sint16") == 0) + reg_info.format = eFormatVectorOfSInt16; + else if (value.compare("vector-uint16") == 0) + reg_info.format = eFormatVectorOfUInt16; + else if (value.compare("vector-sint32") == 0) + reg_info.format = eFormatVectorOfSInt32; + else if (value.compare("vector-uint32") == 0) + reg_info.format = eFormatVectorOfUInt32; + else if (value.compare("vector-float32") == 0) + reg_info.format = eFormatVectorOfFloat32; + else if (value.compare("vector-uint128") == 0) + reg_info.format = eFormatVectorOfUInt128; + } + else if (name.compare("set") == 0) + { + set_name.SetCString(value.c_str()); + } + else if (name.compare("gcc") == 0) + { + reg_info.kinds[eRegisterKindGCC] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); + } + else if (name.compare("dwarf") == 0) + { + reg_info.kinds[eRegisterKindDWARF] = Args::StringToUInt32(value.c_str(), LLDB_INVALID_REGNUM, 0); + } + else if (name.compare("generic") == 0) + { + reg_info.kinds[eRegisterKindGeneric] = Args::StringToGenericRegister (value.c_str()); + } + else if (name.compare("container-regs") == 0) + { + std::pair<llvm::StringRef, llvm::StringRef> value_pair; + value_pair.second = value; + do + { + value_pair = value_pair.second.split(','); + if (!value_pair.first.empty()) + { + uint32_t reg = Args::StringToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, 16); + if (reg != LLDB_INVALID_REGNUM) + value_regs.push_back (reg); + } + } while (!value_pair.second.empty()); + } + else if (name.compare("invalidate-regs") == 0) + { + std::pair<llvm::StringRef, llvm::StringRef> value_pair; + value_pair.second = value; + do + { + value_pair = value_pair.second.split(','); + if (!value_pair.first.empty()) + { + uint32_t reg = Args::StringToUInt32 (value_pair.first.str().c_str(), LLDB_INVALID_REGNUM, 16); + if (reg != LLDB_INVALID_REGNUM) + invalidate_regs.push_back (reg); + } + } while (!value_pair.second.empty()); + } + } + + reg_info.byte_offset = reg_offset; + assert (reg_info.byte_size != 0); + reg_offset += reg_info.byte_size; + if (!value_regs.empty()) + { + value_regs.push_back(LLDB_INVALID_REGNUM); + reg_info.value_regs = value_regs.data(); + } + if (!invalidate_regs.empty()) + { + invalidate_regs.push_back(LLDB_INVALID_REGNUM); + reg_info.invalidate_regs = invalidate_regs.data(); + } + + m_register_info.AddRegister(reg_info, reg_name, alt_name, set_name); + } + } + else + { + break; + } + } + + // We didn't get anything if the accumulated reg_num is zero. See if we are + // debugging ARM and fill with a hard coded register set until we can get an + // updated debugserver down on the devices. + // On the other hand, if the accumulated reg_num is positive, see if we can + // add composite registers to the existing primordial ones. + bool from_scratch = (reg_num == 0); + + const ArchSpec &target_arch = GetTarget().GetArchitecture(); + const ArchSpec &remote_host_arch = m_gdb_comm.GetHostArchitecture(); + const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); + + // Use the process' architecture instead of the host arch, if available + ArchSpec remote_arch; + if (remote_process_arch.IsValid ()) + remote_arch = remote_process_arch; + else + remote_arch = remote_host_arch; + + if (!target_arch.IsValid()) + { + if (remote_arch.IsValid() + && remote_arch.GetMachine() == llvm::Triple::arm + && remote_arch.GetTriple().getVendor() == llvm::Triple::Apple) + m_register_info.HardcodeARMRegisters(from_scratch); + } + else if (target_arch.GetMachine() == llvm::Triple::arm) + { + m_register_info.HardcodeARMRegisters(from_scratch); + } + + // At this point, we can finalize our register info. + m_register_info.Finalize (); +} + +Error +ProcessGDBRemote::WillLaunch (Module* module) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::WillAttachToProcessWithID (lldb::pid_t pid) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) +{ + return WillLaunchOrAttach (); +} + +Error +ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url) +{ + Error error (WillLaunchOrAttach ()); + + if (error.Fail()) + return error; + + error = ConnectToDebugserver (remote_url); + + if (error.Fail()) + return error; + StartAsyncThread (); + + lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID (); + if (pid == LLDB_INVALID_PROCESS_ID) + { + // We don't have a valid process ID, so note that we are connected + // and could now request to launch or attach, or get remote process + // listings... + SetPrivateState (eStateConnected); + } + else + { + // We have a valid process + SetID (pid); + GetThreadList(); + if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false)) + { + const StateType state = SetThreadStopInfo (m_last_stop_packet); + if (state == eStateStopped) + { + SetPrivateState (state); + } + else + error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but state was not stopped: %s", pid, remote_url, StateAsCString (state)); + } + else + error.SetErrorStringWithFormat ("Process %" PRIu64 " was reported after connecting to '%s', but no stop reply packet was received", pid, remote_url); + } + + if (error.Success() + && !GetTarget().GetArchitecture().IsValid() + && m_gdb_comm.GetHostArchitecture().IsValid()) + { + // Prefer the *process'* architecture over that of the *host*, if available. + if (m_gdb_comm.GetProcessArchitecture().IsValid()) + GetTarget().SetArchitecture(m_gdb_comm.GetProcessArchitecture()); + else + GetTarget().SetArchitecture(m_gdb_comm.GetHostArchitecture()); + } + + return error; +} + +Error +ProcessGDBRemote::WillLaunchOrAttach () +{ + Error error; + m_stdio_communication.Clear (); + return error; +} + +//---------------------------------------------------------------------- +// Process Control +//---------------------------------------------------------------------- +Error +ProcessGDBRemote::DoLaunch (Module *exe_module, const ProcessLaunchInfo &launch_info) +{ + Error error; + + uint32_t launch_flags = launch_info.GetFlags().Get(); + const char *stdin_path = NULL; + const char *stdout_path = NULL; + const char *stderr_path = NULL; + const char *working_dir = launch_info.GetWorkingDirectory(); + + const ProcessLaunchInfo::FileAction *file_action; + file_action = launch_info.GetFileActionForFD (STDIN_FILENO); + if (file_action) + { + if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) + stdin_path = file_action->GetPath(); + } + file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); + if (file_action) + { + if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) + stdout_path = file_action->GetPath(); + } + file_action = launch_info.GetFileActionForFD (STDERR_FILENO); + if (file_action) + { + if (file_action->GetAction () == ProcessLaunchInfo::FileAction::eFileActionOpen) + stderr_path = file_action->GetPath(); + } + + // ::LogSetBitMask (GDBR_LOG_DEFAULT); + // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); + // ::LogSetLogFile ("/dev/stdout"); + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + + ObjectFile * object_file = exe_module->GetObjectFile(); + if (object_file) + { + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + char connect_url[128]; + snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port); + + // Make sure we aren't already connected? + if (!m_gdb_comm.IsConnected()) + { + error = StartDebugserverProcess (host_port, launch_info); + if (error.Fail()) + { + if (log) + log->Printf("failed to start debugserver process: %s", error.AsCString()); + return error; + } + + error = ConnectToDebugserver (connect_url); + } + + if (error.Success()) + { + lldb_utility::PseudoTerminal pty; + const bool disable_stdio = (launch_flags & eLaunchFlagDisableSTDIO) != 0; + + // If the debugserver is local and we aren't disabling STDIO, lets use + // a pseudo terminal to instead of relying on the 'O' packets for stdio + // since 'O' packets can really slow down debugging if the inferior + // does a lot of output. + PlatformSP platform_sp (m_target.GetPlatform()); + if (platform_sp && platform_sp->IsHost() && !disable_stdio) + { + const char *slave_name = NULL; + if (stdin_path == NULL || stdout_path == NULL || stderr_path == NULL) + { + if (pty.OpenFirstAvailableMaster(O_RDWR|O_NOCTTY, NULL, 0)) + slave_name = pty.GetSlaveName (NULL, 0); + } + if (stdin_path == NULL) + stdin_path = slave_name; + + if (stdout_path == NULL) + stdout_path = slave_name; + + if (stderr_path == NULL) + stderr_path = slave_name; + } + + // Set STDIN to /dev/null if we want STDIO disabled or if either + // STDOUT or STDERR have been set to something and STDIN hasn't + if (disable_stdio || (stdin_path == NULL && (stdout_path || stderr_path))) + stdin_path = "/dev/null"; + + // Set STDOUT to /dev/null if we want STDIO disabled or if either + // STDIN or STDERR have been set to something and STDOUT hasn't + if (disable_stdio || (stdout_path == NULL && (stdin_path || stderr_path))) + stdout_path = "/dev/null"; + + // Set STDERR to /dev/null if we want STDIO disabled or if either + // STDIN or STDOUT have been set to something and STDERR hasn't + if (disable_stdio || (stderr_path == NULL && (stdin_path || stdout_path))) + stderr_path = "/dev/null"; + + if (stdin_path) + m_gdb_comm.SetSTDIN (stdin_path); + if (stdout_path) + m_gdb_comm.SetSTDOUT (stdout_path); + if (stderr_path) + m_gdb_comm.SetSTDERR (stderr_path); + + m_gdb_comm.SetDisableASLR (launch_flags & eLaunchFlagDisableASLR); + + m_gdb_comm.SendLaunchArchPacket (m_target.GetArchitecture().GetArchitectureName()); + + if (working_dir && working_dir[0]) + { + m_gdb_comm.SetWorkingDir (working_dir); + } + + // Send the environment and the program + arguments after we connect + const Args &environment = launch_info.GetEnvironmentEntries(); + if (environment.GetArgumentCount()) + { + size_t num_environment_entries = environment.GetArgumentCount(); + for (size_t i=0; i<num_environment_entries; ++i) + { + const char *env_entry = environment.GetArgumentAtIndex(i); + if (env_entry == NULL || m_gdb_comm.SendEnvironmentPacket(env_entry) != 0) + break; + } + } + + const uint32_t old_packet_timeout = m_gdb_comm.SetPacketTimeout (10); + int arg_packet_err = m_gdb_comm.SendArgumentsPacket (launch_info.GetArguments().GetConstArgumentVector()); + if (arg_packet_err == 0) + { + std::string error_str; + if (m_gdb_comm.GetLaunchSuccess (error_str)) + { + SetID (m_gdb_comm.GetCurrentProcessID ()); + } + else + { + error.SetErrorString (error_str.c_str()); + } + } + else + { + error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err); + } + + m_gdb_comm.SetPacketTimeout (old_packet_timeout); + + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + if (log) + log->Printf("failed to connect to debugserver: %s", error.AsCString()); + KillDebugserverProcess (); + return error; + } + + if (m_gdb_comm.SendPacketAndWaitForResponse("?", 1, m_last_stop_packet, false)) + { + SetPrivateState (SetThreadStopInfo (m_last_stop_packet)); + + if (!disable_stdio) + { + if (pty.GetMasterFileDescriptor() != lldb_utility::PseudoTerminal::invalid_fd) + SetSTDIOFileDescriptor (pty.ReleaseMasterFileDescriptor()); + } + } + } + else + { + if (log) + log->Printf("failed to connect to debugserver: %s", error.AsCString()); + } + } + else + { + // Set our user ID to an invalid process ID. + SetID(LLDB_INVALID_PROCESS_ID); + error.SetErrorStringWithFormat ("failed to get object file from '%s' for arch %s", + exe_module->GetFileSpec().GetFilename().AsCString(), + exe_module->GetArchitecture().GetArchitectureName()); + } + return error; + +} + + +Error +ProcessGDBRemote::ConnectToDebugserver (const char *connect_url) +{ + Error error; + // Sleep and wait a bit for debugserver to start to listen... + std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor()); + if (conn_ap.get()) + { + const uint32_t max_retry_count = 50; + uint32_t retry_count = 0; + while (!m_gdb_comm.IsConnected()) + { + if (conn_ap->Connect(connect_url, &error) == eConnectionStatusSuccess) + { + m_gdb_comm.SetConnection (conn_ap.release()); + break; + } + else if (error.WasInterrupted()) + { + // If we were interrupted, don't keep retrying. + break; + } + + retry_count++; + + if (retry_count >= max_retry_count) + break; + + usleep (100000); + } + } + + if (!m_gdb_comm.IsConnected()) + { + if (error.Success()) + error.SetErrorString("not connected to remote gdb server"); + return error; + } + + // We always seem to be able to open a connection to a local port + // so we need to make sure we can then send data to it. If we can't + // then we aren't actually connected to anything, so try and do the + // handshake with the remote GDB server and make sure that goes + // alright. + if (!m_gdb_comm.HandshakeWithServer (NULL)) + { + m_gdb_comm.Disconnect(); + if (error.Success()) + error.SetErrorString("not connected to remote gdb server"); + return error; + } + m_gdb_comm.ResetDiscoverableSettings(); + m_gdb_comm.QueryNoAckModeSupported (); + m_gdb_comm.GetThreadSuffixSupported (); + m_gdb_comm.GetListThreadsInStopReplySupported (); + m_gdb_comm.GetHostInfo (); + m_gdb_comm.GetVContSupported ('c'); + m_gdb_comm.GetVAttachOrWaitSupported(); + + size_t num_cmds = GetExtraStartupCommands().GetArgumentCount(); + for (size_t idx = 0; idx < num_cmds; idx++) + { + StringExtractorGDBRemote response; + m_gdb_comm.SendPacketAndWaitForResponse (GetExtraStartupCommands().GetArgumentAtIndex(idx), response, false); + } + return error; +} + +void +ProcessGDBRemote::DidLaunchOrAttach () +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + if (log) + log->Printf ("ProcessGDBRemote::DidLaunch()"); + if (GetID() != LLDB_INVALID_PROCESS_ID) + { + m_dispatch_queue_offsets_addr = LLDB_INVALID_ADDRESS; + + BuildDynamicRegisterInfo (false); + + // See if the GDB server supports the qHostInfo information + + ArchSpec gdb_remote_arch = m_gdb_comm.GetHostArchitecture(); + + // See if the GDB server supports the qProcessInfo packet, if so + // prefer that over the Host information as it will be more specific + // to our process. + + if (m_gdb_comm.GetProcessArchitecture().IsValid()) + gdb_remote_arch = m_gdb_comm.GetProcessArchitecture(); + + if (gdb_remote_arch.IsValid()) + { + ArchSpec &target_arch = GetTarget().GetArchitecture(); + + if (target_arch.IsValid()) + { + // If the remote host is ARM and we have apple as the vendor, then + // ARM executables and shared libraries can have mixed ARM architectures. + // You can have an armv6 executable, and if the host is armv7, then the + // system will load the best possible architecture for all shared libraries + // it has, so we really need to take the remote host architecture as our + // defacto architecture in this case. + + if (gdb_remote_arch.GetMachine() == llvm::Triple::arm && + gdb_remote_arch.GetTriple().getVendor() == llvm::Triple::Apple) + { + target_arch = gdb_remote_arch; + } + else + { + // Fill in what is missing in the triple + const llvm::Triple &remote_triple = gdb_remote_arch.GetTriple(); + llvm::Triple &target_triple = target_arch.GetTriple(); + if (target_triple.getVendorName().size() == 0) + { + target_triple.setVendor (remote_triple.getVendor()); + + if (target_triple.getOSName().size() == 0) + { + target_triple.setOS (remote_triple.getOS()); + + if (target_triple.getEnvironmentName().size() == 0) + target_triple.setEnvironment (remote_triple.getEnvironment()); + } + } + } + } + else + { + // The target doesn't have a valid architecture yet, set it from + // the architecture we got from the remote GDB server + target_arch = gdb_remote_arch; + } + } + } +} + +void +ProcessGDBRemote::DidLaunch () +{ + DidLaunchOrAttach (); +} + +Error +ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid) +{ + ProcessAttachInfo attach_info; + return DoAttachToProcessWithID(attach_pid, attach_info); +} + +Error +ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) +{ + Error error; + // Clear out and clean up from any current state + Clear(); + if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + // Make sure we aren't already connected? + if (!m_gdb_comm.IsConnected()) + { + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + char connect_url[128]; + snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port); + + error = StartDebugserverProcess (host_port, attach_info); + + if (error.Fail()) + { + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "unable to launch " DEBUGSERVER_BASENAME; + + SetExitStatus (-1, error_string); + } + else + { + error = ConnectToDebugserver (connect_url); + } + } + + if (error.Success()) + { + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); + SetID (attach_pid); + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet, packet_len)); + } + } + return error; +} + +size_t +ProcessGDBRemote::AttachInputReaderCallback +( + void *baton, + InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + if (notification == eInputReaderGotToken) + { + ProcessGDBRemote *gdb_process = (ProcessGDBRemote *)baton; + if (gdb_process->m_waiting_for_attach) + gdb_process->m_waiting_for_attach = false; + reader->SetIsDone(true); + return 1; + } + return 0; +} + +Error +ProcessGDBRemote::DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const ProcessAttachInfo &attach_info) +{ + Error error; + // Clear out and clean up from any current state + Clear(); + + if (process_name && process_name[0]) + { + // Make sure we aren't already connected? + if (!m_gdb_comm.IsConnected()) + { + char host_port[128]; + snprintf (host_port, sizeof(host_port), "localhost:%u", get_random_port ()); + char connect_url[128]; + snprintf (connect_url, sizeof(connect_url), "connect://%s", host_port); + + error = StartDebugserverProcess (host_port, attach_info); + if (error.Fail()) + { + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "unable to launch " DEBUGSERVER_BASENAME; + + SetExitStatus (-1, error_string); + } + else + { + error = ConnectToDebugserver (connect_url); + } + } + + if (error.Success()) + { + StreamString packet; + + if (wait_for_launch) + { + if (!m_gdb_comm.GetVAttachOrWaitSupported()) + { + packet.PutCString ("vAttachWait"); + } + else + { + if (attach_info.GetIgnoreExisting()) + packet.PutCString("vAttachWait"); + else + packet.PutCString ("vAttachOrWait"); + } + } + else + packet.PutCString("vAttachName"); + packet.PutChar(';'); + packet.PutBytesAsRawHex8(process_name, strlen(process_name), lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); + + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet.GetData(), packet.GetSize())); + + } + } + return error; +} + + +void +ProcessGDBRemote::DidAttach () +{ + DidLaunchOrAttach (); +} + + +Error +ProcessGDBRemote::WillResume () +{ + m_continue_c_tids.clear(); + m_continue_C_tids.clear(); + m_continue_s_tids.clear(); + m_continue_S_tids.clear(); + return Error(); +} + +Error +ProcessGDBRemote::DoResume () +{ + Error error; + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + if (log) + log->Printf ("ProcessGDBRemote::Resume()"); + + Listener listener ("gdb-remote.resume-packet-sent"); + if (listener.StartListeningForEvents (&m_gdb_comm, GDBRemoteCommunication::eBroadcastBitRunPacketSent)) + { + listener.StartListeningForEvents (&m_async_broadcaster, ProcessGDBRemote::eBroadcastBitAsyncThreadDidExit); + + const size_t num_threads = GetThreadList().GetSize(); + + StreamString continue_packet; + bool continue_packet_error = false; + if (m_gdb_comm.HasAnyVContSupport ()) + { + if (m_continue_c_tids.size() == num_threads) + { + // All threads are continuing, just send a "c" packet + continue_packet.PutCString ("c"); + } + else + { + continue_packet.PutCString ("vCont"); + + if (!m_continue_c_tids.empty()) + { + if (m_gdb_comm.GetVContSupported ('c')) + { + for (tid_collection::const_iterator t_pos = m_continue_c_tids.begin(), t_end = m_continue_c_tids.end(); t_pos != t_end; ++t_pos) + continue_packet.Printf(";c:%4.4" PRIx64, *t_pos); + } + else + continue_packet_error = true; + } + + if (!continue_packet_error && !m_continue_C_tids.empty()) + { + if (m_gdb_comm.GetVContSupported ('C')) + { + for (tid_sig_collection::const_iterator s_pos = m_continue_C_tids.begin(), s_end = m_continue_C_tids.end(); s_pos != s_end; ++s_pos) + continue_packet.Printf(";C%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); + } + else + continue_packet_error = true; + } + + if (!continue_packet_error && !m_continue_s_tids.empty()) + { + if (m_gdb_comm.GetVContSupported ('s')) + { + for (tid_collection::const_iterator t_pos = m_continue_s_tids.begin(), t_end = m_continue_s_tids.end(); t_pos != t_end; ++t_pos) + continue_packet.Printf(";s:%4.4" PRIx64, *t_pos); + } + else + continue_packet_error = true; + } + + if (!continue_packet_error && !m_continue_S_tids.empty()) + { + if (m_gdb_comm.GetVContSupported ('S')) + { + for (tid_sig_collection::const_iterator s_pos = m_continue_S_tids.begin(), s_end = m_continue_S_tids.end(); s_pos != s_end; ++s_pos) + continue_packet.Printf(";S%2.2x:%4.4" PRIx64, s_pos->second, s_pos->first); + } + else + continue_packet_error = true; + } + + if (continue_packet_error) + continue_packet.GetString().clear(); + } + } + else + continue_packet_error = true; + + if (continue_packet_error) + { + // Either no vCont support, or we tried to use part of the vCont + // packet that wasn't supported by the remote GDB server. + // We need to try and make a simple packet that can do our continue + const size_t num_continue_c_tids = m_continue_c_tids.size(); + const size_t num_continue_C_tids = m_continue_C_tids.size(); + const size_t num_continue_s_tids = m_continue_s_tids.size(); + const size_t num_continue_S_tids = m_continue_S_tids.size(); + if (num_continue_c_tids > 0) + { + if (num_continue_c_tids == num_threads) + { + // All threads are resuming... + m_gdb_comm.SetCurrentThreadForRun (-1); + continue_packet.PutChar ('c'); + continue_packet_error = false; + } + else if (num_continue_c_tids == 1 && + num_continue_C_tids == 0 && + num_continue_s_tids == 0 && + num_continue_S_tids == 0 ) + { + // Only one thread is continuing + m_gdb_comm.SetCurrentThreadForRun (m_continue_c_tids.front()); + continue_packet.PutChar ('c'); + continue_packet_error = false; + } + } + + if (continue_packet_error && num_continue_C_tids > 0) + { + if ((num_continue_C_tids + num_continue_c_tids) == num_threads && + num_continue_C_tids > 0 && + num_continue_s_tids == 0 && + num_continue_S_tids == 0 ) + { + const int continue_signo = m_continue_C_tids.front().second; + // Only one thread is continuing + if (num_continue_C_tids > 1) + { + // More that one thread with a signal, yet we don't have + // vCont support and we are being asked to resume each + // thread with a signal, we need to make sure they are + // all the same signal, or we can't issue the continue + // accurately with the current support... + if (num_continue_C_tids > 1) + { + continue_packet_error = false; + for (size_t i=1; i<m_continue_C_tids.size(); ++i) + { + if (m_continue_C_tids[i].second != continue_signo) + continue_packet_error = true; + } + } + if (!continue_packet_error) + m_gdb_comm.SetCurrentThreadForRun (-1); + } + else + { + // Set the continue thread ID + continue_packet_error = false; + m_gdb_comm.SetCurrentThreadForRun (m_continue_C_tids.front().first); + } + if (!continue_packet_error) + { + // Add threads continuing with the same signo... + continue_packet.Printf("C%2.2x", continue_signo); + } + } + } + + if (continue_packet_error && num_continue_s_tids > 0) + { + if (num_continue_s_tids == num_threads) + { + // All threads are resuming... + m_gdb_comm.SetCurrentThreadForRun (-1); + continue_packet.PutChar ('s'); + continue_packet_error = false; + } + else if (num_continue_c_tids == 0 && + num_continue_C_tids == 0 && + num_continue_s_tids == 1 && + num_continue_S_tids == 0 ) + { + // Only one thread is stepping + m_gdb_comm.SetCurrentThreadForRun (m_continue_s_tids.front()); + continue_packet.PutChar ('s'); + continue_packet_error = false; + } + } + + if (!continue_packet_error && num_continue_S_tids > 0) + { + if (num_continue_S_tids == num_threads) + { + const int step_signo = m_continue_S_tids.front().second; + // Are all threads trying to step with the same signal? + continue_packet_error = false; + if (num_continue_S_tids > 1) + { + for (size_t i=1; i<num_threads; ++i) + { + if (m_continue_S_tids[i].second != step_signo) + continue_packet_error = true; + } + } + if (!continue_packet_error) + { + // Add threads stepping with the same signo... + m_gdb_comm.SetCurrentThreadForRun (-1); + continue_packet.Printf("S%2.2x", step_signo); + } + } + else if (num_continue_c_tids == 0 && + num_continue_C_tids == 0 && + num_continue_s_tids == 0 && + num_continue_S_tids == 1 ) + { + // Only one thread is stepping with signal + m_gdb_comm.SetCurrentThreadForRun (m_continue_S_tids.front().first); + continue_packet.Printf("S%2.2x", m_continue_S_tids.front().second); + continue_packet_error = false; + } + } + } + + if (continue_packet_error) + { + error.SetErrorString ("can't make continue packet for this resume"); + } + else + { + EventSP event_sp; + TimeValue timeout; + timeout = TimeValue::Now(); + timeout.OffsetWithSeconds (5); + if (!IS_VALID_LLDB_HOST_THREAD(m_async_thread)) + { + error.SetErrorString ("Trying to resume but the async thread is dead."); + if (log) + log->Printf ("ProcessGDBRemote::DoResume: Trying to resume but the async thread is dead."); + return error; + } + + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (continue_packet.GetData(), continue_packet.GetSize())); + + if (listener.WaitForEvent (&timeout, event_sp) == false) + { + error.SetErrorString("Resume timed out."); + if (log) + log->Printf ("ProcessGDBRemote::DoResume: Resume timed out."); + } + else if (event_sp->BroadcasterIs (&m_async_broadcaster)) + { + error.SetErrorString ("Broadcast continue, but the async thread was killed before we got an ack back."); + if (log) + log->Printf ("ProcessGDBRemote::DoResume: Broadcast continue, but the async thread was killed before we got an ack back."); + return error; + } + } + } + + return error; +} + +void +ProcessGDBRemote::ClearThreadIDList () +{ + Mutex::Locker locker(m_thread_list_real.GetMutex()); + m_thread_ids.clear(); +} + +bool +ProcessGDBRemote::UpdateThreadIDList () +{ + Mutex::Locker locker(m_thread_list_real.GetMutex()); + bool sequence_mutex_unavailable = false; + m_gdb_comm.GetCurrentThreadIDs (m_thread_ids, sequence_mutex_unavailable); + if (sequence_mutex_unavailable) + { + return false; // We just didn't get the list + } + return true; +} + +bool +ProcessGDBRemote::UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + // locker will keep a mutex locked until it goes out of scope + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); + if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) + log->Printf ("ProcessGDBRemote::%s (pid = %" PRIu64 ")", __FUNCTION__, GetID()); + + size_t num_thread_ids = m_thread_ids.size(); + // The "m_thread_ids" thread ID list should always be updated after each stop + // reply packet, but in case it isn't, update it here. + if (num_thread_ids == 0) + { + if (!UpdateThreadIDList ()) + return false; + num_thread_ids = m_thread_ids.size(); + } + + ThreadList old_thread_list_copy(old_thread_list); + if (num_thread_ids > 0) + { + for (size_t i=0; i<num_thread_ids; ++i) + { + tid_t tid = m_thread_ids[i]; + ThreadSP thread_sp (old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); + if (!thread_sp) + { + thread_sp.reset (new ThreadGDBRemote (*this, tid)); + if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) + log->Printf( + "ProcessGDBRemote::%s Making new thread: %p for thread ID: 0x%" PRIx64 ".\n", + __FUNCTION__, + thread_sp.get(), + thread_sp->GetID()); + } + else + { + if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) + log->Printf( + "ProcessGDBRemote::%s Found old thread: %p for thread ID: 0x%" PRIx64 ".\n", + __FUNCTION__, + thread_sp.get(), + thread_sp->GetID()); + } + new_thread_list.AddThread(thread_sp); + } + } + + // Whatever that is left in old_thread_list_copy are not + // present in new_thread_list. Remove non-existent threads from internal id table. + size_t old_num_thread_ids = old_thread_list_copy.GetSize(false); + for (size_t i=0; i<old_num_thread_ids; i++) + { + ThreadSP old_thread_sp(old_thread_list_copy.GetThreadAtIndex (i, false)); + if (old_thread_sp) + { + lldb::tid_t old_thread_id = old_thread_sp->GetProtocolID(); + m_thread_id_to_index_id_map.erase(old_thread_id); + } + } + + return true; +} + + +StateType +ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) +{ + stop_packet.SetFilePos (0); + const char stop_type = stop_packet.GetChar(); + switch (stop_type) + { + case 'T': + case 'S': + { + // This is a bit of a hack, but is is required. If we did exec, we + // need to clear our thread lists and also know to rebuild our dynamic + // register info before we lookup and threads and populate the expedited + // register values so we need to know this right away so we can cleanup + // and update our registers. + const uint32_t stop_id = GetStopID(); + if (stop_id == 0) + { + // Our first stop, make sure we have a process ID, and also make + // sure we know about our registers + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID (); + if (pid != LLDB_INVALID_PROCESS_ID) + SetID (pid); + } + BuildDynamicRegisterInfo (true); + } + // Stop with signal and thread info + const uint8_t signo = stop_packet.GetHexU8(); + std::string name; + std::string value; + std::string thread_name; + std::string reason; + std::string description; + uint32_t exc_type = 0; + std::vector<addr_t> exc_data; + addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS; + ThreadSP thread_sp; + ThreadGDBRemote *gdb_thread = NULL; + + while (stop_packet.GetNameColonValue(name, value)) + { + if (name.compare("metype") == 0) + { + // exception type in big endian hex + exc_type = Args::StringToUInt32 (value.c_str(), 0, 16); + } + else if (name.compare("medata") == 0) + { + // exception data in big endian hex + exc_data.push_back(Args::StringToUInt64 (value.c_str(), 0, 16)); + } + else if (name.compare("thread") == 0) + { + // thread in big endian hex + lldb::tid_t tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); + // m_thread_list_real does have its own mutex, but we need to + // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...) + // and the m_thread_list_real.AddThread(...) so it doesn't change on us + Mutex::Locker locker (m_thread_list_real.GetMutex ()); + thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false); + + if (!thread_sp) + { + // Create the thread if we need to + thread_sp.reset (new ThreadGDBRemote (*this, tid)); + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD)); + if (log && log->GetMask().Test(GDBR_LOG_VERBOSE)) + log->Printf ("ProcessGDBRemote::%s Adding new thread: %p for thread ID: 0x%" PRIx64 ".\n", + __FUNCTION__, + thread_sp.get(), + thread_sp->GetID()); + + m_thread_list_real.AddThread(thread_sp); + } + gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get()); + + } + else if (name.compare("threads") == 0) + { + Mutex::Locker locker(m_thread_list_real.GetMutex()); + m_thread_ids.clear(); + // A comma separated list of all threads in the current + // process that includes the thread for this stop reply + // packet + size_t comma_pos; + lldb::tid_t tid; + while ((comma_pos = value.find(',')) != std::string::npos) + { + value[comma_pos] = '\0'; + // thread in big endian hex + tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); + if (tid != LLDB_INVALID_THREAD_ID) + m_thread_ids.push_back (tid); + value.erase(0, comma_pos + 1); + + } + tid = Args::StringToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16); + if (tid != LLDB_INVALID_THREAD_ID) + m_thread_ids.push_back (tid); + } + else if (name.compare("hexname") == 0) + { + StringExtractor name_extractor; + // Swap "value" over into "name_extractor" + name_extractor.GetStringRef().swap(value); + // Now convert the HEX bytes into a string value + name_extractor.GetHexByteString (value); + thread_name.swap (value); + } + else if (name.compare("name") == 0) + { + thread_name.swap (value); + } + else if (name.compare("qaddr") == 0) + { + thread_dispatch_qaddr = Args::StringToUInt64 (value.c_str(), 0, 16); + } + else if (name.compare("reason") == 0) + { + reason.swap(value); + } + else if (name.compare("description") == 0) + { + StringExtractor desc_extractor; + // Swap "value" over into "name_extractor" + desc_extractor.GetStringRef().swap(value); + // Now convert the HEX bytes into a string value + desc_extractor.GetHexByteString (thread_name); + } + else if (name.size() == 2 && ::isxdigit(name[0]) && ::isxdigit(name[1])) + { + // We have a register number that contains an expedited + // register value. Lets supply this register to our thread + // so it won't have to go and read it. + if (gdb_thread) + { + uint32_t reg = Args::StringToUInt32 (name.c_str(), UINT32_MAX, 16); + + if (reg != UINT32_MAX) + { + StringExtractor reg_value_extractor; + // Swap "value" over into "reg_value_extractor" + reg_value_extractor.GetStringRef().swap(value); + if (!gdb_thread->PrivateSetRegisterValue (reg, reg_value_extractor)) + { + Host::SetCrashDescriptionWithFormat("Setting thread register '%s' (decoded to %u (0x%x)) with value '%s' for stop packet: '%s'", + name.c_str(), + reg, + reg, + reg_value_extractor.GetStringRef().c_str(), + stop_packet.GetStringRef().c_str()); + } + } + } + } + } + + if (thread_sp) + { + // Clear the stop info just in case we don't set it to anything + thread_sp->SetStopInfo (StopInfoSP()); + + gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr); + gdb_thread->SetName (thread_name.empty() ? NULL : thread_name.c_str()); + if (exc_type != 0) + { + const size_t exc_data_size = exc_data.size(); + + thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp, + exc_type, + exc_data_size, + exc_data_size >= 1 ? exc_data[0] : 0, + exc_data_size >= 2 ? exc_data[1] : 0, + exc_data_size >= 3 ? exc_data[2] : 0)); + } + else + { + bool handled = false; + bool did_exec = false; + if (!reason.empty()) + { + if (reason.compare("trace") == 0) + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); + handled = true; + } + else if (reason.compare("breakpoint") == 0) + { + addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp) + { + // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, + // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that + // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. + handled = true; + if (bp_site_sp->ValidForThisThread (thread_sp.get())) + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); + } + else + { + StopInfoSP invalid_stop_info_sp; + thread_sp->SetStopInfo (invalid_stop_info_sp); + } + } + + } + else if (reason.compare("trap") == 0) + { + // Let the trap just use the standard signal stop reason below... + } + else if (reason.compare("watchpoint") == 0) + { + break_id_t watch_id = LLDB_INVALID_WATCH_ID; + // TODO: locate the watchpoint somehow... + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id)); + handled = true; + } + else if (reason.compare("exception") == 0) + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str())); + handled = true; + } + else if (reason.compare("exec") == 0) + { + did_exec = true; + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp)); + handled = true; + } + } + + if (signo && did_exec == false) + { + if (signo == SIGTRAP) + { + // Currently we are going to assume SIGTRAP means we are either + // hitting a breakpoint or hardware single stepping. + handled = true; + addr_t pc = thread_sp->GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc); + + if (bp_site_sp) + { + // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, + // we can just report no reason. We don't need to worry about stepping over the breakpoint here, that + // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc. + if (bp_site_sp->ValidForThisThread (thread_sp.get())) + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID())); + } + else + { + StopInfoSP invalid_stop_info_sp; + thread_sp->SetStopInfo (invalid_stop_info_sp); + } + } + else + { + // If we were stepping then assume the stop was the result of the trace. If we were + // not stepping then report the SIGTRAP. + // FIXME: We are still missing the case where we single step over a trap instruction. + if (thread_sp->GetTemporaryResumeState() == eStateStepping) + thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp)); + else + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo)); + } + } + if (!handled) + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo)); + } + + if (!description.empty()) + { + lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ()); + if (stop_info_sp) + { + stop_info_sp->SetDescription (description.c_str()); + } + else + { + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str())); + } + } + } + } + return eStateStopped; + } + break; + + case 'W': + // process exited + return eStateExited; + + default: + break; + } + return eStateInvalid; +} + +void +ProcessGDBRemote::RefreshStateAfterStop () +{ + Mutex::Locker locker(m_thread_list_real.GetMutex()); + m_thread_ids.clear(); + // Set the thread stop info. It might have a "threads" key whose value is + // a list of all thread IDs in the current process, so m_thread_ids might + // get set. + SetThreadStopInfo (m_last_stop_packet); + // Check to see if SetThreadStopInfo() filled in m_thread_ids? + if (m_thread_ids.empty()) + { + // No, we need to fetch the thread list manually + UpdateThreadIDList(); + } + + // Let all threads recover from stopping and do any clean up based + // on the previous thread state (if any). + m_thread_list_real.RefreshStateAfterStop(); + +} + +Error +ProcessGDBRemote::DoHalt (bool &caused_stop) +{ + Error error; + + bool timed_out = false; + Mutex::Locker locker; + + if (m_public_state.GetValue() == eStateAttaching) + { + // We are being asked to halt during an attach. We need to just close + // our file handle and debugserver will go away, and we can be done... + m_gdb_comm.Disconnect(); + } + else + { + if (!m_gdb_comm.SendInterrupt (locker, 2, timed_out)) + { + if (timed_out) + error.SetErrorString("timed out sending interrupt packet"); + else + error.SetErrorString("unknown error sending interrupt packet"); + } + + caused_stop = m_gdb_comm.GetInterruptWasSent (); + } + return error; +} + +Error +ProcessGDBRemote::DoDetach(bool keep_stopped) +{ + Error error; + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + if (log) + log->Printf ("ProcessGDBRemote::DoDetach(keep_stopped: %i)", keep_stopped); + + DisableAllBreakpointSites (); + + m_thread_list.DiscardThreadPlans(); + + error = m_gdb_comm.Detach (keep_stopped); + if (log) + { + if (error.Success()) + log->PutCString ("ProcessGDBRemote::DoDetach() detach packet sent successfully"); + else + log->Printf ("ProcessGDBRemote::DoDetach() detach packet send failed: %s", error.AsCString() ? error.AsCString() : "<unknown error>"); + } + + if (!error.Success()) + return error; + + // Sleep for one second to let the process get all detached... + StopAsyncThread (); + + SetPrivateState (eStateDetached); + ResumePrivateStateThread(); + + //KillDebugserverProcess (); + return error; +} + + +Error +ProcessGDBRemote::DoDestroy () +{ + Error error; + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + if (log) + log->Printf ("ProcessGDBRemote::DoDestroy()"); + + // There is a bug in older iOS debugservers where they don't shut down the process + // they are debugging properly. If the process is sitting at a breakpoint or an exception, + // this can cause problems with restarting. So we check to see if any of our threads are stopped + // at a breakpoint, and if so we remove all the breakpoints, resume the process, and THEN + // destroy it again. + // + // Note, we don't have a good way to test the version of debugserver, but I happen to know that + // the set of all the iOS debugservers which don't support GetThreadSuffixSupported() and that of + // the debugservers with this bug are equal. There really should be a better way to test this! + // + // We also use m_destroy_tried_resuming to make sure we only do this once, if we resume and then halt and + // get called here to destroy again and we're still at a breakpoint or exception, then we should + // just do the straight-forward kill. + // + // And of course, if we weren't able to stop the process by the time we get here, it isn't + // necessary (or helpful) to do any of this. + + if (!m_gdb_comm.GetThreadSuffixSupported() && m_public_state.GetValue() != eStateRunning) + { + PlatformSP platform_sp = GetTarget().GetPlatform(); + + // FIXME: These should be ConstStrings so we aren't doing strcmp'ing. + if (platform_sp + && platform_sp->GetName() + && platform_sp->GetName() == PlatformRemoteiOS::GetPluginNameStatic()) + { + if (m_destroy_tried_resuming) + { + if (log) + log->PutCString ("ProcessGDBRemote::DoDestroy()Tried resuming to destroy once already, not doing it again."); + } + else + { + // At present, the plans are discarded and the breakpoints disabled Process::Destroy, + // but we really need it to happen here and it doesn't matter if we do it twice. + m_thread_list.DiscardThreadPlans(); + DisableAllBreakpointSites(); + + bool stop_looks_like_crash = false; + ThreadList &threads = GetThreadList(); + + { + Mutex::Locker locker(threads.GetMutex()); + + size_t num_threads = threads.GetSize(); + for (size_t i = 0; i < num_threads; i++) + { + ThreadSP thread_sp = threads.GetThreadAtIndex(i); + StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); + StopReason reason = eStopReasonInvalid; + if (stop_info_sp) + reason = stop_info_sp->GetStopReason(); + if (reason == eStopReasonBreakpoint + || reason == eStopReasonException) + { + if (log) + log->Printf ("ProcessGDBRemote::DoDestroy() - thread: 0x%4.4" PRIx64 " stopped with reason: %s.", + thread_sp->GetProtocolID(), + stop_info_sp->GetDescription()); + stop_looks_like_crash = true; + break; + } + } + } + + if (stop_looks_like_crash) + { + if (log) + log->PutCString ("ProcessGDBRemote::DoDestroy() - Stopped at a breakpoint, continue and then kill."); + m_destroy_tried_resuming = true; + + // If we are going to run again before killing, it would be good to suspend all the threads + // before resuming so they won't get into more trouble. Sadly, for the threads stopped with + // the breakpoint or exception, the exception doesn't get cleared if it is suspended, so we do + // have to run the risk of letting those threads proceed a bit. + + { + Mutex::Locker locker(threads.GetMutex()); + + size_t num_threads = threads.GetSize(); + for (size_t i = 0; i < num_threads; i++) + { + ThreadSP thread_sp = threads.GetThreadAtIndex(i); + StopInfoSP stop_info_sp = thread_sp->GetPrivateStopInfo(); + StopReason reason = eStopReasonInvalid; + if (stop_info_sp) + reason = stop_info_sp->GetStopReason(); + if (reason != eStopReasonBreakpoint + && reason != eStopReasonException) + { + if (log) + log->Printf ("ProcessGDBRemote::DoDestroy() - Suspending thread: 0x%4.4" PRIx64 " before running.", + thread_sp->GetProtocolID()); + thread_sp->SetResumeState(eStateSuspended); + } + } + } + Resume (); + return Destroy(); + } + } + } + } + + // Interrupt if our inferior is running... + int exit_status = SIGABRT; + std::string exit_string; + + if (m_gdb_comm.IsConnected()) + { + if (m_public_state.GetValue() != eStateAttaching) + { + + StringExtractorGDBRemote response; + bool send_async = true; + const uint32_t old_packet_timeout = m_gdb_comm.SetPacketTimeout (3); + + if (m_gdb_comm.SendPacketAndWaitForResponse("k", 1, response, send_async)) + { + char packet_cmd = response.GetChar(0); + + if (packet_cmd == 'W' || packet_cmd == 'X') + { + SetLastStopPacket (response); + ClearThreadIDList (); + exit_status = response.GetHexU8(); + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::DoDestroy - got unexpected response to k packet: %s", response.GetStringRef().c_str()); + exit_string.assign("got unexpected response to k packet: "); + exit_string.append(response.GetStringRef()); + } + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet"); + exit_string.assign("failed to send the k packet"); + } + + m_gdb_comm.SetPacketTimeout(old_packet_timeout); + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::DoDestroy - failed to send k packet"); + exit_string.assign ("killed or interrupted while attaching."); + } + } + else + { + // If we missed setting the exit status on the way out, do it here. + // NB set exit status can be called multiple times, the first one sets the status. + exit_string.assign("destroying when not connected to debugserver"); + } + + SetExitStatus(exit_status, exit_string.c_str()); + + StopAsyncThread (); + KillDebugserverProcess (); + return error; +} + +void +ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response) +{ + lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex); + const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; + if (did_exec) + { + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + if (log) + log->Printf ("ProcessGDBRemote::SetLastStopPacket () - detected exec"); + + m_thread_list_real.Clear(); + m_thread_list.Clear(); + BuildDynamicRegisterInfo (true); + m_gdb_comm.ResetDiscoverableSettings(); + } + m_last_stop_packet = response; +} + + +//------------------------------------------------------------------ +// Process Queries +//------------------------------------------------------------------ + +bool +ProcessGDBRemote::IsAlive () +{ + return m_gdb_comm.IsConnected() && m_private_state.GetValue() != eStateExited; +} + +addr_t +ProcessGDBRemote::GetImageInfoAddress() +{ + return m_gdb_comm.GetShlibInfoAddr(); +} + +//------------------------------------------------------------------ +// Process Memory +//------------------------------------------------------------------ +size_t +ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &error) +{ + if (size > m_max_memory_size) + { + // Keep memory read sizes down to a sane limit. This function will be + // called multiple times in order to complete the task by + // lldb_private::Process so it is ok to do this. + size = m_max_memory_size; + } + + char packet[64]; + const int packet_len = ::snprintf (packet, sizeof(packet), "m%" PRIx64 ",%" PRIx64, (uint64_t)addr, (uint64_t)size); + assert (packet_len + 1 < (int)sizeof(packet)); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, true)) + { + if (response.IsNormalResponse()) + { + error.Clear(); + return response.GetHexBytes(buf, size, '\xdd'); + } + else if (response.IsErrorResponse()) + error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, addr); + else if (response.IsUnsupportedResponse()) + error.SetErrorStringWithFormat("GDB server does not support reading memory"); + else + error.SetErrorStringWithFormat("unexpected response to GDB server memory read packet '%s': '%s'", packet, response.GetStringRef().c_str()); + } + else + { + error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); + } + return 0; +} + +size_t +ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Error &error) +{ + if (size > m_max_memory_size) + { + // Keep memory read sizes down to a sane limit. This function will be + // called multiple times in order to complete the task by + // lldb_private::Process so it is ok to do this. + size = m_max_memory_size; + } + + StreamString packet; + packet.Printf("M%" PRIx64 ",%" PRIx64 ":", addr, (uint64_t)size); + packet.PutBytesAsRawHex8(buf, size, lldb::endian::InlHostByteOrder(), lldb::endian::InlHostByteOrder()); + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, true)) + { + if (response.IsOKResponse()) + { + error.Clear(); + return size; + } + else if (response.IsErrorResponse()) + error.SetErrorStringWithFormat("memory write failed for 0x%" PRIx64, addr); + else if (response.IsUnsupportedResponse()) + error.SetErrorStringWithFormat("GDB server does not support writing memory"); + else + error.SetErrorStringWithFormat("unexpected response to GDB server memory write packet '%s': '%s'", packet.GetString().c_str(), response.GetStringRef().c_str()); + } + else + { + error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetString().c_str()); + } + return 0; +} + +lldb::addr_t +ProcessGDBRemote::DoAllocateMemory (size_t size, uint32_t permissions, Error &error) +{ + addr_t allocated_addr = LLDB_INVALID_ADDRESS; + + LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); + switch (supported) + { + case eLazyBoolCalculate: + case eLazyBoolYes: + allocated_addr = m_gdb_comm.AllocateMemory (size, permissions); + if (allocated_addr != LLDB_INVALID_ADDRESS || supported == eLazyBoolYes) + return allocated_addr; + + case eLazyBoolNo: + // Call mmap() to create memory in the inferior.. + unsigned prot = 0; + if (permissions & lldb::ePermissionsReadable) + prot |= eMmapProtRead; + if (permissions & lldb::ePermissionsWritable) + prot |= eMmapProtWrite; + if (permissions & lldb::ePermissionsExecutable) + prot |= eMmapProtExec; + + if (InferiorCallMmap(this, allocated_addr, 0, size, prot, + eMmapFlagsAnon | eMmapFlagsPrivate, -1, 0)) + m_addr_to_mmap_size[allocated_addr] = size; + else + allocated_addr = LLDB_INVALID_ADDRESS; + break; + } + + if (allocated_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat("unable to allocate %" PRIu64 " bytes of memory with permissions %s", (uint64_t)size, GetPermissionsAsCString (permissions)); + else + error.Clear(); + return allocated_addr; +} + +Error +ProcessGDBRemote::GetMemoryRegionInfo (addr_t load_addr, + MemoryRegionInfo ®ion_info) +{ + + Error error (m_gdb_comm.GetMemoryRegionInfo (load_addr, region_info)); + return error; +} + +Error +ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num) +{ + + Error error (m_gdb_comm.GetWatchpointSupportInfo (num)); + return error; +} + +Error +ProcessGDBRemote::GetWatchpointSupportInfo (uint32_t &num, bool& after) +{ + Error error (m_gdb_comm.GetWatchpointSupportInfo (num, after)); + return error; +} + +Error +ProcessGDBRemote::DoDeallocateMemory (lldb::addr_t addr) +{ + Error error; + LazyBool supported = m_gdb_comm.SupportsAllocDeallocMemory(); + + switch (supported) + { + case eLazyBoolCalculate: + // We should never be deallocating memory without allocating memory + // first so we should never get eLazyBoolCalculate + error.SetErrorString ("tried to deallocate memory without ever allocating memory"); + break; + + case eLazyBoolYes: + if (!m_gdb_comm.DeallocateMemory (addr)) + error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); + break; + + case eLazyBoolNo: + // Call munmap() to deallocate memory in the inferior.. + { + MMapMap::iterator pos = m_addr_to_mmap_size.find(addr); + if (pos != m_addr_to_mmap_size.end() && + InferiorCallMunmap(this, addr, pos->second)) + m_addr_to_mmap_size.erase (pos); + else + error.SetErrorStringWithFormat("unable to deallocate memory at 0x%" PRIx64, addr); + } + break; + } + + return error; +} + + +//------------------------------------------------------------------ +// Process STDIO +//------------------------------------------------------------------ +size_t +ProcessGDBRemote::PutSTDIN (const char *src, size_t src_len, Error &error) +{ + if (m_stdio_communication.IsConnected()) + { + ConnectionStatus status; + m_stdio_communication.Write(src, src_len, status, NULL); + } + return 0; +} + +Error +ProcessGDBRemote::EnableBreakpointSite (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); + user_id_t site_id = bp_site->GetID(); + const addr_t addr = bp_site->GetLoadAddress(); + if (log) + log->Printf ("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64, site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) + { + if (log) + log->Printf ("ProcessGDBRemote::EnableBreakpointSite (size_id = %" PRIu64 ") address = 0x%" PRIx64 " -- SUCCESS (already enabled)", site_id, (uint64_t)addr); + return error; + } + else + { + const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); + + if (bp_site->HardwarePreferred()) + { + // Try and set hardware breakpoint, and if that fails, fall through + // and set a software breakpoint? + if (m_gdb_comm.SupportsGDBStoppointPacket (eBreakpointHardware)) + { + if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointHardware, true, addr, bp_op_size) == 0) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eHardware); + return error; + } + } + } + + if (m_gdb_comm.SupportsGDBStoppointPacket (eBreakpointSoftware)) + { + if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, true, addr, bp_op_size) == 0) + { + bp_site->SetEnabled(true); + bp_site->SetType (BreakpointSite::eExternal); + return error; + } + } + + return EnableSoftwareBreakpoint (bp_site); + } + + if (log) + { + const char *err_string = error.AsCString(); + log->Printf ("ProcessGDBRemote::EnableBreakpointSite () error for breakpoint at 0x%8.8" PRIx64 ": %s", + bp_site->GetLoadAddress(), + err_string ? err_string : "NULL"); + } + // We shouldn't reach here on a successful breakpoint enable... + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +Error +ProcessGDBRemote::DisableBreakpointSite (BreakpointSite *bp_site) +{ + Error error; + assert (bp_site != NULL); + addr_t addr = bp_site->GetLoadAddress(); + user_id_t site_id = bp_site->GetID(); + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_BREAKPOINTS)); + if (log) + log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64, site_id, (uint64_t)addr); + + if (bp_site->IsEnabled()) + { + const size_t bp_op_size = GetSoftwareBreakpointTrapOpcode (bp_site); + + BreakpointSite::Type bp_type = bp_site->GetType(); + switch (bp_type) + { + case BreakpointSite::eSoftware: + error = DisableSoftwareBreakpoint (bp_site); + break; + + case BreakpointSite::eHardware: + if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, false, addr, bp_op_size)) + error.SetErrorToGenericError(); + break; + + case BreakpointSite::eExternal: + if (m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, false, addr, bp_op_size)) + error.SetErrorToGenericError(); + break; + } + if (error.Success()) + bp_site->SetEnabled(false); + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::DisableBreakpointSite (site_id = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", site_id, (uint64_t)addr); + return error; + } + + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +// Pre-requisite: wp != NULL. +static GDBStoppointType +GetGDBStoppointType (Watchpoint *wp) +{ + assert(wp); + bool watch_read = wp->WatchpointRead(); + bool watch_write = wp->WatchpointWrite(); + + // watch_read and watch_write cannot both be false. + assert(watch_read || watch_write); + if (watch_read && watch_write) + return eWatchpointReadWrite; + else if (watch_read) + return eWatchpointRead; + else // Must be watch_write, then. + return eWatchpointWrite; +} + +Error +ProcessGDBRemote::EnableWatchpoint (Watchpoint *wp, bool notify) +{ + Error error; + if (wp) + { + user_id_t watchID = wp->GetID(); + addr_t addr = wp->GetLoadAddress(); + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); + if (log) + log->Printf ("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ")", watchID); + if (wp->IsEnabled()) + { + if (log) + log->Printf("ProcessGDBRemote::EnableWatchpoint(watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 ": watchpoint already enabled.", watchID, (uint64_t)addr); + return error; + } + + GDBStoppointType type = GetGDBStoppointType(wp); + // Pass down an appropriate z/Z packet... + if (m_gdb_comm.SupportsGDBStoppointPacket (type)) + { + if (m_gdb_comm.SendGDBStoppointTypePacket(type, true, addr, wp->GetByteSize()) == 0) + { + wp->SetEnabled(true, notify); + return error; + } + else + error.SetErrorString("sending gdb watchpoint packet failed"); + } + else + error.SetErrorString("watchpoints not supported"); + } + else + { + error.SetErrorString("Watchpoint argument was NULL."); + } + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +Error +ProcessGDBRemote::DisableWatchpoint (Watchpoint *wp, bool notify) +{ + Error error; + if (wp) + { + user_id_t watchID = wp->GetID(); + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_WATCHPOINTS)); + + addr_t addr = wp->GetLoadAddress(); + + if (log) + log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64, watchID, (uint64_t)addr); + + if (!wp->IsEnabled()) + { + if (log) + log->Printf ("ProcessGDBRemote::DisableWatchpoint (watchID = %" PRIu64 ") addr = 0x%8.8" PRIx64 " -- SUCCESS (already disabled)", watchID, (uint64_t)addr); + // See also 'class WatchpointSentry' within StopInfo.cpp. + // This disabling attempt might come from the user-supplied actions, we'll route it in order for + // the watchpoint object to intelligently process this action. + wp->SetEnabled(false, notify); + return error; + } + + if (wp->IsHardware()) + { + GDBStoppointType type = GetGDBStoppointType(wp); + // Pass down an appropriate z/Z packet... + if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, wp->GetByteSize()) == 0) + { + wp->SetEnabled(false, notify); + return error; + } + else + error.SetErrorString("sending gdb watchpoint packet failed"); + } + // TODO: clear software watchpoints if we implement them + } + else + { + error.SetErrorString("Watchpoint argument was NULL."); + } + if (error.Success()) + error.SetErrorToGenericError(); + return error; +} + +void +ProcessGDBRemote::Clear() +{ + m_flags = 0; + m_thread_list_real.Clear(); + m_thread_list.Clear(); +} + +Error +ProcessGDBRemote::DoSignal (int signo) +{ + Error error; + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + if (log) + log->Printf ("ProcessGDBRemote::DoSignal (signal = %d)", signo); + + if (!m_gdb_comm.SendAsyncSignal (signo)) + error.SetErrorStringWithFormat("failed to send signal %i", signo); + return error; +} + +Error +ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url) +{ + ProcessLaunchInfo launch_info; + return StartDebugserverProcess(debugserver_url, launch_info); +} + +Error +ProcessGDBRemote::StartDebugserverProcess (const char *debugserver_url, const ProcessInfo &process_info) // The connection string to use in the spawned debugserver ("localhost:1234" or "/dev/tty...") +{ + Error error; + if (m_debugserver_pid == LLDB_INVALID_PROCESS_ID) + { + // If we locate debugserver, keep that located version around + static FileSpec g_debugserver_file_spec; + + ProcessLaunchInfo debugserver_launch_info; + char debugserver_path[PATH_MAX]; + FileSpec &debugserver_file_spec = debugserver_launch_info.GetExecutableFile(); + + // Always check to see if we have an environment override for the path + // to the debugserver to use and use it if we do. + const char *env_debugserver_path = getenv("LLDB_DEBUGSERVER_PATH"); + if (env_debugserver_path) + debugserver_file_spec.SetFile (env_debugserver_path, false); + else + debugserver_file_spec = g_debugserver_file_spec; + bool debugserver_exists = debugserver_file_spec.Exists(); + if (!debugserver_exists) + { + // The debugserver binary is in the LLDB.framework/Resources + // directory. + if (Host::GetLLDBPath (ePathTypeSupportExecutableDir, debugserver_file_spec)) + { + debugserver_file_spec.GetFilename().SetCString(DEBUGSERVER_BASENAME); + debugserver_exists = debugserver_file_spec.Exists(); + if (debugserver_exists) + { + g_debugserver_file_spec = debugserver_file_spec; + } + else + { + g_debugserver_file_spec.Clear(); + debugserver_file_spec.Clear(); + } + } + } + + if (debugserver_exists) + { + debugserver_file_spec.GetPath (debugserver_path, sizeof(debugserver_path)); + + m_stdio_communication.Clear(); + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + + Args &debugserver_args = debugserver_launch_info.GetArguments(); + char arg_cstr[PATH_MAX]; + + // Start args with "debugserver /file/path -r --" + debugserver_args.AppendArgument(debugserver_path); + debugserver_args.AppendArgument(debugserver_url); + // use native registers, not the GDB registers + debugserver_args.AppendArgument("--native-regs"); + // make debugserver run in its own session so signals generated by + // special terminal key sequences (^C) don't affect debugserver + debugserver_args.AppendArgument("--setsid"); + + const char *env_debugserver_log_file = getenv("LLDB_DEBUGSERVER_LOG_FILE"); + if (env_debugserver_log_file) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-file=%s", env_debugserver_log_file); + debugserver_args.AppendArgument(arg_cstr); + } + + const char *env_debugserver_log_flags = getenv("LLDB_DEBUGSERVER_LOG_FLAGS"); + if (env_debugserver_log_flags) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--log-flags=%s", env_debugserver_log_flags); + debugserver_args.AppendArgument(arg_cstr); + } +// debugserver_args.AppendArgument("--log-file=/tmp/debugserver.txt"); +// debugserver_args.AppendArgument("--log-flags=0x802e0e"); + + // We currently send down all arguments, attach pids, or attach + // process names in dedicated GDB server packets, so we don't need + // to pass them as arguments. This is currently because of all the + // things we need to setup prior to launching: the environment, + // current working dir, file actions, etc. +#if 0 + // Now append the program arguments + if (inferior_argv) + { + // Terminate the debugserver args so we can now append the inferior args + debugserver_args.AppendArgument("--"); + + for (int i = 0; inferior_argv[i] != NULL; ++i) + debugserver_args.AppendArgument (inferior_argv[i]); + } + else if (attach_pid != LLDB_INVALID_PROCESS_ID) + { + ::snprintf (arg_cstr, sizeof(arg_cstr), "--attach=%u", attach_pid); + debugserver_args.AppendArgument (arg_cstr); + } + else if (attach_name && attach_name[0]) + { + if (wait_for_launch) + debugserver_args.AppendArgument ("--waitfor"); + else + debugserver_args.AppendArgument ("--attach"); + debugserver_args.AppendArgument (attach_name); + } +#endif + + ProcessLaunchInfo::FileAction file_action; + + // Close STDIN, STDOUT and STDERR. We might need to redirect them + // to "/dev/null" if we run into any problems. + file_action.Close (STDIN_FILENO); + debugserver_launch_info.AppendFileAction (file_action); + file_action.Close (STDOUT_FILENO); + debugserver_launch_info.AppendFileAction (file_action); + file_action.Close (STDERR_FILENO); + debugserver_launch_info.AppendFileAction (file_action); + + if (log) + { + StreamString strm; + debugserver_args.Dump (&strm); + log->Printf("%s arguments:\n%s", debugserver_args.GetArgumentAtIndex(0), strm.GetData()); + } + + debugserver_launch_info.SetMonitorProcessCallback (MonitorDebugserverProcess, this, false); + debugserver_launch_info.SetUserID(process_info.GetUserID()); + + error = Host::LaunchProcess(debugserver_launch_info); + + if (error.Success ()) + m_debugserver_pid = debugserver_launch_info.GetProcessID(); + else + m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + + if (error.Fail() || log) + error.PutToLog(log, "Host::LaunchProcess (launch_info) => pid=%" PRIu64 ", path='%s'", m_debugserver_pid, debugserver_path); + } + else + { + error.SetErrorStringWithFormat ("unable to locate " DEBUGSERVER_BASENAME); + } + + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) + StartAsyncThread (); + } + return error; +} + +bool +ProcessGDBRemote::MonitorDebugserverProcess +( + void *callback_baton, + lldb::pid_t debugserver_pid, + bool exited, // True if the process did exit + int signo, // Zero for no signal + int exit_status // Exit value of process if signal is zero +) +{ + // The baton is a "ProcessGDBRemote *". Now this class might be gone + // and might not exist anymore, so we need to carefully try to get the + // target for this process first since we have a race condition when + // we are done running between getting the notice that the inferior + // process has died and the debugserver that was debugging this process. + // In our test suite, we are also continually running process after + // process, so we must be very careful to make sure: + // 1 - process object hasn't been deleted already + // 2 - that a new process object hasn't been recreated in its place + + // "debugserver_pid" argument passed in is the process ID for + // debugserver that we are tracking... + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + + ProcessGDBRemote *process = (ProcessGDBRemote *)callback_baton; + + // Get a shared pointer to the target that has a matching process pointer. + // This target could be gone, or the target could already have a new process + // object inside of it + TargetSP target_sp (Debugger::FindTargetWithProcess(process)); + + if (log) + log->Printf ("ProcessGDBRemote::MonitorDebugserverProcess (baton=%p, pid=%" PRIu64 ", signo=%i (0x%x), exit_status=%i)", callback_baton, debugserver_pid, signo, signo, exit_status); + + if (target_sp) + { + // We found a process in a target that matches, but another thread + // might be in the process of launching a new process that will + // soon replace it, so get a shared pointer to the process so we + // can keep it alive. + ProcessSP process_sp (target_sp->GetProcessSP()); + // Now we have a shared pointer to the process that can't go away on us + // so we now make sure it was the same as the one passed in, and also make + // sure that our previous "process *" didn't get deleted and have a new + // "process *" created in its place with the same pointer. To verify this + // we make sure the process has our debugserver process ID. If we pass all + // of these tests, then we are sure that this process is the one we were + // looking for. + if (process_sp && process == process_sp.get() && process->m_debugserver_pid == debugserver_pid) + { + // Sleep for a half a second to make sure our inferior process has + // time to set its exit status before we set it incorrectly when + // both the debugserver and the inferior process shut down. + usleep (500000); + // If our process hasn't yet exited, debugserver might have died. + // If the process did exit, the we are reaping it. + const StateType state = process->GetState(); + + if (process->m_debugserver_pid != LLDB_INVALID_PROCESS_ID && + state != eStateInvalid && + state != eStateUnloaded && + state != eStateExited && + state != eStateDetached) + { + char error_str[1024]; + if (signo) + { + const char *signal_cstr = process->GetUnixSignals().GetSignalAsCString (signo); + if (signal_cstr) + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %s", signal_cstr); + else + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with signal %i", signo); + } + else + { + ::snprintf (error_str, sizeof (error_str), DEBUGSERVER_BASENAME " died with an exit status of 0x%8.8x", exit_status); + } + + process->SetExitStatus (-1, error_str); + } + // Debugserver has exited we need to let our ProcessGDBRemote + // know that it no longer has a debugserver instance + process->m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + } + } + return true; +} + +void +ProcessGDBRemote::KillDebugserverProcess () +{ + if (m_debugserver_pid != LLDB_INVALID_PROCESS_ID) + { + ::kill (m_debugserver_pid, SIGINT); + m_debugserver_pid = LLDB_INVALID_PROCESS_ID; + } +} + +void +ProcessGDBRemote::Initialize() +{ + static bool g_initialized = false; + + if (g_initialized == false) + { + g_initialized = true; + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance, + DebuggerInitialize); + + Log::Callbacks log_callbacks = { + ProcessGDBRemoteLog::DisableLog, + ProcessGDBRemoteLog::EnableLog, + ProcessGDBRemoteLog::ListLogCategories + }; + + Log::RegisterLogChannel (ProcessGDBRemote::GetPluginNameStatic(), log_callbacks); + } +} + +void +ProcessGDBRemote::DebuggerInitialize (lldb_private::Debugger &debugger) +{ + if (!PluginManager::GetSettingForProcessPlugin(debugger, PluginProperties::GetSettingName())) + { + const bool is_global_setting = true; + PluginManager::CreateSettingForProcessPlugin (debugger, + GetGlobalPluginProperties()->GetValueProperties(), + ConstString ("Properties for the gdb-remote process plug-in."), + is_global_setting); + } +} + +bool +ProcessGDBRemote::StartAsyncThread () +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); + + Mutex::Locker start_locker(m_async_thread_state_mutex); + if (m_async_thread_state == eAsyncThreadNotStarted) + { + // Create a thread that watches our internal state and controls which + // events make it to clients (into the DCProcess event queue). + m_async_thread = Host::ThreadCreate ("<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this, NULL); + if (IS_VALID_LLDB_HOST_THREAD(m_async_thread)) + { + m_async_thread_state = eAsyncThreadRunning; + return true; + } + else + return false; + } + else + { + // Somebody tried to start the async thread while it was either being started or stopped. If the former, and + // it started up successfully, then say all's well. Otherwise it is an error, since we aren't going to restart it. + if (log) + log->Printf ("ProcessGDBRemote::%s () - Called when Async thread was in state: %d.", __FUNCTION__, m_async_thread_state); + if (m_async_thread_state == eAsyncThreadRunning) + return true; + else + return false; + } +} + +void +ProcessGDBRemote::StopAsyncThread () +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); + + Mutex::Locker start_locker(m_async_thread_state_mutex); + if (m_async_thread_state == eAsyncThreadRunning) + { + m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); + + // This will shut down the async thread. + m_gdb_comm.Disconnect(); // Disconnect from the debug server. + + // Stop the stdio thread + if (IS_VALID_LLDB_HOST_THREAD(m_async_thread)) + { + Host::ThreadJoin (m_async_thread, NULL, NULL); + } + m_async_thread_state = eAsyncThreadDone; + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::%s () - Called when Async thread was in state: %d.", __FUNCTION__, m_async_thread_state); + } +} + + +void * +ProcessGDBRemote::AsyncThread (void *arg) +{ + ProcessGDBRemote *process = (ProcessGDBRemote*) arg; + + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, arg, process->GetID()); + + Listener listener ("ProcessGDBRemote::AsyncThread"); + EventSP event_sp; + const uint32_t desired_event_mask = eBroadcastBitAsyncContinue | + eBroadcastBitAsyncThreadShouldExit; + + if (listener.StartListeningForEvents (&process->m_async_broadcaster, desired_event_mask) == desired_event_mask) + { + listener.StartListeningForEvents (&process->m_gdb_comm, Communication::eBroadcastBitReadThreadDidExit); + + bool done = false; + while (!done) + { + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp)...", __FUNCTION__, arg, process->GetID()); + if (listener.WaitForEvent (NULL, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + if (event_sp->BroadcasterIs (&process->m_async_broadcaster)) + { + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") Got an event of type: %d...", __FUNCTION__, arg, process->GetID(), event_type); + + switch (event_type) + { + case eBroadcastBitAsyncContinue: + { + const EventDataBytes *continue_packet = EventDataBytes::GetEventDataFromEvent(event_sp.get()); + + if (continue_packet) + { + const char *continue_cstr = (const char *)continue_packet->GetBytes (); + const size_t continue_cstr_len = continue_packet->GetByteSize (); + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncContinue: %s", __FUNCTION__, arg, process->GetID(), continue_cstr); + + if (::strstr (continue_cstr, "vAttach") == NULL) + process->SetPrivateState(eStateRunning); + StringExtractorGDBRemote response; + StateType stop_state = process->GetGDBRemote().SendContinuePacketAndWaitForResponse (process, continue_cstr, continue_cstr_len, response); + + // We need to immediately clear the thread ID list so we are sure to get a valid list of threads. + // The thread ID list might be contained within the "response", or the stop reply packet that + // caused the stop. So clear it now before we give the stop reply packet to the process + // using the process->SetLastStopPacket()... + process->ClearThreadIDList (); + + switch (stop_state) + { + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + process->SetLastStopPacket (response); + process->SetPrivateState (stop_state); + break; + + case eStateExited: + process->SetLastStopPacket (response); + process->ClearThreadIDList(); + response.SetFilePos(1); + process->SetExitStatus(response.GetHexU8(), NULL); + done = true; + break; + + case eStateInvalid: + process->SetExitStatus(-1, "lost connection"); + break; + + default: + process->SetPrivateState (stop_state); + break; + } + } + } + break; + + case eBroadcastBitAsyncThreadShouldExit: + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got eBroadcastBitAsyncThreadShouldExit...", __FUNCTION__, arg, process->GetID()); + done = true; + break; + + default: + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") got unknown event 0x%8.8x", __FUNCTION__, arg, process->GetID(), event_type); + done = true; + break; + } + } + else if (event_sp->BroadcasterIs (&process->m_gdb_comm)) + { + if (event_type & Communication::eBroadcastBitReadThreadDidExit) + { + process->SetExitStatus (-1, "lost connection"); + done = true; + } + } + } + else + { + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") listener.WaitForEvent (NULL, event_sp) => false", __FUNCTION__, arg, process->GetID()); + done = true; + } + } + } + + if (log) + log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); + + process->m_async_thread = LLDB_INVALID_HOST_THREAD; + return NULL; +} + +const char * +ProcessGDBRemote::GetDispatchQueueNameForThread +( + addr_t thread_dispatch_qaddr, + std::string &dispatch_queue_name +) +{ + dispatch_queue_name.clear(); + if (thread_dispatch_qaddr != 0 && thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) + { + // Cache the dispatch_queue_offsets_addr value so we don't always have + // to look it up + if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) + { + static ConstString g_dispatch_queue_offsets_symbol_name ("dispatch_queue_offsets"); + const Symbol *dispatch_queue_offsets_symbol = NULL; + ModuleSpec libSystem_module_spec (FileSpec("libSystem.B.dylib", false)); + ModuleSP module_sp(GetTarget().GetImages().FindFirstModule (libSystem_module_spec)); + if (module_sp) + dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); + + if (dispatch_queue_offsets_symbol == NULL) + { + ModuleSpec libdispatch_module_spec (FileSpec("libdispatch.dylib", false)); + module_sp = GetTarget().GetImages().FindFirstModule (libdispatch_module_spec); + if (module_sp) + dispatch_queue_offsets_symbol = module_sp->FindFirstSymbolWithNameAndType (g_dispatch_queue_offsets_symbol_name, eSymbolTypeData); + } + if (dispatch_queue_offsets_symbol) + m_dispatch_queue_offsets_addr = dispatch_queue_offsets_symbol->GetAddress().GetLoadAddress(&m_target); + + if (m_dispatch_queue_offsets_addr == LLDB_INVALID_ADDRESS) + return NULL; + } + + uint8_t memory_buffer[8]; + DataExtractor data (memory_buffer, + sizeof(memory_buffer), + m_target.GetArchitecture().GetByteOrder(), + m_target.GetArchitecture().GetAddressByteSize()); + + // Excerpt from src/queue_private.h + struct dispatch_queue_offsets_s + { + uint16_t dqo_version; + uint16_t dqo_label; // in version 1-3, offset to string; in version 4+, offset to a pointer to a string + uint16_t dqo_label_size; // in version 1-3, length of string; in version 4+, size of a (void*) in this process + } dispatch_queue_offsets; + + + Error error; + if (ReadMemory (m_dispatch_queue_offsets_addr, memory_buffer, sizeof(dispatch_queue_offsets), error) == sizeof(dispatch_queue_offsets)) + { + lldb::offset_t data_offset = 0; + if (data.GetU16(&data_offset, &dispatch_queue_offsets.dqo_version, sizeof(dispatch_queue_offsets)/sizeof(uint16_t))) + { + if (ReadMemory (thread_dispatch_qaddr, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize()) + { + data_offset = 0; + lldb::addr_t queue_addr = data.GetAddress(&data_offset); + if (dispatch_queue_offsets.dqo_version >= 4) + { + // libdispatch versions 4+, pointer to dispatch name is in the + // queue structure. + lldb::addr_t pointer_to_label_address = queue_addr + dispatch_queue_offsets.dqo_label; + if (ReadMemory (pointer_to_label_address, &memory_buffer, data.GetAddressByteSize(), error) == data.GetAddressByteSize()) + { + data_offset = 0; + lldb::addr_t label_addr = data.GetAddress(&data_offset); + ReadCStringFromMemory (label_addr, dispatch_queue_name, error); + } + } + else + { + // libdispatch versions 1-3, dispatch name is a fixed width char array + // in the queue structure. + lldb::addr_t label_addr = queue_addr + dispatch_queue_offsets.dqo_label; + dispatch_queue_name.resize(dispatch_queue_offsets.dqo_label_size, '\0'); + size_t bytes_read = ReadMemory (label_addr, &dispatch_queue_name[0], dispatch_queue_offsets.dqo_label_size, error); + if (bytes_read < dispatch_queue_offsets.dqo_label_size) + dispatch_queue_name.erase (bytes_read); + } + } + } + } + } + if (dispatch_queue_name.empty()) + return NULL; + return dispatch_queue_name.c_str(); +} + +//uint32_t +//ProcessGDBRemote::ListProcessesMatchingName (const char *name, StringList &matches, std::vector<lldb::pid_t> &pids) +//{ +// // If we are planning to launch the debugserver remotely, then we need to fire up a debugserver +// // process and ask it for the list of processes. But if we are local, we can let the Host do it. +// if (m_local_debugserver) +// { +// return Host::ListProcessesMatchingName (name, matches, pids); +// } +// else +// { +// // FIXME: Implement talking to the remote debugserver. +// return 0; +// } +// +//} +// +bool +ProcessGDBRemote::NewThreadNotifyBreakpointHit (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) +{ + // I don't think I have to do anything here, just make sure I notice the new thread when it starts to + // run so I can stop it if that's what I want to do. + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->Printf("Hit New Thread Notification breakpoint."); + return false; +} + + +bool +ProcessGDBRemote::StartNoticingNewThreads() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (m_thread_create_bp_sp) + { + if (log && log->GetVerbose()) + log->Printf("Enabled noticing new thread breakpoint."); + m_thread_create_bp_sp->SetEnabled(true); + } + else + { + PlatformSP platform_sp (m_target.GetPlatform()); + if (platform_sp) + { + m_thread_create_bp_sp = platform_sp->SetThreadCreationBreakpoint(m_target); + if (m_thread_create_bp_sp) + { + if (log && log->GetVerbose()) + log->Printf("Successfully created new thread notification breakpoint %i", m_thread_create_bp_sp->GetID()); + m_thread_create_bp_sp->SetCallback (ProcessGDBRemote::NewThreadNotifyBreakpointHit, this, true); + } + else + { + if (log) + log->Printf("Failed to create new thread notification breakpoint."); + } + } + } + return m_thread_create_bp_sp.get() != NULL; +} + +bool +ProcessGDBRemote::StopNoticingNewThreads() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) + log->Printf ("Disabling new thread notification breakpoint."); + + if (m_thread_create_bp_sp) + m_thread_create_bp_sp->SetEnabled(false); + + return true; +} + +lldb_private::DynamicLoader * +ProcessGDBRemote::GetDynamicLoader () +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, NULL)); + return m_dyld_ap.get(); +} + + +class CommandObjectProcessGDBRemotePacketHistory : public CommandObjectParsed +{ +private: + +public: + CommandObjectProcessGDBRemotePacketHistory(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process plugin packet history", + "Dumps the packet history buffer. ", + NULL) + { + } + + ~CommandObjectProcessGDBRemotePacketHistory () + { + } + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + { + process->GetGDBRemote().DumpHistory(result.GetOutputStream()); + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + } + else + { + result.AppendErrorWithFormat ("'%s' takes no arguments", m_cmd_name.c_str()); + } + result.SetStatus (eReturnStatusFailed); + return false; + } +}; + +class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed +{ +private: + +public: + CommandObjectProcessGDBRemotePacketSend(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process plugin packet send", + "Send a custom packet through the GDB remote protocol and print the answer. " + "The packet header and footer will automatically be added to the packet prior to sending and stripped from the result.", + NULL) + { + } + + ~CommandObjectProcessGDBRemotePacketSend () + { + } + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + result.AppendErrorWithFormat ("'%s' takes a one or more packet content arguments", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + { + for (size_t i=0; i<argc; ++ i) + { + const char *packet_cstr = command.GetArgumentAtIndex(0); + bool send_async = true; + StringExtractorGDBRemote response; + process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); + result.SetStatus (eReturnStatusSuccessFinishResult); + Stream &output_strm = result.GetOutputStream(); + output_strm.Printf (" packet: %s\n", packet_cstr); + std::string &response_str = response.GetStringRef(); + + if (strstr(packet_cstr, "qGetProfileData") != NULL) + { + response_str = process->GetGDBRemote().HarmonizeThreadIdsForProfileData(process, response); + } + + if (response_str.empty()) + output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); + else + output_strm.Printf ("response: %s\n", response.GetStringRef().c_str()); + } + } + return true; + } +}; + +class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw +{ +private: + +public: + CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "process plugin packet monitor", + "Send a qRcmd packet through the GDB remote protocol and print the response." + "The argument passed to this command will be hex encoded into a valid 'qRcmd' packet, sent and the response will be printed.", + NULL) + { + } + + ~CommandObjectProcessGDBRemotePacketMonitor () + { + } + + bool + DoExecute (const char *command, CommandReturnObject &result) + { + if (command == NULL || command[0] == '\0') + { + result.AppendErrorWithFormat ("'%s' takes a command string argument", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ProcessGDBRemote *process = (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + { + StreamString packet; + packet.PutCString("qRcmd,"); + packet.PutBytesAsRawHex8(command, strlen(command)); + const char *packet_cstr = packet.GetString().c_str(); + + bool send_async = true; + StringExtractorGDBRemote response; + process->GetGDBRemote().SendPacketAndWaitForResponse(packet_cstr, response, send_async); + result.SetStatus (eReturnStatusSuccessFinishResult); + Stream &output_strm = result.GetOutputStream(); + output_strm.Printf (" packet: %s\n", packet_cstr); + const std::string &response_str = response.GetStringRef(); + + if (response_str.empty()) + output_strm.PutCString ("response: \nerror: UNIMPLEMENTED\n"); + else + output_strm.Printf ("response: %s\n", response.GetStringRef().c_str()); + } + return true; + } +}; + +class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword +{ +private: + +public: + CommandObjectProcessGDBRemotePacket(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process plugin packet", + "Commands that deal with GDB remote packets.", + NULL) + { + LoadSubCommand ("history", CommandObjectSP (new CommandObjectProcessGDBRemotePacketHistory (interpreter))); + LoadSubCommand ("send", CommandObjectSP (new CommandObjectProcessGDBRemotePacketSend (interpreter))); + LoadSubCommand ("monitor", CommandObjectSP (new CommandObjectProcessGDBRemotePacketMonitor (interpreter))); + } + + ~CommandObjectProcessGDBRemotePacket () + { + } +}; + +class CommandObjectMultiwordProcessGDBRemote : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordProcessGDBRemote (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process plugin", + "A set of commands for operating on a ProcessGDBRemote process.", + "process plugin <subcommand> [<subcommand-options>]") + { + LoadSubCommand ("packet", CommandObjectSP (new CommandObjectProcessGDBRemotePacket (interpreter))); + } + + ~CommandObjectMultiwordProcessGDBRemote () + { + } +}; + +CommandObject * +ProcessGDBRemote::GetPluginCommandObject() +{ + if (!m_command_sp) + m_command_sp.reset (new CommandObjectMultiwordProcessGDBRemote (GetTarget().GetDebugger().GetCommandInterpreter())); + return m_command_sp.get(); +} diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h new file mode 100644 index 000000000000..e104b7191eab --- /dev/null +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -0,0 +1,396 @@ +//===-- ProcessGDBRemote.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessGDBRemote_h_ +#define liblldb_ProcessGDBRemote_h_ + +// C Includes + +// C++ Includes +#include <list> +#include <vector> + +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +#include "GDBRemoteCommunicationClient.h" +#include "Utility/StringExtractor.h" +#include "GDBRemoteRegisterContext.h" + +class ThreadGDBRemote; + +class ProcessGDBRemote : public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance (lldb_private::Target& target, + lldb_private::Listener &listener, + const lldb_private::FileSpec *crash_file_path); + + static void + Initialize(); + + static void + DebuggerInitialize (lldb_private::Debugger &debugger); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ProcessGDBRemote(lldb_private::Target& target, lldb_private::Listener &listener); + + virtual + ~ProcessGDBRemote(); + + //------------------------------------------------------------------ + // Check if a given Process + //------------------------------------------------------------------ + virtual bool + CanDebug (lldb_private::Target &target, + bool plugin_specified_by_name); + + virtual lldb_private::CommandObject * + GetPluginCommandObject(); + + //------------------------------------------------------------------ + // Creating a new process, or attaching to an existing one + //------------------------------------------------------------------ + virtual lldb_private::Error + WillLaunch (lldb_private::Module* module); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module *exe_module, + const lldb_private::ProcessLaunchInfo &launch_info); + + virtual void + DidLaunch (); + + virtual lldb_private::Error + WillAttachToProcessWithID (lldb::pid_t pid); + + virtual lldb_private::Error + WillAttachToProcessWithName (const char *process_name, bool wait_for_launch); + + virtual lldb_private::Error + DoConnectRemote (lldb_private::Stream *strm, const char *remote_url); + + lldb_private::Error + WillLaunchOrAttach (); + + virtual lldb_private::Error + DoAttachToProcessWithID (lldb::pid_t pid); + + virtual lldb_private::Error + DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); + + virtual lldb_private::Error + DoAttachToProcessWithName (const char *process_name, + bool wait_for_launch, + const lldb_private::ProcessAttachInfo &attach_info); + + virtual void + DidAttach (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + //------------------------------------------------------------------ + // Process Control + //------------------------------------------------------------------ + virtual lldb_private::Error + WillResume (); + + virtual lldb_private::Error + DoResume (); + + virtual lldb_private::Error + DoHalt (bool &caused_stop); + + virtual lldb_private::Error + DoDetach (bool keep_stopped); + + virtual bool + DetachRequiresHalt() { return true; } + + virtual lldb_private::Error + DoSignal (int signal); + + virtual lldb_private::Error + DoDestroy (); + + virtual void + RefreshStateAfterStop(); + + //------------------------------------------------------------------ + // Process Queries + //------------------------------------------------------------------ + virtual bool + IsAlive (); + + virtual lldb::addr_t + GetImageInfoAddress(); + + //------------------------------------------------------------------ + // Process Memory + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + + virtual size_t + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error); + + virtual lldb_private::Error + GetMemoryRegionInfo (lldb::addr_t load_addr, + lldb_private::MemoryRegionInfo ®ion_info); + + virtual lldb_private::Error + DoDeallocateMemory (lldb::addr_t ptr); + + //------------------------------------------------------------------ + // Process STDIO + //------------------------------------------------------------------ + virtual size_t + PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableBreakpointSite (lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpointSite (lldb_private::BreakpointSite *bp_site); + + //---------------------------------------------------------------------- + // Process Watchpoints + //---------------------------------------------------------------------- + virtual lldb_private::Error + EnableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + DisableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + GetWatchpointSupportInfo (uint32_t &num); + + virtual lldb_private::Error + GetWatchpointSupportInfo (uint32_t &num, bool& after); + + virtual bool + StartNoticingNewThreads(); + + virtual bool + StopNoticingNewThreads(); + + GDBRemoteCommunicationClient & + GetGDBRemote() + { + return m_gdb_comm; + } + +protected: + friend class ThreadGDBRemote; + friend class GDBRemoteCommunicationClient; + friend class GDBRemoteRegisterContext; + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + bool + IsRunning ( lldb::StateType state ) + { + return state == lldb::eStateRunning || IsStepping(state); + } + + bool + IsStepping ( lldb::StateType state) + { + return state == lldb::eStateStepping; + } + bool + CanResume ( lldb::StateType state) + { + return state == lldb::eStateStopped; + } + + bool + HasExited (lldb::StateType state) + { + return state == lldb::eStateExited; + } + + bool + ProcessIDIsValid ( ) const; + + void + Clear ( ); + + lldb_private::Flags & + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags & + GetFlags () const + { + return m_flags; + } + + virtual bool + UpdateThreadList (lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list); + + lldb_private::Error + StartDebugserverProcess (const char *debugserver_url); + + lldb_private::Error + StartDebugserverProcess (const char *debugserver_url, const lldb_private::ProcessInfo &process_info); + + void + KillDebugserverProcess (); + + void + BuildDynamicRegisterInfo (bool force); + + void + SetLastStopPacket (const StringExtractorGDBRemote &response); + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitAsyncContinue = (1 << 0), + eBroadcastBitAsyncThreadShouldExit = (1 << 1), + eBroadcastBitAsyncThreadDidExit = (1 << 2) + }; + + typedef enum AsyncThreadState + { + eAsyncThreadNotStarted, + eAsyncThreadRunning, + eAsyncThreadDone + } AsyncThreadState; + + lldb_private::Flags m_flags; // Process specific flags (see eFlags enums) + GDBRemoteCommunicationClient m_gdb_comm; + lldb::pid_t m_debugserver_pid; + StringExtractorGDBRemote m_last_stop_packet; + lldb_private::Mutex m_last_stop_packet_mutex; + GDBRemoteDynamicRegisterInfo m_register_info; + lldb_private::Broadcaster m_async_broadcaster; + lldb::thread_t m_async_thread; + AsyncThreadState m_async_thread_state; + lldb_private::Mutex m_async_thread_state_mutex; + typedef std::vector<lldb::tid_t> tid_collection; + typedef std::vector< std::pair<lldb::tid_t,int> > tid_sig_collection; + typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap; + tid_collection m_thread_ids; // Thread IDs for all threads. This list gets updated after stopping + tid_collection m_continue_c_tids; // 'c' for continue + tid_sig_collection m_continue_C_tids; // 'C' for continue with signal + tid_collection m_continue_s_tids; // 's' for step + tid_sig_collection m_continue_S_tids; // 'S' for step with signal + lldb::addr_t m_dispatch_queue_offsets_addr; + size_t m_max_memory_size; // The maximum number of bytes to read/write when reading and writing memory + MMapMap m_addr_to_mmap_size; + lldb::BreakpointSP m_thread_create_bp_sp; + bool m_waiting_for_attach; + bool m_destroy_tried_resuming; + lldb::CommandObjectSP m_command_sp; + + bool + StartAsyncThread (); + + void + StopAsyncThread (); + + static void * + AsyncThread (void *arg); + + static bool + MonitorDebugserverProcess (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signo, + int exit_status); + + lldb::StateType + SetThreadStopInfo (StringExtractor& stop_packet); + + void + ClearThreadIDList (); + + bool + UpdateThreadIDList (); + + void + DidLaunchOrAttach (); + + lldb_private::Error + ConnectToDebugserver (const char *host_port); + + const char * + GetDispatchQueueNameForThread (lldb::addr_t thread_dispatch_qaddr, + std::string &dispatch_queue_name); + + static size_t + AttachInputReaderCallback (void *baton, + lldb_private::InputReader *reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + lldb_private::DynamicLoader * + GetDynamicLoader (); + +private: + //------------------------------------------------------------------ + // For ProcessGDBRemote only + //------------------------------------------------------------------ + static bool + NewThreadNotifyBreakpointHit (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + DISALLOW_COPY_AND_ASSIGN (ProcessGDBRemote); + +}; + +#endif // liblldb_ProcessGDBRemote_h_ diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp new file mode 100644 index 000000000000..15b861feaa82 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp @@ -0,0 +1,196 @@ +//===-- ProcessGDBRemoteLog.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ProcessGDBRemoteLog.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/StreamFile.h" + +#include "ProcessGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_lob_sp the first time this function is +// called. +static bool g_log_enabled = false; +static Log * g_log = NULL; +static Log * +GetLog () +{ + if (!g_log_enabled) + return NULL; + return g_log; +} + + +Log * +ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log(GetLog ()); + if (log && mask) + { + uint32_t log_mask = log->GetMask().Get(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +Log * +ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (uint32_t mask) +{ + Log *log(GetLog ()); + if (log && log->GetMask().Get() & mask) + return log; + return NULL; +} + +void +ProcessGDBRemoteLog::DisableLog (const char **categories, Stream *feedback_strm) +{ + Log *log (GetLog ()); + if (log) + { + uint32_t flag_bits = 0; + + if (categories[0] != NULL) + { + flag_bits = log->GetMask().Get(); + for (size_t i = 0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + + if (::strcasecmp (arg, "all") == 0 ) flag_bits &= ~GDBR_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits &= ~GDBR_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits &= ~GDBR_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits &= ~GDBR_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits &= ~GDBR_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits &= ~GDBR_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits &= ~GDBR_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits &= ~GDBR_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits &= ~GDBR_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits &= ~GDBR_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits &= ~GDBR_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits &= ~GDBR_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits &= ~GDBR_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits &= ~GDBR_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories (feedback_strm); + } + + } + } + + if (flag_bits == 0) + g_log_enabled = false; + else + log->GetMask().Reset (flag_bits); + } + + return; +} + +Log * +ProcessGDBRemoteLog::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const char **categories, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits = 0; + if (g_log) + flag_bits = g_log->GetMask().Get(); + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + bool got_unknown_category = false; + for (size_t i=0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + if (::strcasecmp (arg, "all") == 0 ) flag_bits |= GDBR_LOG_ALL; + else if (::strcasecmp (arg, "async") == 0 ) flag_bits |= GDBR_LOG_ASYNC; + else if (::strncasecmp (arg, "break", 5) == 0 ) flag_bits |= GDBR_LOG_BREAKPOINTS; + else if (::strncasecmp (arg, "comm", 4) == 0 ) flag_bits |= GDBR_LOG_COMM; + else if (::strcasecmp (arg, "default") == 0 ) flag_bits |= GDBR_LOG_DEFAULT; + else if (::strcasecmp (arg, "packets") == 0 ) flag_bits |= GDBR_LOG_PACKETS; + else if (::strcasecmp (arg, "memory") == 0 ) flag_bits |= GDBR_LOG_MEMORY; + else if (::strcasecmp (arg, "data-short") == 0 ) flag_bits |= GDBR_LOG_MEMORY_DATA_SHORT; + else if (::strcasecmp (arg, "data-long") == 0 ) flag_bits |= GDBR_LOG_MEMORY_DATA_LONG; + else if (::strcasecmp (arg, "process") == 0 ) flag_bits |= GDBR_LOG_PROCESS; + else if (::strcasecmp (arg, "step") == 0 ) flag_bits |= GDBR_LOG_STEP; + else if (::strcasecmp (arg, "thread") == 0 ) flag_bits |= GDBR_LOG_THREAD; + else if (::strcasecmp (arg, "verbose") == 0 ) flag_bits |= GDBR_LOG_VERBOSE; + else if (::strncasecmp (arg, "watch", 5) == 0 ) flag_bits |= GDBR_LOG_WATCHPOINTS; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListLogCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = GDBR_LOG_DEFAULT; + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + } + g_log_enabled = true; + return g_log; +} + +void +ProcessGDBRemoteLog::ListLogCategories (Stream *strm) +{ + strm->Printf ("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " async - log asynchronous activity\n" + " break - log breakpoints\n" + " communication - log communication activity\n" + " default - enable the default set of logging categories for liblldb\n" + " packets - log gdb remote packets\n" + " memory - log memory reads and writes\n" + " data-short - log memory bytes for memory reads and writes for short transactions only\n" + " data-long - log memory bytes for memory reads and writes for all transactions\n" + " process - log process events and activities\n" + " thread - log thread events and activities\n" + " step - log step related activities\n" + " verbose - enable verbose logging\n" + " watch - log watchpoint related activities\n", ProcessGDBRemote::GetPluginNameStatic().GetCString()); +} + + +void +ProcessGDBRemoteLog::LogIf (uint32_t mask, const char *format, ...) +{ + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h new file mode 100644 index 000000000000..93734067f133 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h @@ -0,0 +1,57 @@ +//===-- ProcessGDBRemoteLog.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessGDBRemoteLog_h_ +#define liblldb_ProcessGDBRemoteLog_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define GDBR_LOG_VERBOSE (1u << 0) +#define GDBR_LOG_PROCESS (1u << 1) +#define GDBR_LOG_THREAD (1u << 2) +#define GDBR_LOG_PACKETS (1u << 3) +#define GDBR_LOG_MEMORY (1u << 4) // Log memory reads/writes calls +#define GDBR_LOG_MEMORY_DATA_SHORT (1u << 5) // Log short memory reads/writes bytes +#define GDBR_LOG_MEMORY_DATA_LONG (1u << 6) // Log all memory reads/writes bytes +#define GDBR_LOG_BREAKPOINTS (1u << 7) +#define GDBR_LOG_WATCHPOINTS (1u << 8) +#define GDBR_LOG_STEP (1u << 9) +#define GDBR_LOG_COMM (1u << 10) +#define GDBR_LOG_ASYNC (1u << 11) +#define GDBR_LOG_ALL (UINT32_MAX) +#define GDBR_LOG_DEFAULT GDBR_LOG_PACKETS + +class ProcessGDBRemoteLog +{ +public: + static lldb_private::Log * + GetLogIfAllCategoriesSet(uint32_t mask = 0); + + static lldb_private::Log * + GetLogIfAnyCategoryIsSet (uint32_t mask); + + static void + DisableLog (const char **categories, lldb_private::Stream *feedback_strm); + + static lldb_private::Log * + EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, lldb_private::Stream *feedback_strm); + + static void + ListLogCategories (lldb_private::Stream *strm); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // liblldb_ProcessGDBRemoteLog_h_ diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp new file mode 100644 index 000000000000..38fb84d66ef3 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp @@ -0,0 +1,214 @@ +//===-- ThreadGDBRemote.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ThreadGDBRemote.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/State.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Unwind.h" +#include "lldb/Breakpoint/Watchpoint.h" + +#include "ProcessGDBRemote.h" +#include "ProcessGDBRemoteLog.h" +#include "Utility/StringExtractorGDBRemote.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Thread Registers +//---------------------------------------------------------------------- + +ThreadGDBRemote::ThreadGDBRemote (Process &process, lldb::tid_t tid) : + Thread(process, tid), + m_thread_name (), + m_dispatch_queue_name (), + m_thread_dispatch_qaddr (LLDB_INVALID_ADDRESS) +{ + ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", + this, + process.GetID(), + GetID()); +} + +ThreadGDBRemote::~ThreadGDBRemote () +{ + ProcessSP process_sp(GetProcess()); + ProcessGDBRemoteLog::LogIf(GDBR_LOG_THREAD, "%p: ThreadGDBRemote::~ThreadGDBRemote (pid = %i, tid = 0x%4.4x)", + this, + process_sp ? process_sp->GetID() : LLDB_INVALID_PROCESS_ID, + GetID()); + DestroyThread(); +} + +const char * +ThreadGDBRemote::GetName () +{ + if (m_thread_name.empty()) + return NULL; + return m_thread_name.c_str(); +} + + +const char * +ThreadGDBRemote::GetQueueName () +{ + // Always re-fetch the dispatch queue name since it can change + + if (m_thread_dispatch_qaddr != 0 || m_thread_dispatch_qaddr != LLDB_INVALID_ADDRESS) + { + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get()); + return gdb_process->GetDispatchQueueNameForThread (m_thread_dispatch_qaddr, m_dispatch_queue_name); + } + } + return NULL; +} + +void +ThreadGDBRemote::WillResume (StateType resume_state) +{ + int signo = GetResumeSignal(); + const lldb::user_id_t tid = GetProtocolID(); + Log *log(lldb_private::GetLogIfAnyCategoriesSet (GDBR_LOG_THREAD)); + if (log) + log->Printf ("Resuming thread: %4.4" PRIx64 " with state: %s.", tid, StateAsCString(resume_state)); + + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get()); + switch (resume_state) + { + case eStateSuspended: + case eStateStopped: + // Don't append anything for threads that should stay stopped. + break; + + case eStateRunning: + if (gdb_process->GetUnixSignals().SignalIsValid (signo)) + gdb_process->m_continue_C_tids.push_back(std::make_pair(tid, signo)); + else + gdb_process->m_continue_c_tids.push_back(tid); + break; + + case eStateStepping: + if (gdb_process->GetUnixSignals().SignalIsValid (signo)) + gdb_process->m_continue_S_tids.push_back(std::make_pair(tid, signo)); + else + gdb_process->m_continue_s_tids.push_back(tid); + break; + + default: + break; + } + } +} + +void +ThreadGDBRemote::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The GDBRemoteRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + const bool force = false; + GetRegisterContext()->InvalidateIfNeeded (force); +} + +bool +ThreadGDBRemote::ThreadIDIsValid (lldb::tid_t thread) +{ + return thread != 0; +} + +void +ThreadGDBRemote::Dump(Log *log, uint32_t index) +{ +} + + +bool +ThreadGDBRemote::ShouldStop (bool &step_more) +{ + return true; +} +lldb::RegisterContextSP +ThreadGDBRemote::GetRegisterContext () +{ + if (m_reg_context_sp.get() == NULL) + m_reg_context_sp = CreateRegisterContextForFrame (NULL); + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadGDBRemote::CreateRegisterContextForFrame (StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + const bool read_all_registers_at_once = false; + uint32_t concrete_frame_idx = 0; + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex (); + + + if (concrete_frame_idx == 0) + { + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get()); + reg_ctx_sp.reset (new GDBRemoteRegisterContext (*this, concrete_frame_idx, gdb_process->m_register_info, read_all_registers_at_once)); + } + } + else + { + Unwind *unwinder = GetUnwinder (); + if (unwinder) + reg_ctx_sp = unwinder->CreateRegisterContextForFrame (frame); + } + return reg_ctx_sp; +} + +bool +ThreadGDBRemote::PrivateSetRegisterValue (uint32_t reg, StringExtractor &response) +{ + GDBRemoteRegisterContext *gdb_reg_ctx = static_cast<GDBRemoteRegisterContext *>(GetRegisterContext ().get()); + assert (gdb_reg_ctx); + return gdb_reg_ctx->PrivateSetRegisterValue (reg, response); +} + +bool +ThreadGDBRemote::CalculateStopInfo () +{ + ProcessSP process_sp (GetProcess()); + if (process_sp) + { + StringExtractorGDBRemote stop_packet; + ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get()); + if (gdb_process->GetGDBRemote().GetThreadStopInfo(GetProtocolID(), stop_packet)) + return gdb_process->SetThreadStopInfo (stop_packet) == eStateStopped; + } + return false; +} + + diff --git a/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h new file mode 100644 index 000000000000..50a3f19c6505 --- /dev/null +++ b/source/Plugins/Process/gdb-remote/ThreadGDBRemote.h @@ -0,0 +1,107 @@ +//===-- ThreadGDBRemote.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadGDBRemote_h_ +#define liblldb_ThreadGDBRemote_h_ + +#include <string> + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" + +class StringExtractor; +class ProcessGDBRemote; + +class ThreadGDBRemote : public lldb_private::Thread +{ +public: + ThreadGDBRemote (lldb_private::Process &process, lldb::tid_t tid); + + virtual + ~ThreadGDBRemote (); + + virtual void + WillResume (lldb::StateType resume_state); + + virtual void + RefreshStateAfterStop(); + + virtual const char * + GetName (); + + virtual const char * + GetQueueName (); + + virtual lldb::RegisterContextSP + GetRegisterContext (); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + void + Dump (lldb_private::Log *log, uint32_t index); + + static bool + ThreadIDIsValid (lldb::tid_t thread); + + bool + ShouldStop (bool &step_more); + + const char * + GetBasicInfoAsString (); + + void + SetName (const char *name) + { + if (name && name[0]) + m_thread_name.assign (name); + else + m_thread_name.clear(); + } + + lldb::addr_t + GetThreadDispatchQAddr () + { + return m_thread_dispatch_qaddr; + } + + void + SetThreadDispatchQAddr (lldb::addr_t thread_dispatch_qaddr) + { + m_thread_dispatch_qaddr = thread_dispatch_qaddr; + } + +protected: + + friend class ProcessGDBRemote; + + bool + PrivateSetRegisterValue (uint32_t reg, + StringExtractor &response); + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + std::string m_thread_name; + std::string m_dispatch_queue_name; + lldb::addr_t m_thread_dispatch_qaddr; + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + + void + SetStopInfoFromPacket (StringExtractor &stop_packet, uint32_t stop_id); + + virtual bool + CalculateStopInfo (); + + +}; + +#endif // liblldb_ThreadGDBRemote_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp b/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp new file mode 100644 index 000000000000..06e87eab33ce --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp @@ -0,0 +1,211 @@ +//===-- DWARFAbbreviationDeclaration.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFAbbreviationDeclaration.h" + +#include "lldb/Core/dwarf.h" + +#include "DWARFFormValue.h" + +using namespace lldb_private; + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration() : + m_code (InvalidCode), + m_tag (0), + m_has_children (0), + m_attributes() +{ +} + +DWARFAbbreviationDeclaration::DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children) : + m_code (InvalidCode), + m_tag (tag), + m_has_children (has_children), + m_attributes() +{ +} + +bool +DWARFAbbreviationDeclaration::Extract(const DataExtractor& data, lldb::offset_t* offset_ptr) +{ + return Extract(data, offset_ptr, data.GetULEB128(offset_ptr)); +} + +bool +DWARFAbbreviationDeclaration::Extract(const DataExtractor& data, lldb::offset_t *offset_ptr, dw_uleb128_t code) +{ + m_code = code; + m_attributes.clear(); + if (m_code) + { + m_tag = data.GetULEB128(offset_ptr); + m_has_children = data.GetU8(offset_ptr); + + while (data.ValidOffset(*offset_ptr)) + { + dw_attr_t attr = data.GetULEB128(offset_ptr); + dw_form_t form = data.GetULEB128(offset_ptr); + + if (attr && form) + m_attributes.push_back(DWARFAttribute(attr, form)); + else + break; + } + + return m_tag != 0; + } + else + { + m_tag = 0; + m_has_children = 0; + } + + return false; +} + + +void +DWARFAbbreviationDeclaration::Dump(Stream *s) const +{ +// *ostrm_ptr << std::setfill(' ') << std::dec << '[' << std::setw(3) << std::right << m_code << ']' << ' ' << std::setw(30) << std::left << DW_TAG_value_to_name(m_tag) << DW_CHILDREN_value_to_name(m_has_children) << std::endl; +// +// DWARFAttribute::const_iterator pos; +// +// for (pos = m_attributes.begin(); pos != m_attributes.end(); ++pos) +// *ostrm_ptr << " " << std::setw(29) << std::left << DW_AT_value_to_name(pos->attr()) << ' ' << DW_FORM_value_to_name(pos->form()) << std::endl; +// +// *ostrm_ptr << std::endl; +} + + + +bool +DWARFAbbreviationDeclaration::IsValid() +{ + return m_code != 0 && m_tag != 0; +} + + +void +DWARFAbbreviationDeclaration::CopyExcludingAddressAttributes(const DWARFAbbreviationDeclaration& abbr_decl, const uint32_t idx) +{ + m_code = abbr_decl.Code(); // Invalidate the code since that can't be copied safely. + m_tag = abbr_decl.Tag(); + m_has_children = abbr_decl.HasChildren(); + + const DWARFAttribute::collection& attributes = abbr_decl.Attributes(); + const uint32_t num_abbr_decl_attributes = attributes.size(); + + dw_attr_t attr; + dw_form_t form; + uint32_t i; + + for (i = 0; i < num_abbr_decl_attributes; ++i) + { + attributes[i].get(attr, form); + switch (attr) + { + case DW_AT_location: + case DW_AT_frame_base: + // Only add these if they are location expressions (have a single + // value) and not location lists (have a lists of location + // expressions which are only valid over specific address ranges) + if (DWARFFormValue::IsBlockForm(form)) + m_attributes.push_back(DWARFAttribute(attr, form)); + break; + + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_ranges: + case DW_AT_entry_pc: + // Don't add these attributes + if (i >= idx) + break; + // Fall through and add attribute + default: + // Add anything that isn't address related + m_attributes.push_back(DWARFAttribute(attr, form)); + break; + } + } +} + +void +DWARFAbbreviationDeclaration::CopyChangingStringToStrp( + const DWARFAbbreviationDeclaration& abbr_decl, + const DataExtractor& debug_info_data, + dw_offset_t debug_info_offset, + const DWARFCompileUnit* cu, + const uint32_t strp_min_len +) +{ + m_code = InvalidCode; + m_tag = abbr_decl.Tag(); + m_has_children = abbr_decl.HasChildren(); + + const DWARFAttribute::collection& attributes = abbr_decl.Attributes(); + const uint32_t num_abbr_decl_attributes = attributes.size(); + + dw_attr_t attr; + dw_form_t form; + uint32_t i; + lldb::offset_t offset = debug_info_offset; + + for (i = 0; i < num_abbr_decl_attributes; ++i) + { + attributes[i].get(attr, form); + dw_offset_t attr_offset = offset; + DWARFFormValue::SkipValue(form, debug_info_data, &offset, cu); + + if (form == DW_FORM_string && ((offset - attr_offset) >= strp_min_len)) + m_attributes.push_back(DWARFAttribute(attr, DW_FORM_strp)); + else + m_attributes.push_back(DWARFAttribute(attr, form)); + } +} + + +uint32_t +DWARFAbbreviationDeclaration::FindAttributeIndex(dw_attr_t attr) const +{ + uint32_t i; + const uint32_t kNumAttributes = m_attributes.size(); + for (i = 0; i < kNumAttributes; ++i) + { + if (m_attributes[i].get_attr() == attr) + return i; + } + return DW_INVALID_INDEX; +} + + +bool +DWARFAbbreviationDeclaration::operator == (const DWARFAbbreviationDeclaration& rhs) const +{ + return Tag() == rhs.Tag() + && HasChildren() == rhs.HasChildren() + && Attributes() == rhs.Attributes(); +} + +#if 0 +DWARFAbbreviationDeclaration::Append(BinaryStreamBuf& out_buff) const +{ + out_buff.Append32_as_ULEB128(Code()); + out_buff.Append32_as_ULEB128(Tag()); + out_buff.Append8(HasChildren()); + const uint32_t kNumAttributes = m_attributes.size(); + for (uint32_t i = 0; i < kNumAttributes; ++i) + { + out_buff.Append32_as_ULEB128(m_attributes[i].attr()); + out_buff.Append32_as_ULEB128(m_attributes[i].form()); + } + out_buff.Append8(0); // Output a zero for attr (faster than using Append32_as_ULEB128) + out_buff.Append8(0); // Output a zero for attr (faster than using Append32_as_ULEB128) +} +#endif // 0 diff --git a/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h b/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h new file mode 100644 index 000000000000..f462b7fc108d --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h @@ -0,0 +1,81 @@ +//===-- DWARFAbbreviationDeclaration.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFAbbreviationDeclaration_h_ +#define liblldb_DWARFAbbreviationDeclaration_h_ + +#include "SymbolFileDWARF.h" +#include "DWARFAttribute.h" + +class DWARFCompileUnit; + +class DWARFAbbreviationDeclaration +{ +public: + enum { InvalidCode = 0 }; + DWARFAbbreviationDeclaration(); + + // For hand crafting an abbreviation declaration + DWARFAbbreviationDeclaration(dw_tag_t tag, uint8_t has_children); + void AddAttribute(const DWARFAttribute& attr) + { + m_attributes.push_back(attr); + } + + dw_uleb128_t Code() const { return m_code; } + void SetCode(dw_uleb128_t code) { m_code = code; } + dw_tag_t Tag() const { return m_tag; } + bool HasChildren() const { return m_has_children; } + size_t NumAttributes() const { return m_attributes.size(); } + dw_attr_t GetAttrByIndex(uint32_t idx) const { return m_attributes.size() > idx ? m_attributes[idx].get_attr() : 0; } + dw_form_t GetFormByIndex(uint32_t idx) const { return m_attributes.size() > idx ? m_attributes[idx].get_form() : 0; } + bool GetAttrAndFormByIndex(uint32_t idx, dw_attr_t& attr, dw_form_t& form) const + { + if (m_attributes.size() > idx) + { + m_attributes[idx].get(attr, form); + return true; + } + attr = form = 0; + return false; + } + + // idx is assumed to be valid when calling GetAttrAndFormByIndexUnchecked() + void GetAttrAndFormByIndexUnchecked(uint32_t idx, dw_attr_t& attr, dw_form_t& form) const + { + m_attributes[idx].get(attr, form); + } + dw_form_t GetFormByIndexUnchecked (uint32_t idx) const + { + return m_attributes[idx].get_form(); + } + void CopyExcludingAddressAttributes(const DWARFAbbreviationDeclaration& abbr_decl, const uint32_t idx); + void CopyChangingStringToStrp( + const DWARFAbbreviationDeclaration& abbr_decl, + const lldb_private::DataExtractor& debug_info_data, + dw_offset_t debug_info_offset, + const DWARFCompileUnit* cu, + const uint32_t strp_min_len); + uint32_t FindAttributeIndex(dw_attr_t attr) const; + bool Extract(const lldb_private::DataExtractor& data, lldb::offset_t *offset_ptr); + bool Extract(const lldb_private::DataExtractor& data, lldb::offset_t *offset_ptr, dw_uleb128_t code); +// void Append(BinaryStreamBuf& out_buff) const; + bool IsValid(); + void Dump(lldb_private::Stream *s) const; + bool operator == (const DWARFAbbreviationDeclaration& rhs) const; +// DWARFAttribute::collection& Attributes() { return m_attributes; } + const DWARFAttribute::collection& Attributes() const { return m_attributes; } +protected: + dw_uleb128_t m_code; + dw_tag_t m_tag; + uint8_t m_has_children; + DWARFAttribute::collection m_attributes; +}; + +#endif // liblldb_DWARFAbbreviationDeclaration_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h b/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h new file mode 100644 index 000000000000..8310b1dda5f1 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFAttribute.h @@ -0,0 +1,45 @@ +//===-- DWARFAttribute.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFAttribute_h_ +#define SymbolFileDWARF_DWARFAttribute_h_ + +#include "DWARFDefines.h" +#include <vector> + +class DWARFAttribute +{ +public: + DWARFAttribute(dw_attr_t attr, dw_form_t form) : + m_attr_form ( attr << 16 | form ) + { + } + + void set(dw_attr_t attr, dw_form_t form) { m_attr_form = (attr << 16) | form; } + void set_attr(dw_attr_t attr) { m_attr_form = (m_attr_form & 0x0000ffffu) | (attr << 16); } + void set_form(dw_form_t form) { m_attr_form = (m_attr_form & 0xffff0000u) | form; } + dw_attr_t get_attr() const { return m_attr_form >> 16; } + dw_form_t get_form() const { return (dw_form_t)m_attr_form; } + void get(dw_attr_t& attr, dw_form_t& form) const + { + register uint32_t attr_form = m_attr_form; + attr = attr_form >> 16; + form = (dw_form_t)attr_form; + } + bool operator == (const DWARFAttribute& rhs) const { return m_attr_form == rhs.m_attr_form; } + typedef std::vector<DWARFAttribute> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + +protected: + uint32_t m_attr_form; // Upper 16 bits is attribute, lower 16 bits is form +}; + + +#endif // SymbolFileDWARF_DWARFAttribute_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp b/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp new file mode 100644 index 000000000000..493b3af6ecd1 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp @@ -0,0 +1,1027 @@ +//===-- DWARFCompileUnit.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFCompileUnit.h" + +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ObjCLanguageRuntime.h" + +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "LogChannelDWARF.h" +#include "NameToDIE.h" +#include "SymbolFileDWARF.h" +#include "SymbolFileDWARFDebugMap.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + + +extern int g_verbose; + +DWARFCompileUnit::DWARFCompileUnit(SymbolFileDWARF* dwarf2Data) : + m_dwarf2Data (dwarf2Data), + m_abbrevs (NULL), + m_user_data (NULL), + m_die_array (), + m_func_aranges_ap (), + m_base_addr (0), + m_offset (DW_INVALID_OFFSET), + m_length (0), + m_version (0), + m_addr_size (DWARFCompileUnit::GetDefaultAddressSize()), + m_producer (eProducerInvalid), + m_producer_version_major (0), + m_producer_version_minor (0), + m_producer_version_update (0) +{ +} + +void +DWARFCompileUnit::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_length = 0; + m_version = 0; + m_abbrevs = NULL; + m_addr_size = DWARFCompileUnit::GetDefaultAddressSize(); + m_base_addr = 0; + m_die_array.clear(); + m_func_aranges_ap.reset(); + m_user_data = NULL; + m_producer = eProducerInvalid; +} + +bool +DWARFCompileUnit::Extract(const DataExtractor &debug_info, lldb::offset_t *offset_ptr) +{ + Clear(); + + m_offset = *offset_ptr; + + if (debug_info.ValidOffset(*offset_ptr)) + { + dw_offset_t abbr_offset; + const DWARFDebugAbbrev *abbr = m_dwarf2Data->DebugAbbrev(); + m_length = debug_info.GetU32(offset_ptr); + m_version = debug_info.GetU16(offset_ptr); + abbr_offset = debug_info.GetU32(offset_ptr); + m_addr_size = debug_info.GetU8 (offset_ptr); + + bool length_OK = debug_info.ValidOffset(GetNextCompileUnitOffset()-1); + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool abbr_offset_OK = m_dwarf2Data->get_debug_abbrev_data().ValidOffset(abbr_offset); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + + if (length_OK && version_OK && addr_size_OK && abbr_offset_OK && abbr != NULL) + { + m_abbrevs = abbr->GetAbbreviationDeclarationSet(abbr_offset); + return true; + } + + // reset the offset to where we tried to parse from if anything went wrong + *offset_ptr = m_offset; + } + + return false; +} + + +dw_offset_t +DWARFCompileUnit::Extract(lldb::offset_t offset, const DataExtractor& debug_info_data, const DWARFAbbreviationDeclarationSet* abbrevs) +{ + Clear(); + + m_offset = offset; + + if (debug_info_data.ValidOffset(offset)) + { + m_length = debug_info_data.GetU32(&offset); + m_version = debug_info_data.GetU16(&offset); + bool abbrevs_OK = debug_info_data.GetU32(&offset) == abbrevs->GetOffset(); + m_abbrevs = abbrevs; + m_addr_size = debug_info_data.GetU8 (&offset); + + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + + if (version_OK && addr_size_OK && abbrevs_OK && debug_info_data.ValidOffset(offset)) + return offset; + } + return DW_INVALID_OFFSET; +} + +void +DWARFCompileUnit::ClearDIEs(bool keep_compile_unit_die) +{ + if (m_die_array.size() > 1) + { + // std::vectors never get any smaller when resized to a smaller size, + // or when clear() or erase() are called, the size will report that it + // is smaller, but the memory allocated remains intact (call capacity() + // to see this). So we need to create a temporary vector and swap the + // contents which will cause just the internal pointers to be swapped + // so that when "tmp_array" goes out of scope, it will destroy the + // contents. + + // Save at least the compile unit DIE + DWARFDebugInfoEntry::collection tmp_array; + m_die_array.swap(tmp_array); + if (keep_compile_unit_die) + m_die_array.push_back(tmp_array.front()); + } +} + +//---------------------------------------------------------------------- +// ParseCompileUnitDIEsIfNeeded +// +// Parses a compile unit and indexes its DIEs if it hasn't already been +// done. +//---------------------------------------------------------------------- +size_t +DWARFCompileUnit::ExtractDIEsIfNeeded (bool cu_die_only) +{ + const size_t initial_die_array_size = m_die_array.size(); + if ((cu_die_only && initial_die_array_size > 0) || initial_die_array_size > 1) + return 0; // Already parsed + + Timer scoped_timer (__PRETTY_FUNCTION__, + "%8.8x: DWARFCompileUnit::ExtractDIEsIfNeeded( cu_die_only = %i )", + m_offset, + cu_die_only); + + // Set the offset to that of the first DIE and calculate the start of the + // next compilation unit header. + lldb::offset_t offset = GetFirstDIEOffset(); + lldb::offset_t next_cu_offset = GetNextCompileUnitOffset(); + + DWARFDebugInfoEntry die; + // Keep a flat array of the DIE for binary lookup by DIE offset + if (!cu_die_only) + { + Log *log (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | DWARF_LOG_LOOKUPS)); + if (log) + { + m_dwarf2Data->GetObjectFile()->GetModule()->LogMessageVerboseBacktrace (log, + "DWARFCompileUnit::ExtractDIEsIfNeeded () for compile unit at .debug_info[0x%8.8x]", + GetOffset()); + } + } + + uint32_t depth = 0; + // We are in our compile unit, parse starting at the offset + // we were told to parse + const DataExtractor& debug_info_data = m_dwarf2Data->get_debug_info_data(); + std::vector<uint32_t> die_index_stack; + die_index_stack.reserve(32); + die_index_stack.push_back(0); + bool prev_die_had_children = false; + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (GetAddressByteSize()); + while (offset < next_cu_offset && + die.FastExtract (debug_info_data, this, fixed_form_sizes, &offset)) + { +// if (log) +// log->Printf("0x%8.8x: %*.*s%s%s", +// die.GetOffset(), +// depth * 2, depth * 2, "", +// DW_TAG_value_to_name (die.Tag()), +// die.HasChildren() ? " *" : ""); + + const bool null_die = die.IsNULL(); + if (depth == 0) + { + uint64_t base_addr = die.GetAttributeValueAsUnsigned(m_dwarf2Data, this, DW_AT_low_pc, LLDB_INVALID_ADDRESS); + if (base_addr == LLDB_INVALID_ADDRESS) + base_addr = die.GetAttributeValueAsUnsigned(m_dwarf2Data, this, DW_AT_entry_pc, 0); + SetBaseAddress (base_addr); + if (initial_die_array_size == 0) + AddDIE (die); + if (cu_die_only) + return 1; + } + else + { + if (null_die) + { + if (prev_die_had_children) + { + // This will only happen if a DIE says is has children + // but all it contains is a NULL tag. Since we are removing + // the NULL DIEs from the list (saves up to 25% in C++ code), + // we need a way to let the DIE know that it actually doesn't + // have children. + if (!m_die_array.empty()) + m_die_array.back().SetEmptyChildren(true); + } + } + else + { + die.SetParentIndex(m_die_array.size() - die_index_stack[depth-1]); + + if (die_index_stack.back()) + m_die_array[die_index_stack.back()].SetSiblingIndex(m_die_array.size()-die_index_stack.back()); + + // Only push the DIE if it isn't a NULL DIE + m_die_array.push_back(die); + } + } + + if (null_die) + { + // NULL DIE. + if (!die_index_stack.empty()) + die_index_stack.pop_back(); + + if (depth > 0) + --depth; + if (depth == 0) + break; // We are done with this compile unit! + + prev_die_had_children = false; + } + else + { + die_index_stack.back() = m_die_array.size() - 1; + // Normal DIE + const bool die_has_children = die.HasChildren(); + if (die_has_children) + { + die_index_stack.push_back(0); + ++depth; + } + prev_die_had_children = die_has_children; + } + } + + // Give a little bit of info if we encounter corrupt DWARF (our offset + // should always terminate at or before the start of the next compilation + // unit header). + if (offset > next_cu_offset) + { + m_dwarf2Data->GetObjectFile()->GetModule()->ReportWarning ("DWARF compile unit extends beyond its bounds cu 0x%8.8x at 0x%8.8" PRIx64 "\n", + GetOffset(), + offset); + } + + // Since std::vector objects will double their size, we really need to + // make a new array with the perfect size so we don't end up wasting + // space. So here we copy and swap to make sure we don't have any extra + // memory taken up. + + if (m_die_array.size () < m_die_array.capacity()) + { + DWARFDebugInfoEntry::collection exact_size_die_array (m_die_array.begin(), m_die_array.end()); + exact_size_die_array.swap (m_die_array); + } + Log *log (LogChannelDWARF::GetLogIfAll (DWARF_LOG_DEBUG_INFO | DWARF_LOG_VERBOSE)); + if (log) + { + StreamString strm; + DWARFDebugInfoEntry::DumpDIECollection (strm, m_die_array); + log->PutCString (strm.GetString().c_str()); + } + + return m_die_array.size(); +} + + +dw_offset_t +DWARFCompileUnit::GetAbbrevOffset() const +{ + return m_abbrevs ? m_abbrevs->GetOffset() : DW_INVALID_OFFSET; +} + + + +bool +DWARFCompileUnit::Verify(Stream *s) const +{ + const DataExtractor& debug_info = m_dwarf2Data->get_debug_info_data(); + bool valid_offset = debug_info.ValidOffset(m_offset); + bool length_OK = debug_info.ValidOffset(GetNextCompileUnitOffset()-1); + bool version_OK = SymbolFileDWARF::SupportedVersion(m_version); + bool abbr_offset_OK = m_dwarf2Data->get_debug_abbrev_data().ValidOffset(GetAbbrevOffset()); + bool addr_size_OK = ((m_addr_size == 4) || (m_addr_size == 8)); + bool verbose = s->GetVerbose(); + if (valid_offset && length_OK && version_OK && addr_size_OK && abbr_offset_OK) + { + if (verbose) + s->Printf(" 0x%8.8x: OK\n", m_offset); + return true; + } + else + { + s->Printf(" 0x%8.8x: ", m_offset); + + m_dwarf2Data->get_debug_info_data().Dump (s, m_offset, lldb::eFormatHex, 1, Size(), 32, LLDB_INVALID_ADDRESS, 0, 0); + s->EOL(); + if (valid_offset) + { + if (!length_OK) + s->Printf(" The length (0x%8.8x) for this compile unit is too large for the .debug_info provided.\n", m_length); + if (!version_OK) + s->Printf(" The 16 bit compile unit header version is not supported.\n"); + if (!abbr_offset_OK) + s->Printf(" The offset into the .debug_abbrev section (0x%8.8x) is not valid.\n", GetAbbrevOffset()); + if (!addr_size_OK) + s->Printf(" The address size is unsupported: 0x%2.2x\n", m_addr_size); + } + else + s->Printf(" The start offset of the compile unit header in the .debug_info is invalid.\n"); + } + return false; +} + + +void +DWARFCompileUnit::Dump(Stream *s) const +{ + s->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at {0x%8.8x})\n", + m_offset, m_length, m_version, GetAbbrevOffset(), m_addr_size, GetNextCompileUnitOffset()); +} + + +static uint8_t g_default_addr_size = 4; + +uint8_t +DWARFCompileUnit::GetAddressByteSize(const DWARFCompileUnit* cu) +{ + if (cu) + return cu->GetAddressByteSize(); + return DWARFCompileUnit::GetDefaultAddressSize(); +} + +uint8_t +DWARFCompileUnit::GetDefaultAddressSize() +{ + return g_default_addr_size; +} + +void +DWARFCompileUnit::SetDefaultAddressSize(uint8_t addr_size) +{ + g_default_addr_size = addr_size; +} + +void +DWARFCompileUnit::BuildAddressRangeTable (SymbolFileDWARF* dwarf2Data, + DWARFDebugAranges* debug_aranges, + bool clear_dies_if_already_not_parsed) +{ + // This function is usually called if there in no .debug_aranges section + // in order to produce a compile unit level set of address ranges that + // is accurate. If the DIEs weren't parsed, then we don't want all dies for + // all compile units to stay loaded when they weren't needed. So we can end + // up parsing the DWARF and then throwing them all away to keep memory usage + // down. + const bool clear_dies = ExtractDIEsIfNeeded (false) > 1; + + const DWARFDebugInfoEntry* die = DIE(); + if (die) + die->BuildAddressRangeTable(dwarf2Data, this, debug_aranges); + + if (debug_aranges->IsEmpty()) + { + // We got nothing from the functions, maybe we have a line tables only + // situation. Check the line tables and build the arange table from this. + SymbolContext sc; + sc.comp_unit = dwarf2Data->GetCompUnitForDWARFCompUnit(this); + if (sc.comp_unit) + { + SymbolFileDWARFDebugMap *debug_map_sym_file = m_dwarf2Data->GetDebugMapSymfile(); + if (debug_map_sym_file == NULL) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + + if (line_table) + { + LineTable::FileAddressRanges file_ranges; + const bool append = true; + const size_t num_ranges = line_table->GetContiguousFileAddressRanges (file_ranges, append); + for (uint32_t idx=0; idx<num_ranges; ++idx) + { + const LineTable::FileAddressRanges::Entry &range = file_ranges.GetEntryRef(idx); + debug_aranges->AppendRange(GetOffset(), range.GetRangeBase(), range.GetRangeEnd()); + printf ("0x%8.8x: [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")\n", GetOffset(), range.GetRangeBase(), range.GetRangeEnd()); + } + } + } + else + debug_map_sym_file->AddOSOARanges(dwarf2Data,debug_aranges); + } + } + + // Keep memory down by clearing DIEs if this generate function + // caused them to be parsed + if (clear_dies) + ClearDIEs (true); + +} + + +const DWARFDebugAranges & +DWARFCompileUnit::GetFunctionAranges () +{ + if (m_func_aranges_ap.get() == NULL) + { + m_func_aranges_ap.reset (new DWARFDebugAranges()); + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_ARANGES)); + + if (log) + { + m_dwarf2Data->GetObjectFile()->GetModule()->LogMessage (log, + "DWARFCompileUnit::GetFunctionAranges() for compile unit at .debug_info[0x%8.8x]", + GetOffset()); + } + const DWARFDebugInfoEntry* die = DIE(); + if (die) + die->BuildFunctionAddressRangeTable (m_dwarf2Data, this, m_func_aranges_ap.get()); + const bool minimize = false; + m_func_aranges_ap->Sort(minimize); + } + return *m_func_aranges_ap.get(); +} + +bool +DWARFCompileUnit::LookupAddress +( + const dw_addr_t address, + DWARFDebugInfoEntry** function_die_handle, + DWARFDebugInfoEntry** block_die_handle +) +{ + bool success = false; + + if (function_die_handle != NULL && DIE()) + { + + const DWARFDebugAranges &func_aranges = GetFunctionAranges (); + + // Re-check the aranges auto pointer contents in case it was created above + if (!func_aranges.IsEmpty()) + { + *function_die_handle = GetDIEPtr(func_aranges.FindAddress(address)); + if (*function_die_handle != NULL) + { + success = true; + if (block_die_handle != NULL) + { + DWARFDebugInfoEntry* child = (*function_die_handle)->GetFirstChild(); + while (child) + { + if (child->LookupAddress(address, m_dwarf2Data, this, NULL, block_die_handle)) + break; + child = child->GetSibling(); + } + } + } + } + } + return success; +} + +//---------------------------------------------------------------------- +// Compare function DWARFDebugAranges::Range structures +//---------------------------------------------------------------------- +static bool CompareDIEOffset (const DWARFDebugInfoEntry& die1, const DWARFDebugInfoEntry& die2) +{ + return die1.GetOffset() < die2.GetOffset(); +} + +//---------------------------------------------------------------------- +// GetDIEPtr() +// +// Get the DIE (Debug Information Entry) with the specified offset. +//---------------------------------------------------------------------- +DWARFDebugInfoEntry* +DWARFCompileUnit::GetDIEPtr(dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + ExtractDIEsIfNeeded (false); + DWARFDebugInfoEntry compare_die; + compare_die.SetOffset(die_offset); + DWARFDebugInfoEntry::iterator end = m_die_array.end(); + DWARFDebugInfoEntry::iterator pos = lower_bound(m_die_array.begin(), end, compare_die, CompareDIEOffset); + if (pos != end) + { + if (die_offset == (*pos).GetOffset()) + return &(*pos); + } + } + return NULL; // Not found in any compile units +} + +//---------------------------------------------------------------------- +// GetDIEPtrContainingOffset() +// +// Get the DIE (Debug Information Entry) that contains the specified +// .debug_info offset. +//---------------------------------------------------------------------- +const DWARFDebugInfoEntry* +DWARFCompileUnit::GetDIEPtrContainingOffset(dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + ExtractDIEsIfNeeded (false); + DWARFDebugInfoEntry compare_die; + compare_die.SetOffset(die_offset); + DWARFDebugInfoEntry::iterator end = m_die_array.end(); + DWARFDebugInfoEntry::iterator pos = lower_bound(m_die_array.begin(), end, compare_die, CompareDIEOffset); + if (pos != end) + { + if (die_offset >= (*pos).GetOffset()) + { + DWARFDebugInfoEntry::iterator next = pos + 1; + if (next != end) + { + if (die_offset < (*next).GetOffset()) + return &(*pos); + } + } + } + } + return NULL; // Not found in any compile units +} + + + +size_t +DWARFCompileUnit::AppendDIEsWithTag (const dw_tag_t tag, DWARFDIECollection& dies, uint32_t depth) const +{ + size_t old_size = dies.Size(); + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + for (pos = m_die_array.begin(); pos != end; ++pos) + { + if (pos->Tag() == tag) + dies.Append (&(*pos)); + } + + // Return the number of DIEs added to the collection + return dies.Size() - old_size; +} + +//void +//DWARFCompileUnit::AddGlobalDIEByIndex (uint32_t die_idx) +//{ +// m_global_die_indexes.push_back (die_idx); +//} +// +// +//void +//DWARFCompileUnit::AddGlobal (const DWARFDebugInfoEntry* die) +//{ +// // Indexes to all file level global and static variables +// m_global_die_indexes; +// +// if (m_die_array.empty()) +// return; +// +// const DWARFDebugInfoEntry* first_die = &m_die_array[0]; +// const DWARFDebugInfoEntry* end = first_die + m_die_array.size(); +// if (first_die <= die && die < end) +// m_global_die_indexes.push_back (die - first_die); +//} + + +void +DWARFCompileUnit::Index (const uint32_t cu_idx, + NameToDIE& func_basenames, + NameToDIE& func_fullnames, + NameToDIE& func_methods, + NameToDIE& func_selectors, + NameToDIE& objc_class_selectors, + NameToDIE& globals, + NameToDIE& types, + NameToDIE& namespaces) +{ + const DataExtractor* debug_str = &m_dwarf2Data->get_debug_str_data(); + + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (GetAddressByteSize()); + + Log *log (LogChannelDWARF::GetLogIfAll (DWARF_LOG_LOOKUPS)); + + if (log) + { + m_dwarf2Data->GetObjectFile()->GetModule()->LogMessage (log, + "DWARFCompileUnit::Index() for compile unit at .debug_info[0x%8.8x]", + GetOffset()); + } + + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator begin = m_die_array.begin(); + DWARFDebugInfoEntry::const_iterator end = m_die_array.end(); + for (pos = begin; pos != end; ++pos) + { + const DWARFDebugInfoEntry &die = *pos; + + const dw_tag_t tag = die.Tag(); + + switch (tag) + { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_constant: + case DW_TAG_enumeration_type: + case DW_TAG_string_type: + case DW_TAG_subroutine_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_typedef: + case DW_TAG_namespace: + case DW_TAG_variable: + case DW_TAG_unspecified_type: + break; + + default: + continue; + } + + DWARFDebugInfoEntry::Attributes attributes; + const char *name = NULL; + const char *mangled_cstr = NULL; + bool is_declaration = false; + //bool is_artificial = false; + bool has_address = false; + bool has_location = false; + bool is_global_or_static_variable = false; + + dw_offset_t specification_die_offset = DW_INVALID_OFFSET; + const size_t num_attributes = die.GetAttributes(m_dwarf2Data, this, fixed_form_sizes, attributes); + if (num_attributes > 0) + { + for (uint32_t i=0; i<num_attributes; ++i) + { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + switch (attr) + { + case DW_AT_name: + if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) + name = form_value.AsCString(debug_str); + break; + + case DW_AT_declaration: + if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) + is_declaration = form_value.Unsigned() != 0; + break; + +// case DW_AT_artificial: +// if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) +// is_artificial = form_value.Unsigned() != 0; +// break; + + case DW_AT_MIPS_linkage_name: + case DW_AT_linkage_name: + if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) + mangled_cstr = form_value.AsCString(debug_str); + break; + + case DW_AT_low_pc: + case DW_AT_high_pc: + case DW_AT_ranges: + has_address = true; + break; + + case DW_AT_entry_pc: + has_address = true; + break; + + case DW_AT_location: + has_location = true; + if (tag == DW_TAG_variable) + { + const DWARFDebugInfoEntry* parent_die = die.GetParent(); + while ( parent_die != NULL ) + { + switch (parent_die->Tag()) + { + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + // Even if this is a function level static, we don't add it. We could theoretically + // add these if we wanted to by introspecting into the DW_AT_location and seeing + // if the location describes a hard coded address, but we dont want the performance + // penalty of that right now. + is_global_or_static_variable = false; +// if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) +// { +// // If we have valid block data, then we have location expression bytes +// // that are fixed (not a location list). +// const uint8_t *block_data = form_value.BlockData(); +// if (block_data) +// { +// uint32_t block_length = form_value.Unsigned(); +// if (block_length == 1 + attributes.CompileUnitAtIndex(i)->GetAddressByteSize()) +// { +// if (block_data[0] == DW_OP_addr) +// add_die = true; +// } +// } +// } + parent_die = NULL; // Terminate the while loop. + break; + + case DW_TAG_compile_unit: + is_global_or_static_variable = true; + parent_die = NULL; // Terminate the while loop. + break; + + default: + parent_die = parent_die->GetParent(); // Keep going in the while loop. + break; + } + } + } + break; + + case DW_AT_specification: + if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) + specification_die_offset = form_value.Reference(this); + break; + } + } + } + + switch (tag) + { + case DW_TAG_subprogram: + if (has_address) + { + if (name) + { + // Note, this check is also done in ParseMethodName, but since this is a hot loop, we do the + // simple inlined check outside the call. + ObjCLanguageRuntime::MethodName objc_method(name, true); + if (objc_method.IsValid(true)) + { + ConstString objc_class_name_with_category (objc_method.GetClassNameWithCategory()); + ConstString objc_selector_name (objc_method.GetSelector()); + ConstString objc_fullname_no_category_name (objc_method.GetFullNameWithoutCategory(true)); + ConstString objc_class_name_no_category (objc_method.GetClassName()); + func_fullnames.Insert (ConstString(name), die.GetOffset()); + if (objc_class_name_with_category) + objc_class_selectors.Insert(objc_class_name_with_category, die.GetOffset()); + if (objc_class_name_no_category && objc_class_name_no_category != objc_class_name_with_category) + objc_class_selectors.Insert(objc_class_name_no_category, die.GetOffset()); + if (objc_selector_name) + func_selectors.Insert (objc_selector_name, die.GetOffset()); + if (objc_fullname_no_category_name) + func_fullnames.Insert (objc_fullname_no_category_name, die.GetOffset()); + } + // If we have a mangled name, then the DW_AT_name attribute + // is usually the method name without the class or any parameters + const DWARFDebugInfoEntry *parent = die.GetParent(); + bool is_method = false; + if (parent) + { + dw_tag_t parent_tag = parent->Tag(); + if (parent_tag == DW_TAG_class_type || parent_tag == DW_TAG_structure_type) + { + is_method = true; + } + else + { + if (specification_die_offset != DW_INVALID_OFFSET) + { + const DWARFDebugInfoEntry *specification_die = m_dwarf2Data->DebugInfo()->GetDIEPtr (specification_die_offset, NULL); + if (specification_die) + { + parent = specification_die->GetParent(); + if (parent) + { + parent_tag = parent->Tag(); + + if (parent_tag == DW_TAG_class_type || parent_tag == DW_TAG_structure_type) + is_method = true; + } + } + } + } + } + + + if (is_method) + func_methods.Insert (ConstString(name), die.GetOffset()); + else + func_basenames.Insert (ConstString(name), die.GetOffset()); + + if (!is_method && !mangled_cstr && !objc_method.IsValid(true)) + func_fullnames.Insert (ConstString(name), die.GetOffset()); + } + if (mangled_cstr) + { + // Make sure our mangled name isn't the same string table entry + // as our name. If it starts with '_', then it is ok, else compare + // the string to make sure it isn't the same and we don't end up + // with duplicate entries + if (name != mangled_cstr && ((mangled_cstr[0] == '_') || (name && ::strcmp(name, mangled_cstr) != 0))) + { + Mangled mangled (ConstString(mangled_cstr), true); + func_fullnames.Insert (mangled.GetMangledName(), die.GetOffset()); + if (mangled.GetDemangledName()) + func_fullnames.Insert (mangled.GetDemangledName(), die.GetOffset()); + } + } + } + break; + + case DW_TAG_inlined_subroutine: + if (has_address) + { + if (name) + func_basenames.Insert (ConstString(name), die.GetOffset()); + if (mangled_cstr) + { + // Make sure our mangled name isn't the same string table entry + // as our name. If it starts with '_', then it is ok, else compare + // the string to make sure it isn't the same and we don't end up + // with duplicate entries + if (name != mangled_cstr && ((mangled_cstr[0] == '_') || (::strcmp(name, mangled_cstr) != 0))) + { + Mangled mangled (ConstString(mangled_cstr), true); + func_fullnames.Insert (mangled.GetMangledName(), die.GetOffset()); + if (mangled.GetDemangledName()) + func_fullnames.Insert (mangled.GetDemangledName(), die.GetOffset()); + } + } + else + func_fullnames.Insert (ConstString(name), die.GetOffset()); + } + break; + + case DW_TAG_base_type: + case DW_TAG_class_type: + case DW_TAG_constant: + case DW_TAG_enumeration_type: + case DW_TAG_string_type: + case DW_TAG_subroutine_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_typedef: + case DW_TAG_unspecified_type: + if (name && is_declaration == false) + { + types.Insert (ConstString(name), die.GetOffset()); + } + break; + + case DW_TAG_namespace: + if (name) + namespaces.Insert (ConstString(name), die.GetOffset()); + break; + + case DW_TAG_variable: + if (name && has_location && is_global_or_static_variable) + { + globals.Insert (ConstString(name), die.GetOffset()); + // Be sure to include variables by their mangled and demangled + // names if they have any since a variable can have a basename + // "i", a mangled named "_ZN12_GLOBAL__N_11iE" and a demangled + // mangled name "(anonymous namespace)::i"... + + // Make sure our mangled name isn't the same string table entry + // as our name. If it starts with '_', then it is ok, else compare + // the string to make sure it isn't the same and we don't end up + // with duplicate entries + if (mangled_cstr && name != mangled_cstr && ((mangled_cstr[0] == '_') || (::strcmp(name, mangled_cstr) != 0))) + { + Mangled mangled (ConstString(mangled_cstr), true); + globals.Insert (mangled.GetMangledName(), die.GetOffset()); + if (mangled.GetDemangledName()) + globals.Insert (mangled.GetDemangledName(), die.GetOffset()); + } + } + break; + + default: + continue; + } + } +} + +bool +DWARFCompileUnit::Supports_unnamed_objc_bitfields () +{ + if (GetProducer() == eProducerClang) + { + const uint32_t major_version = GetProducerVersionMajor(); + if (major_version > 425 || (major_version == 425 && GetProducerVersionUpdate() >= 13)) + return true; + else + return false; + } + return true; // Assume all other compilers didn't have incorrect ObjC bitfield info +} + +bool +DWARFCompileUnit::Supports_DW_AT_APPLE_objc_complete_type () +{ + if (GetProducer() == eProducerLLVMGCC) + return false; + return true; +} + +bool +DWARFCompileUnit::DW_AT_decl_file_attributes_are_invalid() +{ + // llvm-gcc makes completely invalid decl file attributes and won't ever + // be fixed, so we need to know to ignore these. + return GetProducer() == eProducerLLVMGCC; +} + +void +DWARFCompileUnit::ParseProducerInfo () +{ + m_producer_version_major = UINT32_MAX; + m_producer_version_minor = UINT32_MAX; + m_producer_version_update = UINT32_MAX; + + const DWARFDebugInfoEntry *die = GetCompileUnitDIEOnly(); + if (die) + { + + const char *producer_cstr = die->GetAttributeValueAsString(m_dwarf2Data, this, DW_AT_producer, NULL); + if (producer_cstr) + { + RegularExpression llvm_gcc_regex("^4\\.[012]\\.[01] \\(Based on Apple Inc\\. build [0-9]+\\) \\(LLVM build [\\.0-9]+\\)$"); + if (llvm_gcc_regex.Execute (producer_cstr)) + { + m_producer = eProducerLLVMGCC; + } + else if (strstr(producer_cstr, "clang")) + { + static RegularExpression g_clang_version_regex("clang-([0-9]+)\\.([0-9]+)\\.([0-9]+)"); + RegularExpression::Match regex_match(3); + if (g_clang_version_regex.Execute (producer_cstr, ®ex_match)) + { + std::string str; + if (regex_match.GetMatchAtIndex (producer_cstr, 1, str)) + m_producer_version_major = Args::StringToUInt32(str.c_str(), UINT32_MAX, 10); + if (regex_match.GetMatchAtIndex (producer_cstr, 2, str)) + m_producer_version_minor = Args::StringToUInt32(str.c_str(), UINT32_MAX, 10); + if (regex_match.GetMatchAtIndex (producer_cstr, 3, str)) + m_producer_version_update = Args::StringToUInt32(str.c_str(), UINT32_MAX, 10); + } + m_producer = eProducerClang; + } + else if (strstr(producer_cstr, "GNU")) + m_producer = eProducerGCC; + } + } + if (m_producer == eProducerInvalid) + m_producer = eProcucerOther; +} + +DWARFCompileUnit::Producer +DWARFCompileUnit::GetProducer () +{ + if (m_producer == eProducerInvalid) + ParseProducerInfo (); + return m_producer; +} + + +uint32_t +DWARFCompileUnit::GetProducerVersionMajor() +{ + if (m_producer_version_major == 0) + ParseProducerInfo (); + return m_producer_version_major; +} + +uint32_t +DWARFCompileUnit::GetProducerVersionMinor() +{ + if (m_producer_version_minor == 0) + ParseProducerInfo (); + return m_producer_version_minor; +} + +uint32_t +DWARFCompileUnit::GetProducerVersionUpdate() +{ + if (m_producer_version_update == 0) + ParseProducerInfo (); + return m_producer_version_update; +} + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h b/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h new file mode 100644 index 000000000000..9fee0a2d2236 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h @@ -0,0 +1,210 @@ +//===-- DWARFCompileUnit.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFCompileUnit_h_ +#define SymbolFileDWARF_DWARFCompileUnit_h_ + +#include "DWARFDebugInfoEntry.h" +#include "SymbolFileDWARF.h" + +class NameToDIE; + +class DWARFCompileUnit +{ +public: + enum Producer + { + eProducerInvalid = 0, + eProducerClang, + eProducerGCC, + eProducerLLVMGCC, + eProcucerOther + }; + + DWARFCompileUnit(SymbolFileDWARF* dwarf2Data); + + bool Extract(const lldb_private::DataExtractor &debug_info, lldb::offset_t *offset_ptr); + dw_offset_t Extract(lldb::offset_t offset, const lldb_private::DataExtractor& debug_info_data, const DWARFAbbreviationDeclarationSet* abbrevs); + size_t ExtractDIEsIfNeeded (bool cu_die_only); + bool LookupAddress( + const dw_addr_t address, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + size_t AppendDIEsWithTag (const dw_tag_t tag, DWARFDIECollection& matching_dies, uint32_t depth = UINT32_MAX) const; + void Clear(); + bool Verify(lldb_private::Stream *s) const; + void Dump(lldb_private::Stream *s) const; + dw_offset_t GetOffset() const { return m_offset; } + uint32_t Size() const { return 11; /* Size in bytes of the compile unit header */ } + bool ContainsDIEOffset(dw_offset_t die_offset) const { return die_offset >= GetFirstDIEOffset() && die_offset < GetNextCompileUnitOffset(); } + dw_offset_t GetFirstDIEOffset() const { return m_offset + Size(); } + dw_offset_t GetNextCompileUnitOffset() const { return m_offset + m_length + 4; } + size_t GetDebugInfoSize() const { return m_length + 4 - Size(); /* Size in bytes of the .debug_info data associated with this compile unit. */ } + uint32_t GetLength() const { return m_length; } + uint16_t GetVersion() const { return m_version; } + const DWARFAbbreviationDeclarationSet* GetAbbreviations() const { return m_abbrevs; } + dw_offset_t GetAbbrevOffset() const; + uint8_t GetAddressByteSize() const { return m_addr_size; } + dw_addr_t GetBaseAddress() const { return m_base_addr; } + void ClearDIEs(bool keep_compile_unit_die); + void BuildAddressRangeTable (SymbolFileDWARF* dwarf2Data, + DWARFDebugAranges* debug_aranges, + bool clear_dies_if_already_not_parsed); + + void + SetBaseAddress(dw_addr_t base_addr) + { + m_base_addr = base_addr; + } + + const DWARFDebugInfoEntry* + GetCompileUnitDIEOnly() + { + ExtractDIEsIfNeeded (true); + if (m_die_array.empty()) + return NULL; + return &m_die_array[0]; + } + + const DWARFDebugInfoEntry* + DIE() + { + ExtractDIEsIfNeeded (false); + if (m_die_array.empty()) + return NULL; + return &m_die_array[0]; + } + + void + AddDIE (DWARFDebugInfoEntry& die) + { + // The average bytes per DIE entry has been seen to be + // around 14-20 so lets pre-reserve half of that since + // we are now stripping the NULL tags. + + // Only reserve the memory if we are adding children of + // the main compile unit DIE. The compile unit DIE is always + // the first entry, so if our size is 1, then we are adding + // the first compile unit child DIE and should reserve + // the memory. + if (m_die_array.empty()) + m_die_array.reserve(GetDebugInfoSize() / 24); + m_die_array.push_back(die); + } + + bool + HasDIEsParsed () const + { + return m_die_array.size() > 1; + } + + DWARFDebugInfoEntry* + GetDIEAtIndexUnchecked (uint32_t idx) + { + return &m_die_array[idx]; + } + + DWARFDebugInfoEntry* + GetDIEPtr (dw_offset_t die_offset); + + const DWARFDebugInfoEntry* + GetDIEPtrContainingOffset (dw_offset_t die_offset); + + static uint8_t + GetAddressByteSize(const DWARFCompileUnit* cu); + + static uint8_t + GetDefaultAddressSize(); + + static void + SetDefaultAddressSize(uint8_t addr_size); + + void * + GetUserData() const + { + return m_user_data; + } + + void + SetUserData(void *d) + { + m_user_data = d; + } + + bool + Supports_DW_AT_APPLE_objc_complete_type (); + + bool + DW_AT_decl_file_attributes_are_invalid(); + + bool + Supports_unnamed_objc_bitfields (); + +// void +// AddGlobalDIEByIndex (uint32_t die_idx); +// +// void +// AddGlobal (const DWARFDebugInfoEntry* die); +// + void + Index (const uint32_t cu_idx, + NameToDIE& func_basenames, + NameToDIE& func_fullnames, + NameToDIE& func_methods, + NameToDIE& func_selectors, + NameToDIE& objc_class_selectors, + NameToDIE& globals, + NameToDIE& types, + NameToDIE& namespaces); + + const DWARFDebugAranges & + GetFunctionAranges (); + + SymbolFileDWARF* + GetSymbolFileDWARF () const + { + return m_dwarf2Data; + } + + Producer + GetProducer (); + + uint32_t + GetProducerVersionMajor(); + + uint32_t + GetProducerVersionMinor(); + + uint32_t + GetProducerVersionUpdate(); + +protected: + SymbolFileDWARF* m_dwarf2Data; + const DWARFAbbreviationDeclarationSet *m_abbrevs; + void * m_user_data; + DWARFDebugInfoEntry::collection m_die_array; // The compile unit debug information entry item + std::unique_ptr<DWARFDebugAranges> m_func_aranges_ap; // A table similar to the .debug_aranges table, but this one points to the exact DW_TAG_subprogram DIEs + dw_addr_t m_base_addr; + dw_offset_t m_offset; + uint32_t m_length; + uint16_t m_version; + uint8_t m_addr_size; + Producer m_producer; + uint32_t m_producer_version_major; + uint32_t m_producer_version_minor; + uint32_t m_producer_version_update; + + void + ParseProducerInfo (); +private: + DISALLOW_COPY_AND_ASSIGN (DWARFCompileUnit); +}; + +#endif // SymbolFileDWARF_DWARFCompileUnit_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp new file mode 100644 index 000000000000..1beb75d33642 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp @@ -0,0 +1,62 @@ +//===-- DWARFDIECollection.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDIECollection.h" + +#include <algorithm> + +#include "lldb/Core/Stream.h" + +#include "DWARFDebugInfoEntry.h" + +using namespace lldb_private; +using namespace std; + +bool +DWARFDIECollection::Insert(const DWARFDebugInfoEntry *die) +{ + iterator end_pos = m_dies.end(); + iterator insert_pos = upper_bound(m_dies.begin(), end_pos, die); + if (insert_pos != end_pos && (*insert_pos == die)) + return false; + m_dies.insert(insert_pos, die); + return true; +} + +void +DWARFDIECollection::Append (const DWARFDebugInfoEntry *die) +{ + m_dies.push_back (die); +} + +const DWARFDebugInfoEntry * +DWARFDIECollection::GetDIEPtrAtIndex(uint32_t idx) const +{ + if (idx < m_dies.size()) + return m_dies[idx]; + return NULL; +} + + +size_t +DWARFDIECollection::Size() const +{ + return m_dies.size(); +} + +void +DWARFDIECollection::Dump(Stream *s, const char* title) const +{ + if (title && title[0] != '\0') + s->Printf( "%s\n", title); + const_iterator end_pos = m_dies.end(); + const_iterator pos; + for (pos = m_dies.begin(); pos != end_pos; ++pos) + s->Printf( "0x%8.8x\n", (*pos)->GetOffset()); +} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h b/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h new file mode 100644 index 000000000000..173d0a5604d0 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h @@ -0,0 +1,51 @@ +//===-- DWARFDIECollection.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDIECollection_h_ +#define SymbolFileDWARF_DWARFDIECollection_h_ + +#include "SymbolFileDWARF.h" +#include <vector> + +class DWARFDIECollection +{ +public: + DWARFDIECollection() : + m_dies() + { + } + ~DWARFDIECollection() + { + } + + void + Append (const DWARFDebugInfoEntry *die); + + void + Dump(lldb_private::Stream *s, const char* title) const; + + const DWARFDebugInfoEntry* + GetDIEPtrAtIndex(uint32_t idx) const; + + bool + Insert(const DWARFDebugInfoEntry *die); + + size_t + Size() const; + +protected: + typedef std::vector<const DWARFDebugInfoEntry *> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_dies; // Ordered list of die offsets +}; + + +#endif // SymbolFileDWARF_DWARFDIECollection_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp new file mode 100644 index 000000000000..47657d5089b2 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp @@ -0,0 +1,202 @@ +//===-- DWARFDebugAbbrev.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugAbbrev.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Clear() +//---------------------------------------------------------------------- +void +DWARFAbbreviationDeclarationSet::Clear() +{ + m_idx_offset = 0; + m_decls.clear(); +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Extract() +//---------------------------------------------------------------------- +bool +DWARFAbbreviationDeclarationSet::Extract(const DataExtractor& data, lldb::offset_t *offset_ptr) +{ + const lldb::offset_t begin_offset = *offset_ptr; + m_offset = begin_offset; + Clear(); + DWARFAbbreviationDeclaration abbrevDeclaration; + dw_uleb128_t prev_abbr_code = 0; + while (abbrevDeclaration.Extract(data, offset_ptr)) + { + m_decls.push_back(abbrevDeclaration); + if (m_idx_offset == 0) + m_idx_offset = abbrevDeclaration.Code(); + else + { + if (prev_abbr_code + 1 != abbrevDeclaration.Code()) + m_idx_offset = UINT32_MAX; // Out of order indexes, we can't do O(1) lookups... + } + prev_abbr_code = abbrevDeclaration.Code(); + } + return begin_offset != *offset_ptr; +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::Dump() +//---------------------------------------------------------------------- +void +DWARFAbbreviationDeclarationSet::Dump(Stream *s) const +{ + std::for_each (m_decls.begin(), m_decls.end(), bind2nd(std::mem_fun_ref(&DWARFAbbreviationDeclaration::Dump),s)); +} + + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration() +//---------------------------------------------------------------------- +const DWARFAbbreviationDeclaration* +DWARFAbbreviationDeclarationSet::GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const +{ + if (m_idx_offset == UINT32_MAX) + { + DWARFAbbreviationDeclarationCollConstIter pos; + DWARFAbbreviationDeclarationCollConstIter end = m_decls.end(); + for (pos = m_decls.begin(); pos != end; ++pos) + { + if (pos->Code() == abbrCode) + return &(*pos); + } + } + else + { + uint32_t idx = abbrCode - m_idx_offset; + if (idx < m_decls.size()) + return &m_decls[idx]; + } + return NULL; +} + +//---------------------------------------------------------------------- +// DWARFAbbreviationDeclarationSet::AppendAbbrevDeclSequential() +// +// Append an abbreviation declaration with a sequential code for O(n) +// lookups. Handy when creating an DWARFAbbreviationDeclarationSet. +//---------------------------------------------------------------------- +dw_uleb128_t +DWARFAbbreviationDeclarationSet::AppendAbbrevDeclSequential(const DWARFAbbreviationDeclaration& abbrevDecl) +{ + // Get the next abbreviation code based on our current array size + dw_uleb128_t code = m_decls.size()+1; + + // Push the new declaration on the back + m_decls.push_back(abbrevDecl); + + // Update the code for this new declaration + m_decls.back().SetCode(code); + + return code; // return the new abbreviation code! +} + + +//---------------------------------------------------------------------- +// Encode +// +// Encode the abbreviation table onto the end of the buffer provided +// into a byte representation as would be found in a ".debug_abbrev" +// debug information section. +//---------------------------------------------------------------------- +//void +//DWARFAbbreviationDeclarationSet::Encode(BinaryStreamBuf& debug_abbrev_buf) const +//{ +// DWARFAbbreviationDeclarationCollConstIter pos; +// DWARFAbbreviationDeclarationCollConstIter end = m_decls.end(); +// for (pos = m_decls.begin(); pos != end; ++pos) +// pos->Append(debug_abbrev_buf); +// debug_abbrev_buf.Append8(0); +//} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev constructor +//---------------------------------------------------------------------- +DWARFDebugAbbrev::DWARFDebugAbbrev() : + m_abbrevCollMap(), + m_prev_abbr_offset_pos(m_abbrevCollMap.end()) +{ +} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::Parse() +//---------------------------------------------------------------------- +void +DWARFDebugAbbrev::Parse(const DataExtractor& data) +{ + lldb::offset_t offset = 0; + + while (data.ValidOffset(offset)) + { + uint32_t initial_cu_offset = offset; + DWARFAbbreviationDeclarationSet abbrevDeclSet; + + if (abbrevDeclSet.Extract(data, &offset)) + m_abbrevCollMap[initial_cu_offset] = abbrevDeclSet; + else + break; + } + m_prev_abbr_offset_pos = m_abbrevCollMap.end(); +} + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::Dump() +//---------------------------------------------------------------------- +void +DWARFDebugAbbrev::Dump(Stream *s) const +{ + if (m_abbrevCollMap.empty()) + { + s->PutCString("< EMPTY >\n"); + return; + } + + DWARFAbbreviationDeclarationCollMapConstIter pos; + for (pos = m_abbrevCollMap.begin(); pos != m_abbrevCollMap.end(); ++pos) + { + s->Printf("Abbrev table for offset: 0x%8.8x\n", pos->first); + pos->second.Dump(s); + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugAbbrev::GetAbbreviationDeclarationSet() +//---------------------------------------------------------------------- +const DWARFAbbreviationDeclarationSet* +DWARFDebugAbbrev::GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const +{ + DWARFAbbreviationDeclarationCollMapConstIter end = m_abbrevCollMap.end(); + DWARFAbbreviationDeclarationCollMapConstIter pos; + if (m_prev_abbr_offset_pos != end && m_prev_abbr_offset_pos->first == cu_abbr_offset) + return &(m_prev_abbr_offset_pos->second); + else + { + pos = m_abbrevCollMap.find(cu_abbr_offset); + m_prev_abbr_offset_pos = pos; + } + + if (pos != m_abbrevCollMap.end()) + return &(pos->second); + return NULL; +} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h new file mode 100644 index 000000000000..eba439928a2c --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h @@ -0,0 +1,74 @@ +//===-- DWARFDebugAbbrev.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugAbbrev_h_ +#define SymbolFileDWARF_DWARFDebugAbbrev_h_ + +#include <list> +#include <map> + +#include "lldb/lldb-private.h" + +#include "DWARFDefines.h" +#include "DWARFAbbreviationDeclaration.h" + +typedef std::vector<DWARFAbbreviationDeclaration> DWARFAbbreviationDeclarationColl; +typedef DWARFAbbreviationDeclarationColl::iterator DWARFAbbreviationDeclarationCollIter; +typedef DWARFAbbreviationDeclarationColl::const_iterator DWARFAbbreviationDeclarationCollConstIter; + + +class DWARFAbbreviationDeclarationSet +{ +public: + DWARFAbbreviationDeclarationSet() : + m_offset(DW_INVALID_OFFSET), + m_idx_offset(0), + m_decls() + { + } + + DWARFAbbreviationDeclarationSet(dw_offset_t offset, uint32_t idx_offset) : + m_offset(offset), + m_idx_offset(idx_offset), + m_decls() + { + } + + void Clear(); + dw_offset_t GetOffset() const { return m_offset; } + void Dump(lldb_private::Stream *s) const; + bool Extract(const lldb_private::DataExtractor& data, lldb::offset_t *offset_ptr); + //void Encode(BinaryStreamBuf& debug_abbrev_buf) const; + dw_uleb128_t AppendAbbrevDeclSequential(const DWARFAbbreviationDeclaration& abbrevDecl); + + const DWARFAbbreviationDeclaration* GetAbbreviationDeclaration(dw_uleb128_t abbrCode) const; +private: + dw_offset_t m_offset; + uint32_t m_idx_offset; + std::vector<DWARFAbbreviationDeclaration> m_decls; +}; + +typedef std::map<dw_offset_t, DWARFAbbreviationDeclarationSet> DWARFAbbreviationDeclarationCollMap; +typedef DWARFAbbreviationDeclarationCollMap::iterator DWARFAbbreviationDeclarationCollMapIter; +typedef DWARFAbbreviationDeclarationCollMap::const_iterator DWARFAbbreviationDeclarationCollMapConstIter; + + +class DWARFDebugAbbrev +{ +public: + DWARFDebugAbbrev(); + const DWARFAbbreviationDeclarationSet* GetAbbreviationDeclarationSet(dw_offset_t cu_abbr_offset) const; + void Dump(lldb_private::Stream *s) const; + void Parse(const lldb_private::DataExtractor& data); +protected: + DWARFAbbreviationDeclarationCollMap m_abbrevCollMap; + mutable DWARFAbbreviationDeclarationCollMapConstIter m_prev_abbr_offset_pos; +}; + +#endif // SymbolFileDWARF_DWARFDebugAbbrev_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp new file mode 100644 index 000000000000..b1eb27299efb --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp @@ -0,0 +1,274 @@ +//===-- DWARFDebugArangeSet.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugArangeSet.h" + +#include <assert.h> +#include "lldb/Core/Stream.h" +#include "SymbolFileDWARF.h" + +using namespace lldb_private; + +DWARFDebugArangeSet::DWARFDebugArangeSet() : + m_offset(DW_INVALID_OFFSET), + m_header(), + m_arange_descriptors() +{ + m_header.length = 0; + m_header.version = 0; + m_header.cu_offset = 0; + m_header.addr_size = 0; + m_header.seg_size = 0; +} + +void +DWARFDebugArangeSet::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_header.length = 0; + m_header.version = 0; + m_header.cu_offset = 0; + m_header.addr_size = 0; + m_header.seg_size = 0; + m_arange_descriptors.clear(); +} + +void +DWARFDebugArangeSet::SetHeader +( + uint16_t version, + uint32_t cu_offset, + uint8_t addr_size, + uint8_t seg_size +) +{ + m_header.version = version; + m_header.cu_offset = cu_offset; + m_header.addr_size = addr_size; + m_header.seg_size = seg_size; +} + +void +DWARFDebugArangeSet::Compact() +{ + if (m_arange_descriptors.empty()) + return; + + // Iterate through all arange descriptors and combine any ranges that + // overlap or have matching boundaries. The m_arange_descriptors are assumed + // to be in ascending order after being built by adding descriptors + // using the AddDescriptor method. + uint32_t i = 0; + while (i + 1 < m_arange_descriptors.size()) + { + if (m_arange_descriptors[i].end_address() >= m_arange_descriptors[i+1].address) + { + // The current range ends at or exceeds the start of the next address range. + // Compute the max end address between the two and use that to make the new + // length. + const dw_addr_t max_end_addr = std::max(m_arange_descriptors[i].end_address(), m_arange_descriptors[i+1].end_address()); + m_arange_descriptors[i].length = max_end_addr - m_arange_descriptors[i].address; + // Now remove the next entry as it was just combined with the previous one. + m_arange_descriptors.erase(m_arange_descriptors.begin()+i+1); + } + else + { + // Discontiguous address range, just proceed to the next one. + ++i; + } + } +} +//---------------------------------------------------------------------- +// Compare function DWARFDebugArangeSet::Descriptor structures +//---------------------------------------------------------------------- +static bool DescriptorLessThan (const DWARFDebugArangeSet::Descriptor& range1, const DWARFDebugArangeSet::Descriptor& range2) +{ + return range1.address < range2.address; +} + +//---------------------------------------------------------------------- +// Add a range descriptor and keep things sorted so we can easily +// compact the ranges before being saved or used. +//---------------------------------------------------------------------- +void +DWARFDebugArangeSet::AddDescriptor(const DWARFDebugArangeSet::Descriptor& range) +{ + if (m_arange_descriptors.empty()) + { + m_arange_descriptors.push_back(range); + return; + } + + DescriptorIter end = m_arange_descriptors.end(); + DescriptorIter pos = lower_bound(m_arange_descriptors.begin(), end, range, DescriptorLessThan); + const dw_addr_t range_end_addr = range.end_address(); + if (pos != end) + { + const dw_addr_t found_end_addr = pos->end_address(); + if (range.address < pos->address) + { + if (range_end_addr < pos->address) + { + // Non-contiguous entries, add this one before the found entry + m_arange_descriptors.insert(pos, range); + } + else if (range_end_addr == pos->address) + { + // The top end of 'range' is the lower end of the entry + // pointed to by 'pos'. We can combine range with the + // entry we found by setting the starting address and + // increasing the length since they don't overlap. + pos->address = range.address; + pos->length += range.length; + } + else + { + // We can combine these two and make sure the largest end + // address is used to make end address. + pos->address = range.address; + pos->length = std::max(found_end_addr, range_end_addr) - pos->address; + } + } + else if (range.address == pos->address) + { + pos->length = std::max(pos->length, range.length); + } + } + else + { + // NOTE: 'pos' points to entry past the end which is ok for insert, + // don't use otherwise!!! + const dw_addr_t max_addr = m_arange_descriptors.back().end_address(); + if (max_addr < range.address) + { + // Non-contiguous entries, add this one before the found entry + m_arange_descriptors.insert(pos, range); + } + else if (max_addr == range.address) + { + m_arange_descriptors.back().length += range.length; + } + else + { + m_arange_descriptors.back().length = std::max(max_addr, range_end_addr) - m_arange_descriptors.back().address; + } + } +} + +bool +DWARFDebugArangeSet::Extract(const DataExtractor &data, lldb::offset_t *offset_ptr) +{ + if (data.ValidOffset(*offset_ptr)) + { + m_arange_descriptors.clear(); + m_offset = *offset_ptr; + + // 7.20 Address Range Table + // + // Each set of entries in the table of address ranges contained in + // the .debug_aranges section begins with a header consisting of: a + // 4-byte length containing the length of the set of entries for this + // compilation unit, not including the length field itself; a 2-byte + // version identifier containing the value 2 for DWARF Version 2; a + // 4-byte offset into the.debug_infosection; a 1-byte unsigned integer + // containing the size in bytes of an address (or the offset portion of + // an address for segmented addressing) on the target system; and a + // 1-byte unsigned integer containing the size in bytes of a segment + // descriptor on the target system. This header is followed by a series + // of tuples. Each tuple consists of an address and a length, each in + // the size appropriate for an address on the target architecture. + m_header.length = data.GetU32(offset_ptr); + m_header.version = data.GetU16(offset_ptr); + m_header.cu_offset = data.GetU32(offset_ptr); + m_header.addr_size = data.GetU8(offset_ptr); + m_header.seg_size = data.GetU8(offset_ptr); + + + // The first tuple following the header in each set begins at an offset + // that is a multiple of the size of a single tuple (that is, twice the + // size of an address). The header is padded, if necessary, to the + // appropriate boundary. + const uint32_t header_size = *offset_ptr - m_offset; + const uint32_t tuple_size = m_header.addr_size << 1; + uint32_t first_tuple_offset = 0; + while (first_tuple_offset < header_size) + first_tuple_offset += tuple_size; + + *offset_ptr = m_offset + first_tuple_offset; + + Descriptor arangeDescriptor; + + assert(sizeof(arangeDescriptor.address) == sizeof(arangeDescriptor.length)); + assert(sizeof(arangeDescriptor.address) >= m_header.addr_size); + + while (data.ValidOffset(*offset_ptr)) + { + arangeDescriptor.address = data.GetMaxU64(offset_ptr, m_header.addr_size); + arangeDescriptor.length = data.GetMaxU64(offset_ptr, m_header.addr_size); + + // Each set of tuples is terminated by a 0 for the address and 0 + // for the length. + if (arangeDescriptor.address || arangeDescriptor.length) + m_arange_descriptors.push_back(arangeDescriptor); + else + break; // We are done if we get a zero address and length + } + + return !m_arange_descriptors.empty(); + } + return false; +} + + +dw_offset_t +DWARFDebugArangeSet::GetOffsetOfNextEntry() const +{ + return m_offset + m_header.length + 4; +} + + +void +DWARFDebugArangeSet::Dump(Stream *s) const +{ + s->Printf("Address Range Header: length = 0x%8.8x, version = 0x%4.4x, cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", + m_header.length ,m_header.version, m_header.cu_offset, m_header.addr_size, m_header.seg_size); + + const uint32_t hex_width = m_header.addr_size * 2; + DescriptorConstIter pos; + DescriptorConstIter end = m_arange_descriptors.end(); + for (pos = m_arange_descriptors.begin(); pos != end; ++pos) + s->Printf("[0x%*.*" PRIx64 " - 0x%*.*" PRIx64 ")\n", + hex_width, hex_width, pos->address, + hex_width, hex_width, pos->end_address()); +} + + +class DescriptorContainsAddress +{ +public: + DescriptorContainsAddress (dw_addr_t address) : m_address(address) {} + bool operator() (const DWARFDebugArangeSet::Descriptor& desc) const + { + return (m_address >= desc.address) && (m_address < (desc.address + desc.length)); + } + private: + const dw_addr_t m_address; +}; + +dw_offset_t +DWARFDebugArangeSet::FindAddress(dw_addr_t address) const +{ + DescriptorConstIter end = m_arange_descriptors.end(); + DescriptorConstIter pos = std::find_if( m_arange_descriptors.begin(), end, // Range + DescriptorContainsAddress(address));// Predicate + if (pos != end) + return m_header.cu_offset; + + return DW_INVALID_OFFSET; +} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h new file mode 100644 index 000000000000..19ec8d042e72 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h @@ -0,0 +1,76 @@ +//===-- DWARFDebugArangeSet.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugArangeSet_h_ +#define SymbolFileDWARF_DWARFDebugArangeSet_h_ + +#include "SymbolFileDWARF.h" +#include <vector> + +class SymbolFileDWARF; + +class DWARFDebugArangeSet +{ +public: + struct Header + { + uint32_t length; // The total length of the entries for that set, not including the length field itself. + uint16_t version; // The DWARF version number + uint32_t cu_offset; // The offset from the beginning of the .debug_info section of the compilation unit entry referenced by the table. + uint8_t addr_size; // The size in bytes of an address on the target architecture. For segmented addressing, this is the size of the offset portion of the address + uint8_t seg_size; // The size in bytes of a segment descriptor on the target architecture. If the target system uses a flat address space, this value is 0. + }; + + struct Descriptor + { + dw_addr_t address; + dw_addr_t length; + dw_addr_t end_address() const { return address + length; } + }; + + + DWARFDebugArangeSet(); + void Clear(); + void SetOffset(uint32_t offset) { m_offset = offset; } + void SetHeader(uint16_t version, uint32_t cu_offset, uint8_t addr_size, uint8_t seg_size); + void AddDescriptor(const DWARFDebugArangeSet::Descriptor& range); + void Compact(); + bool Extract(const lldb_private::DataExtractor &data, lldb::offset_t *offset_ptr); + void Dump(lldb_private::Stream *s) const; + dw_offset_t GetCompileUnitDIEOffset() const { return m_header.cu_offset; } + dw_offset_t GetOffsetOfNextEntry() const; + dw_offset_t FindAddress(dw_addr_t address) const; + size_t NumDescriptors() const { return m_arange_descriptors.size(); } + const Header& GetHeader() const { return m_header; } + const Descriptor* GetDescriptor(uint32_t i) const + { + if (i < m_arange_descriptors.size()) + return &m_arange_descriptors[i]; + return NULL; + } + + const Descriptor & + GetDescriptorRef (uint32_t i) const + { + return m_arange_descriptors[i]; + } + + +protected: + typedef std::vector<Descriptor> DescriptorColl; + typedef DescriptorColl::iterator DescriptorIter; + typedef DescriptorColl::const_iterator DescriptorConstIter; + + + uint32_t m_offset; + Header m_header; + DescriptorColl m_arange_descriptors; +}; + +#endif // SymbolFileDWARF_DWARFDebugArangeSet_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp new file mode 100644 index 000000000000..3b004c4b3890 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp @@ -0,0 +1,177 @@ +//===-- DWARFDebugAranges.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugAranges.h" + +#include <assert.h> +#include <stdio.h> + +#include <algorithm> + +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" +#include "DWARFDebugInfo.h" +#include "DWARFCompileUnit.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DWARFDebugAranges::DWARFDebugAranges() : + m_aranges() +{ +} + +//---------------------------------------------------------------------- +// CountArangeDescriptors +//---------------------------------------------------------------------- +class CountArangeDescriptors +{ +public: + CountArangeDescriptors (uint32_t& count_ref) : count(count_ref) + { +// printf("constructor CountArangeDescriptors()\n"); + } + void operator() (const DWARFDebugArangeSet& set) + { + count += set.NumDescriptors(); + } + uint32_t& count; +}; + + +//---------------------------------------------------------------------- +// Extract +//---------------------------------------------------------------------- +bool +DWARFDebugAranges::Extract(const DataExtractor &debug_aranges_data) +{ + if (debug_aranges_data.ValidOffset(0)) + { + lldb::offset_t offset = 0; + + DWARFDebugArangeSet set; + Range range; + while (set.Extract(debug_aranges_data, &offset)) + { + const uint32_t num_descriptors = set.NumDescriptors(); + if (num_descriptors > 0) + { + const dw_offset_t cu_offset = set.GetCompileUnitDIEOffset(); + + for (uint32_t i=0; i<num_descriptors; ++i) + { + const DWARFDebugArangeSet::Descriptor &descriptor = set.GetDescriptorRef(i); + m_aranges.Append(RangeToDIE::Entry (descriptor.address, descriptor.length, cu_offset)); + } + } + set.Clear(); + } + } + return false; +} + +//---------------------------------------------------------------------- +// Generate +//---------------------------------------------------------------------- +bool +DWARFDebugAranges::Generate(SymbolFileDWARF* dwarf2Data) +{ + Clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + const bool clear_dies_if_already_not_parsed = true; + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + if (cu) + cu->BuildAddressRangeTable(dwarf2Data, this, clear_dies_if_already_not_parsed); + } + } + return !IsEmpty(); +} + + +void +DWARFDebugAranges::Dump (Log *log) const +{ + if (log == NULL) + return; + + const size_t num_entries = m_aranges.GetSize(); + for (size_t i=0; i<num_entries; ++i) + { + const RangeToDIE::Entry *entry = m_aranges.GetEntryAtIndex(i); + if (entry) + log->Printf ("0x%8.8x: [0x%" PRIx64 " - 0x%" PRIx64 ")", + entry->data, + entry->GetRangeBase(), + entry->GetRangeEnd()); + } +} + +void +DWARFDebugAranges::AppendRange (dw_offset_t offset, dw_addr_t low_pc, dw_addr_t high_pc) +{ + if (high_pc > low_pc) + m_aranges.Append(RangeToDIE::Entry (low_pc, high_pc - low_pc, offset)); +} + +void +DWARFDebugAranges::Sort (bool minimize) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", + __PRETTY_FUNCTION__, this); + + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_ARANGES)); + size_t orig_arange_size = 0; + if (log) + { + orig_arange_size = m_aranges.GetSize(); + log->Printf ("DWARFDebugAranges::Sort(minimize = %u) with %" PRIu64 " entries", minimize, (uint64_t)orig_arange_size); + } + + m_aranges.Sort(); + m_aranges.CombineConsecutiveEntriesWithEqualData(); + + if (log) + { + if (minimize) + { + const size_t new_arange_size = m_aranges.GetSize(); + const size_t delta = orig_arange_size - new_arange_size; + log->Printf ("DWARFDebugAranges::Sort() %" PRIu64 " entries after minimizing (%" PRIu64 " entries combined for %" PRIu64 " bytes saved)", + (uint64_t)new_arange_size, + (uint64_t)delta, + (uint64_t)delta * sizeof(Range)); + } + Dump (log); + } +} + +//---------------------------------------------------------------------- +// FindAddress +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugAranges::FindAddress(dw_addr_t address) const +{ + const RangeToDIE::Entry *entry = m_aranges.FindEntryThatContains(address); + if (entry) + return entry->data; + return DW_INVALID_OFFSET; +} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h new file mode 100644 index 000000000000..88db929226ab --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h @@ -0,0 +1,94 @@ +//===-- DWARFDebugAranges.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugAranges_h_ +#define SymbolFileDWARF_DWARFDebugAranges_h_ + +#include "DWARFDebugArangeSet.h" +#include <list> + +#include "lldb/Core/RangeMap.h" + +class SymbolFileDWARF; + +class DWARFDebugAranges +{ +protected: + typedef lldb_private::RangeDataArray<dw_addr_t, uint32_t, dw_offset_t, 1> RangeToDIE; + +public: + typedef RangeToDIE::Entry Range; + typedef std::vector<RangeToDIE::Entry> RangeColl; + + DWARFDebugAranges(); + + void + Clear() + { + m_aranges.Clear(); + } + + bool + Extract(const lldb_private::DataExtractor &debug_aranges_data); + + bool + Generate(SymbolFileDWARF* dwarf2Data); + + // Use append range multiple times and then call sort + void + AppendRange (dw_offset_t cu_offset, + dw_addr_t low_pc, + dw_addr_t high_pc); + + void + Sort (bool minimize); + + const Range* + RangeAtIndex(uint32_t idx) const + { + return m_aranges.GetEntryAtIndex (idx); + } + + void + Dump (lldb_private::Log *log) const; + + dw_offset_t + FindAddress(dw_addr_t address) const; + + bool + IsEmpty() const + { + return m_aranges.IsEmpty(); + } + size_t + GetNumRanges() const + { + return m_aranges.GetSize(); + } + + dw_offset_t + OffsetAtIndex(uint32_t idx) const + { + const Range *range = m_aranges.GetEntryAtIndex (idx); + if (range) + return range->data; + return DW_INVALID_OFFSET; + } + + static void + Dump(SymbolFileDWARF* dwarf2Data, lldb_private::Stream *s); + +protected: + + + RangeToDIE m_aranges; +}; + + +#endif // SymbolFileDWARF_DWARFDebugAranges_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp new file mode 100644 index 000000000000..4c76eed8166f --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -0,0 +1,797 @@ +//===-- DWARFDebugInfo.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARF.h" + +#include <algorithm> +#include <set> + +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFCompileUnit.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFFormValue.h" +#include "LogChannelDWARF.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +DWARFDebugInfo::DWARFDebugInfo() : + m_dwarf2Data(NULL), + m_compile_units(), + m_cu_aranges_ap () +{ +} + +//---------------------------------------------------------------------- +// SetDwarfData +//---------------------------------------------------------------------- +void +DWARFDebugInfo::SetDwarfData(SymbolFileDWARF* dwarf2Data) +{ + m_dwarf2Data = dwarf2Data; + m_compile_units.clear(); +} + + +DWARFDebugAranges & +DWARFDebugInfo::GetCompileUnitAranges () +{ + if (m_cu_aranges_ap.get() == NULL && m_dwarf2Data) + { + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_ARANGES)); + + m_cu_aranges_ap.reset (new DWARFDebugAranges()); + const DataExtractor &debug_aranges_data = m_dwarf2Data->get_debug_aranges_data(); + if (debug_aranges_data.GetByteSize() > 0) + { + if (log) + log->Printf ("DWARFDebugInfo::GetCompileUnitAranges() for \"%s\" from .debug_aranges", + m_dwarf2Data->GetObjectFile()->GetFileSpec().GetPath().c_str()); + m_cu_aranges_ap->Extract (debug_aranges_data); + + } + else + { + if (log) + log->Printf ("DWARFDebugInfo::GetCompileUnitAranges() for \"%s\" by parsing", + m_dwarf2Data->GetObjectFile()->GetFileSpec().GetPath().c_str()); + const size_t num_compile_units = GetNumCompileUnits(); + const bool clear_dies_if_already_not_parsed = true; + for (size_t idx = 0; idx < num_compile_units; ++idx) + { + DWARFCompileUnit* cu = GetCompileUnitAtIndex(idx); + if (cu) + cu->BuildAddressRangeTable (m_dwarf2Data, m_cu_aranges_ap.get(), clear_dies_if_already_not_parsed); + } + } + + const bool minimize = true; + m_cu_aranges_ap->Sort (minimize); + } + return *m_cu_aranges_ap.get(); +} + + +//---------------------------------------------------------------------- +// LookupAddress +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::LookupAddress +( + const dw_addr_t address, + const dw_offset_t hint_die_offset, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die +) +{ + + if (hint_die_offset != DW_INVALID_OFFSET) + cu_sp = GetCompileUnit(hint_die_offset); + else + { + DWARFDebugAranges &cu_aranges = GetCompileUnitAranges (); + const dw_offset_t cu_offset = cu_aranges.FindAddress (address); + cu_sp = GetCompileUnit(cu_offset); + } + + if (cu_sp.get()) + { + if (cu_sp->LookupAddress(address, function_die, block_die)) + return true; + cu_sp.reset(); + } + else + { + // The hint_die_offset may have been a pointer to the actual item that + // we are looking for + DWARFDebugInfoEntry* die_ptr = GetDIEPtr(hint_die_offset, &cu_sp); + if (die_ptr) + { + if (cu_sp.get()) + { + if (function_die || block_die) + return die_ptr->LookupAddress(address, m_dwarf2Data, cu_sp.get(), function_die, block_die); + + // We only wanted the compile unit that contained this address + return true; + } + } + } + return false; +} + + +void +DWARFDebugInfo::ParseCompileUnitHeadersIfNeeded() +{ + if (m_compile_units.empty()) + { + if (m_dwarf2Data != NULL) + { + lldb::offset_t offset = 0; + const DataExtractor &debug_info_data = m_dwarf2Data->get_debug_info_data(); + while (debug_info_data.ValidOffset(offset)) + { + DWARFCompileUnitSP cu_sp(new DWARFCompileUnit(m_dwarf2Data)); + // Out of memory? + if (cu_sp.get() == NULL) + break; + + if (cu_sp->Extract(debug_info_data, &offset) == false) + break; + + m_compile_units.push_back(cu_sp); + + offset = cu_sp->GetNextCompileUnitOffset(); + } + } + } +} + +size_t +DWARFDebugInfo::GetNumCompileUnits() +{ + ParseCompileUnitHeadersIfNeeded(); + return m_compile_units.size(); +} + +DWARFCompileUnit* +DWARFDebugInfo::GetCompileUnitAtIndex(uint32_t idx) +{ + DWARFCompileUnit* cu = NULL; + if (idx < GetNumCompileUnits()) + cu = m_compile_units[idx].get(); + return cu; +} + +bool +DWARFDebugInfo::ContainsCompileUnit (const DWARFCompileUnit *cu) const +{ + // Not a verify efficient function, but it is handy for use in assertions + // to make sure that a compile unit comes from a debug information file. + CompileUnitColl::const_iterator end_pos = m_compile_units.end(); + CompileUnitColl::const_iterator pos; + + for (pos = m_compile_units.begin(); pos != end_pos; ++pos) + { + if (pos->get() == cu) + return true; + } + return false; +} + + +static bool CompileUnitOffsetLessThan (const DWARFCompileUnitSP& a, const DWARFCompileUnitSP& b) +{ + return a->GetOffset() < b->GetOffset(); +} + + +static int +CompareDWARFCompileUnitSPOffset (const void *key, const void *arrmem) +{ + const dw_offset_t key_cu_offset = *(dw_offset_t*) key; + const dw_offset_t cu_offset = ((DWARFCompileUnitSP *)arrmem)->get()->GetOffset(); + if (key_cu_offset < cu_offset) + return -1; + if (key_cu_offset > cu_offset) + return 1; + return 0; +} + +DWARFCompileUnitSP +DWARFDebugInfo::GetCompileUnit(dw_offset_t cu_offset, uint32_t* idx_ptr) +{ + DWARFCompileUnitSP cu_sp; + uint32_t cu_idx = DW_INVALID_INDEX; + if (cu_offset != DW_INVALID_OFFSET) + { + ParseCompileUnitHeadersIfNeeded(); + + DWARFCompileUnitSP* match = (DWARFCompileUnitSP*)bsearch(&cu_offset, &m_compile_units[0], m_compile_units.size(), sizeof(DWARFCompileUnitSP), CompareDWARFCompileUnitSPOffset); + if (match) + { + cu_sp = *match; + cu_idx = match - &m_compile_units[0]; + } + } + if (idx_ptr) + *idx_ptr = cu_idx; + return cu_sp; +} + +DWARFCompileUnitSP +DWARFDebugInfo::GetCompileUnitContainingDIE(dw_offset_t die_offset) +{ + DWARFCompileUnitSP cu_sp; + if (die_offset != DW_INVALID_OFFSET) + { + ParseCompileUnitHeadersIfNeeded(); + + CompileUnitColl::const_iterator end_pos = m_compile_units.end(); + CompileUnitColl::const_iterator pos; + + for (pos = m_compile_units.begin(); pos != end_pos; ++pos) + { + dw_offset_t cu_start_offset = (*pos)->GetOffset(); + dw_offset_t cu_end_offset = (*pos)->GetNextCompileUnitOffset(); + if (cu_start_offset <= die_offset && die_offset < cu_end_offset) + { + cu_sp = *pos; + break; + } + } + } + return cu_sp; +} + +//---------------------------------------------------------------------- +// Compare function DWARFDebugAranges::Range structures +//---------------------------------------------------------------------- +static bool CompareDIEOffset (const DWARFDebugInfoEntry& die1, const DWARFDebugInfoEntry& die2) +{ + return die1.GetOffset() < die2.GetOffset(); +} + + +//---------------------------------------------------------------------- +// GetDIE() +// +// Get the DIE (Debug Information Entry) with the specified offset. +//---------------------------------------------------------------------- +DWARFDebugInfoEntry* +DWARFDebugInfo::GetDIEPtr(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr) +{ + DWARFCompileUnitSP cu_sp(GetCompileUnitContainingDIE(die_offset)); + if (cu_sp_ptr) + *cu_sp_ptr = cu_sp; + if (cu_sp.get()) + return cu_sp->GetDIEPtr(die_offset); + return NULL; // Not found in any compile units +} + +DWARFDebugInfoEntry* +DWARFDebugInfo::GetDIEPtrWithCompileUnitHint (dw_offset_t die_offset, DWARFCompileUnit**cu_handle) +{ + assert (cu_handle); + DWARFDebugInfoEntry* die = NULL; + if (*cu_handle) + die = (*cu_handle)->GetDIEPtr(die_offset); + + if (die == NULL) + { + DWARFCompileUnitSP cu_sp (GetCompileUnitContainingDIE(die_offset)); + if (cu_sp.get()) + { + *cu_handle = cu_sp.get(); + die = cu_sp->GetDIEPtr(die_offset); + } + } + if (die == NULL) + *cu_handle = NULL; + return die; +} + + +const DWARFDebugInfoEntry* +DWARFDebugInfo::GetDIEPtrContainingOffset(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr) +{ + DWARFCompileUnitSP cu_sp(GetCompileUnitContainingDIE(die_offset)); + if (cu_sp_ptr) + *cu_sp_ptr = cu_sp; + if (cu_sp.get()) + return cu_sp->GetDIEPtrContainingOffset(die_offset); + + return NULL; // Not found in any compile units + +} + +//---------------------------------------------------------------------- +// DWARFDebugInfo_ParseCallback +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets parses all compile units and DIE's into an internate +// representation for further modification. +//---------------------------------------------------------------------- + +static dw_offset_t +DWARFDebugInfo_ParseCallback +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + DWARFDebugInfo* debug_info = (DWARFDebugInfo*)userData; + DWARFCompileUnit* cu = cu_sp.get(); + if (die) + { + cu->AddDIE(*die); + } + else if (cu) + { + debug_info->AddCompileUnit(cu_sp); + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// AddCompileUnit +//---------------------------------------------------------------------- +void +DWARFDebugInfo::AddCompileUnit(DWARFCompileUnitSP& cu) +{ + m_compile_units.push_back(cu); +} + +/* +void +DWARFDebugInfo::AddDIE(DWARFDebugInfoEntry& die) +{ + m_die_array.push_back(die); +} +*/ + + + + +//---------------------------------------------------------------------- +// Parse +// +// Parses the .debug_info section and uses the .debug_abbrev section +// and various other sections in the SymbolFileDWARF class and calls the +// supplied callback function each time a compile unit header, or debug +// information entry is successfully parsed. This function can be used +// for different tasks such as parsing the file contents into a +// structured data, dumping, verifying and much more. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Parse(SymbolFileDWARF* dwarf2Data, Callback callback, void* userData) +{ + if (dwarf2Data) + { + lldb::offset_t offset = 0; + uint32_t depth = 0; + DWARFCompileUnitSP cu(new DWARFCompileUnit(dwarf2Data)); + if (cu.get() == NULL) + return; + DWARFDebugInfoEntry die; + + while (cu->Extract(dwarf2Data->get_debug_info_data(), &offset)) + { + const dw_offset_t next_cu_offset = cu->GetNextCompileUnitOffset(); + + depth = 0; + // Call the callback function with no DIE pointer for the compile unit + // and get the offset that we are to continue to parse from + offset = callback(dwarf2Data, cu, NULL, offset, depth, userData); + + // Make sure we are within our compile unit + if (offset < next_cu_offset) + { + // We are in our compile unit, parse starting at the offset + // we were told to parse + bool done = false; + while (!done && die.Extract(dwarf2Data, cu.get(), &offset)) + { + // Call the callback function with DIE pointer that falls within the compile unit + offset = callback(dwarf2Data, cu, &die, offset, depth, userData); + + if (die.IsNULL()) + { + if (depth) + --depth; + else + done = true; // We are done with this compile unit! + } + else if (die.HasChildren()) + ++depth; + } + } + + // Make sure the offset returned is valid, and if not stop parsing. + // Returning DW_INVALID_OFFSET from this callback is a good way to end + // all parsing + if (!dwarf2Data->get_debug_info_data().ValidOffset(offset)) + break; + + // See if during the callback anyone retained a copy of the compile + // unit other than ourselves and if so, let whomever did own the object + // and create a new one for our own use! + if (!cu.unique()) + cu.reset(new DWARFCompileUnit(dwarf2Data)); + + + // Make sure we start on a proper + offset = next_cu_offset; + } + } +} + +typedef struct DumpInfo +{ + DumpInfo(Stream* init_strm, uint32_t off, uint32_t depth) : + strm(init_strm), + die_offset(off), + recurse_depth(depth), + found_depth(UINT32_MAX), + found_die(false), + ancestors() + { + } + Stream* strm; + const uint32_t die_offset; + const uint32_t recurse_depth; + uint32_t found_depth; + bool found_die; + std::vector<DWARFDebugInfoEntry> ancestors; + + DISALLOW_COPY_AND_ASSIGN(DumpInfo); +} DumpInfo; + +//---------------------------------------------------------------------- +// DumpCallback +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets called each time a compile unit header or debug information +// entry is successfully parsed. +// +// This function dump DWARF information and obey recurse depth and +// whether a single DIE is to be dumped (or all of the data). +//---------------------------------------------------------------------- +static dw_offset_t DumpCallback +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + DumpInfo* dumpInfo = (DumpInfo*)userData; + + const DWARFCompileUnit* cu = cu_sp.get(); + + Stream *s = dumpInfo->strm; + bool show_parents = s->GetFlags().Test(DWARFDebugInfo::eDumpFlag_ShowAncestors); + + if (die) + { + // Are we dumping everything? + if (dumpInfo->die_offset == DW_INVALID_OFFSET) + { + // Yes we are dumping everything. Obey our recurse level though + if (curr_depth < dumpInfo->recurse_depth) + die->Dump(dwarf2Data, cu, *s, 0); + } + else + { + // We are dumping a specific DIE entry by offset + if (dumpInfo->die_offset == die->GetOffset()) + { + // We found the DIE we were looking for, dump it! + if (show_parents) + { + s->SetIndentLevel(0); + const uint32_t num_ancestors = dumpInfo->ancestors.size(); + if (num_ancestors > 0) + { + for (uint32_t i=0; i<num_ancestors-1; ++i) + { + dumpInfo->ancestors[i].Dump(dwarf2Data, cu, *s, 0); + s->IndentMore(); + } + } + } + + dumpInfo->found_depth = curr_depth; + + die->Dump(dwarf2Data, cu, *s, 0); + + // Note that we found the DIE we were looking for + dumpInfo->found_die = true; + + // Since we are dumping a single DIE, if there are no children we are done! + if (!die->HasChildren() || dumpInfo->recurse_depth == 0) + return DW_INVALID_OFFSET; // Return an invalid address to end parsing + } + else if (dumpInfo->found_die) + { + // Are we done with all the children? + if (curr_depth <= dumpInfo->found_depth) + return DW_INVALID_OFFSET; + + // We have already found our DIE and are printing it's children. Obey + // our recurse depth and return an invalid offset if we get done + // dumping all the the children + if (dumpInfo->recurse_depth == UINT32_MAX || curr_depth <= dumpInfo->found_depth + dumpInfo->recurse_depth) + die->Dump(dwarf2Data, cu, *s, 0); + } + else if (dumpInfo->die_offset > die->GetOffset()) + { + if (show_parents) + dumpInfo->ancestors.back() = *die; + } + } + + // Keep up with our indent level + if (die->IsNULL()) + { + if (show_parents) + dumpInfo->ancestors.pop_back(); + + if (curr_depth <= 1) + return cu->GetNextCompileUnitOffset(); + else + s->IndentLess(); + } + else if (die->HasChildren()) + { + if (show_parents) + { + DWARFDebugInfoEntry null_die; + dumpInfo->ancestors.push_back(null_die); + } + s->IndentMore(); + } + } + else + { + if (cu == NULL) + s->PutCString("NULL - cu"); + // We have a compile unit, reset our indent level to zero just in case + s->SetIndentLevel(0); + + // See if we are dumping everything? + if (dumpInfo->die_offset == DW_INVALID_OFFSET) + { + // We are dumping everything + cu->Dump(s); + return cu->GetFirstDIEOffset(); // Return true to parse all DIEs in this Compile Unit + } + else + { + if (show_parents) + { + dumpInfo->ancestors.clear(); + dumpInfo->ancestors.resize(1); + } + + // We are dumping only a single DIE possibly with it's children and + // we must find it's compile unit before we can dump it properly + if (dumpInfo->die_offset < cu->GetFirstDIEOffset()) + { + // Not found, maybe the DIE offset provided wasn't correct? + // *ostrm_ptr << "DIE at offset " << HEX32 << dumpInfo->die_offset << " was not found." << endl; + return DW_INVALID_OFFSET; + } + else + { + // See if the DIE is in this compile unit? + if (dumpInfo->die_offset < cu->GetNextCompileUnitOffset()) + { + // This DIE is in this compile unit! + if (s->GetVerbose()) + cu->Dump(s); // Dump the compile unit for the DIE in verbose mode + + return next_offset; + // // We found our compile unit that contains our DIE, just skip to dumping the requested DIE... + // return dumpInfo->die_offset; + } + else + { + // Skip to the next compile unit as the DIE isn't in the current one! + return cu->GetNextCompileUnitOffset(); + } + } + } + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// Dump +// +// Dump the information in the .debug_info section to the specified +// ostream. If die_offset is valid, a single DIE will be dumped. If the +// die_offset is invalid, all the DWARF information will be dumped. Both +// cases will obey a "recurse_depth" or how deep to traverse into the +// children of each DIE entry. A recurse_depth of zero will dump all +// compile unit headers. A recurse_depth of 1 will dump all compile unit +// headers and the DW_TAG_compile unit tags. A depth of 2 will also +// dump all types and functions. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Dump +( + Stream *s, + SymbolFileDWARF* dwarf2Data, + const uint32_t die_offset, + const uint32_t recurse_depth +) +{ + DumpInfo dumpInfo(s, die_offset, recurse_depth); + s->PutCString(".debug_info contents"); + if (dwarf2Data->get_debug_info_data().GetByteSize() > 0) + { + if (die_offset == DW_INVALID_OFFSET) + s->PutCString(":\n"); + else + { + s->Printf(" for DIE entry at .debug_info[0x%8.8x]", die_offset); + if (recurse_depth != UINT32_MAX) + s->Printf(" recursing %u levels deep.", recurse_depth); + s->EOL(); + } + } + else + { + s->PutCString(": < EMPTY >\n"); + return; + } + DWARFDebugInfo::Parse(dwarf2Data, DumpCallback, &dumpInfo); +} + + +//---------------------------------------------------------------------- +// Dump +// +// Dump the contents of this DWARFDebugInfo object as has been parsed +// and/or modified after it has been parsed. +//---------------------------------------------------------------------- +void +DWARFDebugInfo::Dump (Stream *s, const uint32_t die_offset, const uint32_t recurse_depth) +{ + DumpInfo dumpInfo(s, die_offset, recurse_depth); + + s->PutCString("Dumping .debug_info section from internal representation\n"); + + CompileUnitColl::const_iterator pos; + uint32_t curr_depth = 0; + ParseCompileUnitHeadersIfNeeded(); + for (pos = m_compile_units.begin(); pos != m_compile_units.end(); ++pos) + { + const DWARFCompileUnitSP& cu_sp = *pos; + DumpCallback(m_dwarf2Data, (DWARFCompileUnitSP&)cu_sp, NULL, 0, curr_depth, &dumpInfo); + + const DWARFDebugInfoEntry* die = cu_sp->DIE(); + if (die) + die->Dump(m_dwarf2Data, cu_sp.get(), *s, recurse_depth); + } +} + + +//---------------------------------------------------------------------- +// FindCallbackString +// +// A callback function for the static DWARFDebugInfo::Parse() function +// that gets called each time a compile unit header or debug information +// entry is successfully parsed. +// +// This function will find the die_offset of any items whose DW_AT_name +// matches the given string +//---------------------------------------------------------------------- +typedef struct FindCallbackStringInfoTag +{ + const char* name; + bool ignore_case; + RegularExpression* regex; + vector<dw_offset_t>& die_offsets; +} FindCallbackStringInfo; + +static dw_offset_t FindCallbackString +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_sp, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t curr_depth, + void* userData +) +{ + FindCallbackStringInfo* info = (FindCallbackStringInfo*)userData; + const DWARFCompileUnit* cu = cu_sp.get(); + + if (die) + { + const char* die_name = die->GetName(dwarf2Data, cu); + if (die_name) + { + if (info->regex) + { + if (info->regex->Execute(die_name)) + info->die_offsets.push_back(die->GetOffset()); + } + else + { + if ((info->ignore_case ? strcasecmp(die_name, info->name) : strcmp(die_name, info->name)) == 0) + info->die_offsets.push_back(die->GetOffset()); + } + } + } + + // Just return the current offset to parse the next CU or DIE entry + return next_offset; +} + +//---------------------------------------------------------------------- +// Find +// +// Finds all DIE that have a specific DW_AT_name attribute by manually +// searching through the debug information (not using the +// .debug_pubnames section). The string must match the entire name +// and case sensitive searches are an option. +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::Find(const char* name, bool ignore_case, vector<dw_offset_t>& die_offsets) const +{ + die_offsets.clear(); + if (name && name[0]) + { + FindCallbackStringInfo info = { name, ignore_case, NULL, die_offsets }; + DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info); + } + return !die_offsets.empty(); +} + +//---------------------------------------------------------------------- +// Find +// +// Finds all DIE that have a specific DW_AT_name attribute by manually +// searching through the debug information (not using the +// .debug_pubnames section). The string must match the supplied regular +// expression. +//---------------------------------------------------------------------- +bool +DWARFDebugInfo::Find(RegularExpression& re, vector<dw_offset_t>& die_offsets) const +{ + die_offsets.clear(); + FindCallbackStringInfo info = { NULL, false, &re, die_offsets }; + DWARFDebugInfo::Parse(m_dwarf2Data, FindCallbackString, &info); + return !die_offsets.empty(); +} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h new file mode 100644 index 000000000000..50a7ae76921f --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -0,0 +1,89 @@ +//===-- DWARFDebugInfo.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugInfo_h_ +#define SymbolFileDWARF_DWARFDebugInfo_h_ + +#include <vector> +#include <map> + +#include "lldb/lldb-private.h" +#include "lldb/lldb-private.h" +#include "SymbolFileDWARF.h" + +typedef std::multimap<const char*, dw_offset_t, CStringCompareFunctionObject> CStringToDIEMap; +typedef CStringToDIEMap::iterator CStringToDIEMapIter; +typedef CStringToDIEMap::const_iterator CStringToDIEMapConstIter; + +typedef std::shared_ptr<DWARFCompileUnit> DWARFCompileUnitSP; + +class DWARFDebugInfo +{ +public: + typedef dw_offset_t (*Callback)( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnitSP& cu_shared_ptr, + DWARFDebugInfoEntry* die, + const dw_offset_t next_offset, + const uint32_t depth, + void* userData); + + DWARFDebugInfo(); + void SetDwarfData(SymbolFileDWARF* dwarf2Data); + + bool LookupAddress( + const dw_addr_t address, + const dw_offset_t cu_offset, // Can be valid (find in .debug_aranges), or DW_INVALID_OFFSET if we need to search manually + DWARFCompileUnitSP& cu_shared_ptr, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + void AddCompileUnit(DWARFCompileUnitSP& cu); + size_t GetNumCompileUnits(); + bool ContainsCompileUnit (const DWARFCompileUnit *cu) const; + DWARFCompileUnit* GetCompileUnitAtIndex(uint32_t idx); + DWARFCompileUnitSP GetCompileUnit(dw_offset_t cu_offset, uint32_t* idx_ptr = NULL); + DWARFCompileUnitSP GetCompileUnitContainingDIE(dw_offset_t die_offset); + + DWARFDebugInfoEntry* GetDIEPtr(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr); + DWARFDebugInfoEntry* GetDIEPtrWithCompileUnitHint (dw_offset_t die_offset, DWARFCompileUnit**cu_handle); + + const DWARFDebugInfoEntry* GetDIEPtrContainingOffset(dw_offset_t die_offset, DWARFCompileUnitSP* cu_sp_ptr); + + void Dump(lldb_private::Stream *s, const uint32_t die_offset, const uint32_t recurse_depth); + static void Parse(SymbolFileDWARF* parser, Callback callback, void* userData); + static void Verify(lldb_private::Stream *s, SymbolFileDWARF* dwarf2Data); + static void Dump(lldb_private::Stream *s, SymbolFileDWARF* dwarf2Data, const uint32_t die_offset, const uint32_t recurse_depth); + bool Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offsets) const; + bool Find(lldb_private::RegularExpression& re, std::vector<dw_offset_t>& die_offsets) const; + + enum + { + eDumpFlag_Verbose = (1<<0), // Verbose dumping + eDumpFlag_ShowForm = (1<<1), // Show the DW_form type + eDumpFlag_ShowAncestors = (1<<2) // Show all parent DIEs when dumping single DIEs + }; + + DWARFDebugAranges & + GetCompileUnitAranges (); + +protected: + SymbolFileDWARF* m_dwarf2Data; + typedef std::vector<DWARFCompileUnitSP> CompileUnitColl; + CompileUnitColl m_compile_units; + std::unique_ptr<DWARFDebugAranges> m_cu_aranges_ap; // A quick address to compile unit table + +private: + // All parsing needs to be done partially any managed by this class as accessors are called. + void ParseCompileUnitHeadersIfNeeded(); + + DISALLOW_COPY_AND_ASSIGN (DWARFDebugInfo); +}; + +#endif // SymbolFileDWARF_DWARFDebugInfo_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp new file mode 100644 index 000000000000..03c12e366f92 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -0,0 +1,2317 @@ +//===-- DWARFDebugInfoEntry.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugInfoEntry.h" + +#include <assert.h> + +#include <algorithm> + +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "DWARFCompileUnit.h" +#include "SymbolFileDWARF.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDeclContext.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFLocationDescription.h" +#include "DWARFLocationList.h" +#include "DWARFDebugRanges.h" + +using namespace lldb_private; +using namespace std; +extern int g_verbose; + + + +DWARFDebugInfoEntry::Attributes::Attributes() : + m_infos() +{ +} + +DWARFDebugInfoEntry::Attributes::~Attributes() +{ +} + + +uint32_t +DWARFDebugInfoEntry::Attributes::FindAttributeIndex(dw_attr_t attr) const +{ + collection::const_iterator end = m_infos.end(); + collection::const_iterator beg = m_infos.begin(); + collection::const_iterator pos; + for (pos = beg; pos != end; ++pos) + { + if (pos->attr == attr) + return std::distance(beg, pos); + } + return UINT32_MAX; +} + +void +DWARFDebugInfoEntry::Attributes::Append(const DWARFCompileUnit *cu, dw_offset_t attr_die_offset, dw_attr_t attr, dw_form_t form) +{ + Info info = { cu, attr_die_offset, attr, form }; + m_infos.push_back(info); +} + +bool +DWARFDebugInfoEntry::Attributes::ContainsAttribute(dw_attr_t attr) const +{ + return FindAttributeIndex(attr) != UINT32_MAX; +} + +bool +DWARFDebugInfoEntry::Attributes::RemoveAttribute(dw_attr_t attr) +{ + uint32_t attr_index = FindAttributeIndex(attr); + if (attr_index != UINT32_MAX) + { + m_infos.erase(m_infos.begin() + attr_index); + return true; + } + return false; +} + +bool +DWARFDebugInfoEntry::Attributes::ExtractFormValueAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, DWARFFormValue &form_value) const +{ + form_value.SetForm(FormAtIndex(i)); + lldb::offset_t offset = DIEOffsetAtIndex(i); + return form_value.ExtractValue(dwarf2Data->get_debug_info_data(), &offset, CompileUnitAtIndex(i)); +} + +uint64_t +DWARFDebugInfoEntry::Attributes::FormValueAsUnsigned (SymbolFileDWARF* dwarf2Data, dw_attr_t attr, uint64_t fail_value) const +{ + const uint32_t attr_idx = FindAttributeIndex (attr); + if (attr_idx != UINT32_MAX) + return FormValueAsUnsignedAtIndex (dwarf2Data, attr_idx, fail_value); + return fail_value; +} + +uint64_t +DWARFDebugInfoEntry::Attributes::FormValueAsUnsignedAtIndex(SymbolFileDWARF* dwarf2Data, uint32_t i, uint64_t fail_value) const +{ + DWARFFormValue form_value; + if (ExtractFormValueAtIndex(dwarf2Data, i, form_value)) + return form_value.Reference(CompileUnitAtIndex(i)); + return fail_value; +} + + + +bool +DWARFDebugInfoEntry::FastExtract +( + const DataExtractor& debug_info_data, + const DWARFCompileUnit* cu, + const uint8_t *fixed_form_sizes, + lldb::offset_t *offset_ptr +) +{ + m_offset = *offset_ptr; + m_parent_idx = 0; + m_sibling_idx = 0; + m_empty_children = false; + const uint64_t abbr_idx = debug_info_data.GetULEB128 (offset_ptr); + assert (abbr_idx < (1 << DIE_ABBR_IDX_BITSIZE)); + m_abbr_idx = abbr_idx; + + //assert (fixed_form_sizes); // For best performance this should be specified! + + if (m_abbr_idx) + { + lldb::offset_t offset = *offset_ptr; + + const DWARFAbbreviationDeclaration *abbrevDecl = cu->GetAbbreviations()->GetAbbreviationDeclaration(m_abbr_idx); + + if (abbrevDecl == NULL) + { + cu->GetSymbolFileDWARF()->GetObjectFile()->GetModule()->ReportError ("{0x%8.8x}: invalid abbreviation code %u, please file a bug and attach the file at the start of this error message", + m_offset, + (unsigned)abbr_idx); + // WE can't parse anymore if the DWARF is borked... + *offset_ptr = UINT32_MAX; + return false; + } + m_tag = abbrevDecl->Tag(); + m_has_children = abbrevDecl->HasChildren(); + // Skip all data in the .debug_info for the attributes + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + register uint32_t i; + register dw_form_t form; + for (i=0; i<numAttributes; ++i) + { + form = abbrevDecl->GetFormByIndexUnchecked(i); + + const uint8_t fixed_skip_size = fixed_form_sizes [form]; + if (fixed_skip_size) + offset += fixed_skip_size; + else + { + bool form_is_indirect = false; + do + { + form_is_indirect = false; + register uint32_t form_size = 0; + switch (form) + { + // Blocks if inlined data that have a length field and the data bytes + // inlined in the .debug_info + case DW_FORM_exprloc : + case DW_FORM_block : form_size = debug_info_data.GetULEB128 (&offset); break; + case DW_FORM_block1 : form_size = debug_info_data.GetU8_unchecked (&offset); break; + case DW_FORM_block2 : form_size = debug_info_data.GetU16_unchecked (&offset);break; + case DW_FORM_block4 : form_size = debug_info_data.GetU32_unchecked (&offset);break; + + // Inlined NULL terminated C-strings + case DW_FORM_string : + debug_info_data.GetCStr (&offset); + break; + + // Compile unit address sized values + case DW_FORM_addr : + form_size = cu->GetAddressByteSize(); + break; + case DW_FORM_ref_addr : + if (cu->GetVersion() <= 2) + form_size = cu->GetAddressByteSize(); + else + form_size = 4; // 4 bytes for DWARF 32, 8 bytes for DWARF 64, but we don't support DWARF64 yet + break; + + // 0 sized form + case DW_FORM_flag_present: + form_size = 0; + break; + + // 1 byte values + case DW_FORM_data1 : + case DW_FORM_flag : + case DW_FORM_ref1 : + form_size = 1; + break; + + // 2 byte values + case DW_FORM_data2 : + case DW_FORM_ref2 : + form_size = 2; + break; + + // 4 byte values + case DW_FORM_strp : + case DW_FORM_data4 : + case DW_FORM_ref4 : + form_size = 4; + break; + + // 8 byte values + case DW_FORM_data8 : + case DW_FORM_ref8 : + case DW_FORM_ref_sig8 : + form_size = 8; + break; + + // signed or unsigned LEB 128 values + case DW_FORM_sdata : + case DW_FORM_udata : + case DW_FORM_ref_udata : + debug_info_data.Skip_LEB128 (&offset); + break; + + case DW_FORM_indirect : + form_is_indirect = true; + form = debug_info_data.GetULEB128 (&offset); + break; + + case DW_FORM_sec_offset : + if (cu->GetAddressByteSize () == 4) + debug_info_data.GetU32 (offset_ptr); + else + debug_info_data.GetU64 (offset_ptr); + break; + + default: + *offset_ptr = m_offset; + return false; + } + offset += form_size; + + } while (form_is_indirect); + } + } + *offset_ptr = offset; + return true; + } + else + { + m_tag = 0; + m_has_children = false; + return true; // NULL debug tag entry + } + + return false; +} + +//---------------------------------------------------------------------- +// Extract +// +// Extract a debug info entry for a given compile unit from the +// .debug_info and .debug_abbrev data within the SymbolFileDWARF class +// starting at the given offset +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::Extract +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + lldb::offset_t *offset_ptr +) +{ + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); +// const DataExtractor& debug_str_data = dwarf2Data->get_debug_str_data(); + const uint32_t cu_end_offset = cu->GetNextCompileUnitOffset(); + const uint8_t cu_addr_size = cu->GetAddressByteSize(); + lldb::offset_t offset = *offset_ptr; +// if (offset >= cu_end_offset) +// Log::Error("DIE at offset 0x%8.8x is beyond the end of the current compile unit (0x%8.8x)", m_offset, cu_end_offset); + if ((offset < cu_end_offset) && debug_info_data.ValidOffset(offset)) + { + m_offset = offset; + + const uint64_t abbr_idx = debug_info_data.GetULEB128(&offset); + assert (abbr_idx < (1 << DIE_ABBR_IDX_BITSIZE)); + m_abbr_idx = abbr_idx; + if (abbr_idx) + { + const DWARFAbbreviationDeclaration *abbrevDecl = cu->GetAbbreviations()->GetAbbreviationDeclaration(abbr_idx); + + if (abbrevDecl) + { + m_tag = abbrevDecl->Tag(); + m_has_children = abbrevDecl->HasChildren(); + + bool isCompileUnitTag = m_tag == DW_TAG_compile_unit; + if (cu && isCompileUnitTag) + ((DWARFCompileUnit*)cu)->SetBaseAddress(0); + + // Skip all data in the .debug_info for the attributes + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + for (i=0; i<numAttributes; ++i) + { + abbrevDecl->GetAttrAndFormByIndexUnchecked(i, attr, form); + + if (isCompileUnitTag && ((attr == DW_AT_entry_pc) || (attr == DW_AT_low_pc))) + { + DWARFFormValue form_value(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + if (attr == DW_AT_low_pc || attr == DW_AT_entry_pc) + ((DWARFCompileUnit*)cu)->SetBaseAddress(form_value.Unsigned()); + } + } + else + { + bool form_is_indirect = false; + do + { + form_is_indirect = false; + register uint32_t form_size = 0; + switch (form) + { + // Blocks if inlined data that have a length field and the data bytes + // inlined in the .debug_info + case DW_FORM_exprloc : + case DW_FORM_block : form_size = debug_info_data.GetULEB128(&offset); break; + case DW_FORM_block1 : form_size = debug_info_data.GetU8(&offset); break; + case DW_FORM_block2 : form_size = debug_info_data.GetU16(&offset); break; + case DW_FORM_block4 : form_size = debug_info_data.GetU32(&offset); break; + + // Inlined NULL terminated C-strings + case DW_FORM_string : debug_info_data.GetCStr(&offset); break; + + // Compile unit address sized values + case DW_FORM_addr : + form_size = cu_addr_size; + break; + case DW_FORM_ref_addr : + if (cu->GetVersion() <= 2) + form_size = cu_addr_size; + else + form_size = 4; // 4 bytes for DWARF 32, 8 bytes for DWARF 64, but we don't support DWARF64 yet + break; + + // 0 sized form + case DW_FORM_flag_present: + form_size = 0; + break; + + // 1 byte values + case DW_FORM_data1 : + case DW_FORM_flag : + case DW_FORM_ref1 : + form_size = 1; + break; + + // 2 byte values + case DW_FORM_data2 : + case DW_FORM_ref2 : + form_size = 2; + break; + + // 4 byte values + case DW_FORM_strp : + form_size = 4; + break; + + case DW_FORM_data4 : + case DW_FORM_ref4 : + form_size = 4; + break; + + // 8 byte values + case DW_FORM_data8 : + case DW_FORM_ref8 : + case DW_FORM_ref_sig8 : + form_size = 8; + break; + + // signed or unsigned LEB 128 values + case DW_FORM_sdata : + case DW_FORM_udata : + case DW_FORM_ref_udata : + debug_info_data.Skip_LEB128(&offset); + break; + + case DW_FORM_indirect : + form = debug_info_data.GetULEB128(&offset); + form_is_indirect = true; + break; + + case DW_FORM_sec_offset : + if (cu->GetAddressByteSize () == 4) + debug_info_data.GetU32 (offset_ptr); + else + debug_info_data.GetU64 (offset_ptr); + break; + + default: + *offset_ptr = offset; + return false; + } + + offset += form_size; + } while (form_is_indirect); + } + } + *offset_ptr = offset; + return true; + } + } + else + { + m_tag = 0; + m_has_children = false; + *offset_ptr = offset; + return true; // NULL debug tag entry + } + } + + return false; +} + +//---------------------------------------------------------------------- +// DumpAncestry +// +// Dumps all of a debug information entries parents up until oldest and +// all of it's attributes to the specified stream. +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::DumpAncestry +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DWARFDebugInfoEntry* oldest, + Stream &s, + uint32_t recurse_depth +) const +{ + const DWARFDebugInfoEntry* parent = GetParent(); + if (parent && parent != oldest) + parent->DumpAncestry(dwarf2Data, cu, oldest, s, 0); + Dump(dwarf2Data, cu, s, recurse_depth); +} + +//---------------------------------------------------------------------- +// Compare two DIE by comparing all their attributes values, and +// following all DW_FORM_ref attributes and comparing their contents as +// well (except for DW_AT_sibling attributes. +// +// DWARFDebugInfoEntry::CompareState compare_state; +// int result = DWARFDebugInfoEntry::Compare(this, 0x00017ccb, 0x0001eb2b, compare_state, false, true); +//---------------------------------------------------------------------- +//int +//DWARFDebugInfoEntry::Compare +//( +// SymbolFileDWARF* dwarf2Data, +// dw_offset_t a_die_offset, +// dw_offset_t b_die_offset, +// CompareState &compare_state, +// bool compare_siblings, +// bool compare_children +//) +//{ +// if (a_die_offset == b_die_offset) +// return 0; +// +// DWARFCompileUnitSP a_cu_sp; +// DWARFCompileUnitSP b_cu_sp; +// const DWARFDebugInfoEntry* a_die = dwarf2Data->DebugInfo()->GetDIEPtr(a_die_offset, &a_cu_sp); +// const DWARFDebugInfoEntry* b_die = dwarf2Data->DebugInfo()->GetDIEPtr(b_die_offset, &b_cu_sp); +// +// return Compare(dwarf2Data, a_cu_sp.get(), a_die, b_cu_sp.get(), b_die, compare_state, compare_siblings, compare_children); +//} +// +//int +//DWARFDebugInfoEntry::Compare +//( +// SymbolFileDWARF* dwarf2Data, +// DWARFCompileUnit* a_cu, const DWARFDebugInfoEntry* a_die, +// DWARFCompileUnit* b_cu, const DWARFDebugInfoEntry* b_die, +// CompareState &compare_state, +// bool compare_siblings, +// bool compare_children +//) +//{ +// if (a_die == b_die) +// return 0; +// +// if (!compare_state.AddTypePair(a_die->GetOffset(), b_die->GetOffset())) +// { +// // We are already comparing both of these types, so let +// // compares complete for the real result +// return 0; +// } +// +// //printf("DWARFDebugInfoEntry::Compare(0x%8.8x, 0x%8.8x)\n", a_die->GetOffset(), b_die->GetOffset()); +// +// // Do we have two valid DIEs? +// if (a_die && b_die) +// { +// // Both DIE are valid +// int result = 0; +// +// const dw_tag_t a_tag = a_die->Tag(); +// const dw_tag_t b_tag = b_die->Tag(); +// if (a_tag == 0 && b_tag == 0) +// return 0; +// +// //printf(" comparing tags: %s and %s\n", DW_TAG_value_to_name(a_tag), DW_TAG_value_to_name(b_tag)); +// +// if (a_tag < b_tag) +// return -1; +// else if (a_tag > b_tag) +// return 1; +// +// DWARFDebugInfoEntry::Attributes a_attrs; +// DWARFDebugInfoEntry::Attributes b_attrs; +// size_t a_attr_count = a_die->GetAttributes(dwarf2Data, a_cu, a_attrs); +// size_t b_attr_count = b_die->GetAttributes(dwarf2Data, b_cu, b_attrs); +// if (a_attr_count != b_attr_count) +// { +// a_attrs.RemoveAttribute(DW_AT_sibling); +// b_attrs.RemoveAttribute(DW_AT_sibling); +// } +// +// a_attr_count = a_attrs.Size(); +// b_attr_count = b_attrs.Size(); +// +// DWARFFormValue a_form_value; +// DWARFFormValue b_form_value; +// +// if (a_attr_count != b_attr_count) +// { +// uint32_t is_decl_index = a_attrs.FindAttributeIndex(DW_AT_declaration); +// uint32_t a_name_index = UINT32_MAX; +// uint32_t b_name_index = UINT32_MAX; +// if (is_decl_index != UINT32_MAX) +// { +// if (a_attr_count == 2) +// { +// a_name_index = a_attrs.FindAttributeIndex(DW_AT_name); +// b_name_index = b_attrs.FindAttributeIndex(DW_AT_name); +// } +// } +// else +// { +// is_decl_index = b_attrs.FindAttributeIndex(DW_AT_declaration); +// if (is_decl_index != UINT32_MAX && a_attr_count == 2) +// { +// a_name_index = a_attrs.FindAttributeIndex(DW_AT_name); +// b_name_index = b_attrs.FindAttributeIndex(DW_AT_name); +// } +// } +// if (a_name_index != UINT32_MAX && b_name_index != UINT32_MAX) +// { +// if (a_attrs.ExtractFormValueAtIndex(dwarf2Data, a_name_index, a_form_value) && +// b_attrs.ExtractFormValueAtIndex(dwarf2Data, b_name_index, b_form_value)) +// { +// result = DWARFFormValue::Compare (a_form_value, b_form_value, a_cu, b_cu, &dwarf2Data->get_debug_str_data()); +// if (result == 0) +// { +// a_attr_count = b_attr_count = 0; +// compare_children = false; +// } +// } +// } +// } +// +// if (a_attr_count < b_attr_count) +// return -1; +// if (a_attr_count > b_attr_count) +// return 1; +// +// +// // The number of attributes are the same... +// if (a_attr_count > 0) +// { +// const DataExtractor* debug_str_data_ptr = &dwarf2Data->get_debug_str_data(); +// +// uint32_t i; +// for (i=0; i<a_attr_count; ++i) +// { +// const dw_attr_t a_attr = a_attrs.AttributeAtIndex(i); +// const dw_attr_t b_attr = b_attrs.AttributeAtIndex(i); +// //printf(" comparing attributes\n\t\t0x%8.8x: %s %s\t\t0x%8.8x: %s %s\n", +// // a_attrs.DIEOffsetAtIndex(i), DW_FORM_value_to_name(a_attrs.FormAtIndex(i)), DW_AT_value_to_name(a_attr), +// // b_attrs.DIEOffsetAtIndex(i), DW_FORM_value_to_name(b_attrs.FormAtIndex(i)), DW_AT_value_to_name(b_attr)); +// +// if (a_attr < b_attr) +// return -1; +// else if (a_attr > b_attr) +// return 1; +// +// switch (a_attr) +// { +// // Since we call a form of GetAttributes which inlines the +// // attributes from DW_AT_abstract_origin and DW_AT_specification +// // we don't care if their values mismatch... +// case DW_AT_abstract_origin: +// case DW_AT_specification: +// case DW_AT_sibling: +// case DW_AT_containing_type: +// //printf(" action = IGNORE\n"); +// result = 0; +// break; // ignore +// +// default: +// if (a_attrs.ExtractFormValueAtIndex(dwarf2Data, i, a_form_value) && +// b_attrs.ExtractFormValueAtIndex(dwarf2Data, i, b_form_value)) +// result = DWARFFormValue::Compare (a_form_value, b_form_value, a_cu, b_cu, debug_str_data_ptr); +// break; +// } +// +// //printf("\t result = %i\n", result); +// +// if (result != 0) +// { +// // Attributes weren't equal, lets see if we care? +// switch (a_attr) +// { +// case DW_AT_decl_file: +// // TODO: add the ability to compare files in two different compile units +// if (a_cu == b_cu) +// { +// //printf(" action = RETURN RESULT\n"); +// return result; // Only return the compare results when the compile units are the same and the decl_file attributes can be compared +// } +// else +// { +// result = 0; +// //printf(" action = IGNORE\n"); +// } +// break; +// +// default: +// switch (a_attrs.FormAtIndex(i)) +// { +// case DW_FORM_ref1: +// case DW_FORM_ref2: +// case DW_FORM_ref4: +// case DW_FORM_ref8: +// case DW_FORM_ref_udata: +// case DW_FORM_ref_addr: +// //printf(" action = COMPARE DIEs 0x%8.8x 0x%8.8x\n", (dw_offset_t)a_form_value.Reference(a_cu), (dw_offset_t)b_form_value.Reference(b_cu)); +// // These attribute values refer to other DIEs, so lets compare those instead of their DIE offsets... +// result = Compare(dwarf2Data, a_form_value.Reference(a_cu), b_form_value.Reference(b_cu), compare_state, false, true); +// if (result != 0) +// return result; +// break; +// +// default: +// // We do care that they were different, return this result... +// //printf(" action = RETURN RESULT\n"); +// return result; +// } +// } +// } +// } +// } +// //printf(" SUCCESS\n\t\t0x%8.8x: %s\n\t\t0x%8.8x: %s\n", a_die->GetOffset(), DW_TAG_value_to_name(a_tag), b_die->GetOffset(), DW_TAG_value_to_name(b_tag)); +// +// if (compare_children) +// { +// bool a_has_children = a_die->HasChildren(); +// bool b_has_children = b_die->HasChildren(); +// if (a_has_children == b_has_children) +// { +// // Both either have kids or don't +// if (a_has_children) +// result = Compare( dwarf2Data, +// a_cu, a_die->GetFirstChild(), +// b_cu, b_die->GetFirstChild(), +// compare_state, true, compare_children); +// else +// result = 0; +// } +// else if (!a_has_children) +// result = -1; // A doesn't have kids, but B does +// else +// result = 1; // A has kids, but B doesn't +// } +// +// if (compare_siblings) +// { +// result = Compare( dwarf2Data, +// a_cu, a_die->GetSibling(), +// b_cu, b_die->GetSibling(), +// compare_state, true, compare_children); +// } +// +// return result; +// } +// +// if (a_die == NULL) +// return -1; // a_die is NULL, yet b_die is non-NULL +// else +// return 1; // a_die is non-NULL, yet b_die is NULL +// +//} +// +// +//int +//DWARFDebugInfoEntry::Compare +//( +// SymbolFileDWARF* dwarf2Data, +// const DWARFCompileUnit* cu_a, +// const DWARFDebugInfoEntry* die_a, +// const DWARFCompileUnit* cu_a, +// const DWARFDebugInfoEntry* die_b, +// CompareState &compare_state +//) +//{ +//} + +//---------------------------------------------------------------------- +// GetDIENamesAndRanges +// +// Gets the valid address ranges for a given DIE by looking for a +// DW_AT_low_pc/DW_AT_high_pc pair, DW_AT_entry_pc, or DW_AT_ranges +// attributes. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::GetDIENamesAndRanges +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const char * &name, + const char * &mangled, + DWARFDebugRanges::RangeList& ranges, + int& decl_file, + int& decl_line, + int& decl_column, + int& call_file, + int& call_line, + int& call_column, + DWARFExpression *frame_base +) const +{ + if (dwarf2Data == NULL) + return false; + + dw_addr_t lo_pc = LLDB_INVALID_ADDRESS; + dw_addr_t hi_pc = LLDB_INVALID_ADDRESS; + std::vector<dw_offset_t> die_offsets; + bool set_frame_base_loclist_addr = false; + + lldb::offset_t offset; + const DWARFAbbreviationDeclaration* abbrevDecl = GetAbbreviationDeclarationPtr(dwarf2Data, cu, offset); + + if (abbrevDecl) + { + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + if (!debug_info_data.ValidOffset(offset)) + return false; + + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + bool do_offset = false; + + for (i=0; i<numAttributes; ++i) + { + abbrevDecl->GetAttrAndFormByIndexUnchecked(i, attr, form); + DWARFFormValue form_value(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + switch (attr) + { + case DW_AT_low_pc: + lo_pc = form_value.Unsigned(); + + if (do_offset) + hi_pc += lo_pc; + do_offset = false; + break; + + case DW_AT_entry_pc: + lo_pc = form_value.Unsigned(); + break; + + case DW_AT_high_pc: + hi_pc = form_value.Unsigned(); + if (form_value.Form() != DW_FORM_addr) + { + if (lo_pc == LLDB_INVALID_ADDRESS) + do_offset = hi_pc != LLDB_INVALID_ADDRESS; + else + hi_pc += lo_pc; // DWARF 4 introduces <offset-from-lo-pc> to save on relocations + } + break; + + case DW_AT_ranges: + { + const DWARFDebugRanges* debug_ranges = dwarf2Data->DebugRanges(); + debug_ranges->FindRanges(form_value.Unsigned(), ranges); + // All DW_AT_ranges are relative to the base address of the + // compile unit. We add the compile unit base address to make + // sure all the addresses are properly fixed up. + ranges.Slide(cu->GetBaseAddress()); + } + break; + + case DW_AT_name: + if (name == NULL) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + break; + + case DW_AT_MIPS_linkage_name: + case DW_AT_linkage_name: + if (mangled == NULL) + mangled = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + break; + + case DW_AT_abstract_origin: + die_offsets.push_back(form_value.Reference(cu)); + break; + + case DW_AT_specification: + die_offsets.push_back(form_value.Reference(cu)); + break; + + case DW_AT_decl_file: + if (decl_file == 0) + decl_file = form_value.Unsigned(); + break; + + case DW_AT_decl_line: + if (decl_line == 0) + decl_line = form_value.Unsigned(); + break; + + case DW_AT_decl_column: + if (decl_column == 0) + decl_column = form_value.Unsigned(); + break; + + case DW_AT_call_file: + if (call_file == 0) + call_file = form_value.Unsigned(); + break; + + case DW_AT_call_line: + if (call_line == 0) + call_line = form_value.Unsigned(); + break; + + case DW_AT_call_column: + if (call_column == 0) + call_column = form_value.Unsigned(); + break; + + case DW_AT_frame_base: + if (frame_base) + { + if (form_value.BlockData()) + { + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + frame_base->SetOpcodeData(debug_info_data, block_offset, block_length); + } + else + { + const DataExtractor &debug_loc_data = dwarf2Data->get_debug_loc_data(); + const dw_offset_t debug_loc_offset = form_value.Unsigned(); + + size_t loc_list_length = DWARFLocationList::Size(debug_loc_data, debug_loc_offset); + if (loc_list_length > 0) + { + frame_base->SetOpcodeData(debug_loc_data, debug_loc_offset, loc_list_length); + if (lo_pc != LLDB_INVALID_ADDRESS) + { + assert (lo_pc >= cu->GetBaseAddress()); + frame_base->SetLocationListSlide(lo_pc - cu->GetBaseAddress()); + } + else + { + set_frame_base_loclist_addr = true; + } + } + } + } + break; + + default: + break; + } + } + } + } + + if (ranges.IsEmpty()) + { + if (lo_pc != LLDB_INVALID_ADDRESS) + { + if (hi_pc != LLDB_INVALID_ADDRESS && hi_pc > lo_pc) + ranges.Append(DWARFDebugRanges::Range (lo_pc, hi_pc - lo_pc)); + else + ranges.Append(DWARFDebugRanges::Range (lo_pc, 0)); + } + } + + if (set_frame_base_loclist_addr) + { + dw_addr_t lowest_range_pc = ranges.GetMinRangeBase(0); + assert (lowest_range_pc >= cu->GetBaseAddress()); + frame_base->SetLocationListSlide (lowest_range_pc - cu->GetBaseAddress()); + } + + if (ranges.IsEmpty() || name == NULL || mangled == NULL) + { + std::vector<dw_offset_t>::const_iterator pos; + std::vector<dw_offset_t>::const_iterator end = die_offsets.end(); + for (pos = die_offsets.begin(); pos != end; ++pos) + { + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* die = NULL; + dw_offset_t die_offset = *pos; + if (die_offset != DW_INVALID_OFFSET) + { + die = dwarf2Data->DebugInfo()->GetDIEPtr(die_offset, &cu_sp_ptr); + if (die) + die->GetDIENamesAndRanges(dwarf2Data, cu_sp_ptr.get(), name, mangled, ranges, decl_file, decl_line, decl_column, call_file, call_line, call_column); + } + } + } + return !ranges.IsEmpty(); +} + +//---------------------------------------------------------------------- +// Dump +// +// Dumps a debug information entry and all of it's attributes to the +// specified stream. +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::Dump +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + Stream &s, + uint32_t recurse_depth +) const +{ + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + lldb::offset_t offset = m_offset; + + if (debug_info_data.ValidOffset(offset)) + { + dw_uleb128_t abbrCode = debug_info_data.GetULEB128(&offset); + + s.Printf("\n0x%8.8x: ", m_offset); + s.Indent(); + if (abbrCode != m_abbr_idx) + { + s.Printf( "error: DWARF has been modified\n"); + } + else if (abbrCode) + { + const DWARFAbbreviationDeclaration* abbrevDecl = cu->GetAbbreviations()->GetAbbreviationDeclaration (abbrCode); + + if (abbrevDecl) + { + s.PutCString(DW_TAG_value_to_name(abbrevDecl->Tag())); + s.Printf( " [%u] %c\n", abbrCode, abbrevDecl->HasChildren() ? '*':' '); + + // Dump all data in the .debug_info for the attributes + const uint32_t numAttributes = abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + for (i=0; i<numAttributes; ++i) + { + abbrevDecl->GetAttrAndFormByIndexUnchecked(i, attr, form); + + DumpAttribute(dwarf2Data, cu, debug_info_data, &offset, s, attr, form); + } + + const DWARFDebugInfoEntry* child = GetFirstChild(); + if (recurse_depth > 0 && child) + { + s.IndentMore(); + + while (child) + { + child->Dump(dwarf2Data, cu, s, recurse_depth-1); + child = child->GetSibling(); + } + s.IndentLess(); + } + } + else + s.Printf( "Abbreviation code note found in 'debug_abbrev' class for code: %u\n", abbrCode); + } + else + { + s.Printf( "NULL\n"); + } + } +} + +void +DWARFDebugInfoEntry::DumpLocation +( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + Stream &s +) const +{ + const DWARFDebugInfoEntry *cu_die = cu->GetCompileUnitDIEOnly(); + const char *cu_name = NULL; + if (cu_die != NULL) + cu_name = cu_die->GetName (dwarf2Data, cu); + const char *obj_file_name = NULL; + ObjectFile *obj_file = dwarf2Data->GetObjectFile(); + if (obj_file) + obj_file_name = obj_file->GetFileSpec().GetFilename().AsCString(); + const char *die_name = GetName (dwarf2Data, cu); + s.Printf ("0x%8.8x/0x%8.8x: %-30s (from %s in %s)", + cu->GetOffset(), + GetOffset(), + die_name ? die_name : "", + cu_name ? cu_name : "<NULL>", + obj_file_name ? obj_file_name : "<NULL>"); +} + +//---------------------------------------------------------------------- +// DumpAttribute +// +// Dumps a debug information entry attribute along with it's form. Any +// special display of attributes is done (disassemble location lists, +// show enumeration values for attributes, etc). +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::DumpAttribute +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DataExtractor& debug_info_data, + lldb::offset_t *offset_ptr, + Stream &s, + dw_attr_t attr, + dw_form_t form +) +{ + bool verbose = s.GetVerbose(); + bool show_form = s.GetFlags().Test(DWARFDebugInfo::eDumpFlag_ShowForm); + + const DataExtractor* debug_str_data = dwarf2Data ? &dwarf2Data->get_debug_str_data() : NULL; + if (verbose) + s.Offset (*offset_ptr); + else + s.Printf (" "); + s.Indent(DW_AT_value_to_name(attr)); + + if (show_form) + { + s.Printf( "[%s", DW_FORM_value_to_name(form)); + } + + DWARFFormValue form_value(form); + + if (!form_value.ExtractValue(debug_info_data, offset_ptr, cu)) + return; + + if (show_form) + { + if (form == DW_FORM_indirect) + { + s.Printf( " [%s]", DW_FORM_value_to_name(form_value.Form())); + } + + s.PutCString("] "); + } + + s.PutCString("( "); + + // Always dump form value if verbose is enabled + if (verbose) + { + form_value.Dump(s, debug_str_data, cu); + } + + + // Check to see if we have any special attribute formatters + switch (attr) + { + case DW_AT_stmt_list: + if ( verbose ) s.PutCString(" ( "); + s.Printf( "0x%8.8" PRIx64, form_value.Unsigned()); + if ( verbose ) s.PutCString(" )"); + break; + + case DW_AT_language: + if ( verbose ) s.PutCString(" ( "); + s.PutCString(DW_LANG_value_to_name(form_value.Unsigned())); + if ( verbose ) s.PutCString(" )"); + break; + + case DW_AT_encoding: + if ( verbose ) s.PutCString(" ( "); + s.PutCString(DW_ATE_value_to_name(form_value.Unsigned())); + if ( verbose ) s.PutCString(" )"); + break; + + case DW_AT_frame_base: + case DW_AT_location: + case DW_AT_data_member_location: + { + const uint8_t* blockData = form_value.BlockData(); + if (blockData) + { + if (!verbose) + form_value.Dump(s, debug_str_data, cu); + + // Location description is inlined in data in the form value + DataExtractor locationData(debug_info_data, (*offset_ptr) - form_value.Unsigned(), form_value.Unsigned()); + if ( verbose ) s.PutCString(" ( "); + print_dwarf_expression (s, locationData, DWARFCompileUnit::GetAddressByteSize(cu), 4, false); + if ( verbose ) s.PutCString(" )"); + } + else + { + // We have a location list offset as the value that is + // the offset into the .debug_loc section that describes + // the value over it's lifetime + uint64_t debug_loc_offset = form_value.Unsigned(); + if (dwarf2Data) + { + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + DWARFLocationList::Dump(s, cu, dwarf2Data->get_debug_loc_data(), debug_loc_offset); + } + else + { + if ( !verbose ) + form_value.Dump(s, NULL, cu); + } + } + } + break; + + case DW_AT_abstract_origin: + case DW_AT_specification: + { + uint64_t abstract_die_offset = form_value.Reference(cu); + form_value.Dump(s, debug_str_data, cu); + // *ostrm_ptr << HEX32 << abstract_die_offset << " ( "; + if ( verbose ) s.PutCString(" ( "); + GetName(dwarf2Data, cu, abstract_die_offset, s); + if ( verbose ) s.PutCString(" )"); + } + break; + + case DW_AT_type: + { + uint64_t type_die_offset = form_value.Reference(cu); + if (!verbose) + form_value.Dump(s, debug_str_data, cu); + s.PutCString(" ( "); + AppendTypeName(dwarf2Data, cu, type_die_offset, s); + s.PutCString(" )"); + } + break; + + case DW_AT_ranges: + { + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + lldb::offset_t ranges_offset = form_value.Unsigned(); + dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0; + if (dwarf2Data) + DWARFDebugRanges::Dump(s, dwarf2Data->get_debug_ranges_data(), &ranges_offset, base_addr); + } + break; + + default: + if ( !verbose ) + form_value.Dump(s, debug_str_data, cu); + break; + } + + s.PutCString(" )\n"); +} + +//---------------------------------------------------------------------- +// Get all attribute values for a given DIE, including following any +// specification or abstract origin attributes and including those in +// the results. Any duplicate attributes will have the first instance +// take precedence (this can happen for declaration attributes). +//---------------------------------------------------------------------- +size_t +DWARFDebugInfoEntry::GetAttributes +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const uint8_t *fixed_form_sizes, + DWARFDebugInfoEntry::Attributes& attributes, + uint32_t curr_depth +) const +{ + lldb::offset_t offset; + const DWARFAbbreviationDeclaration* abbrevDecl = GetAbbreviationDeclarationPtr(dwarf2Data, cu, offset); + + if (abbrevDecl) + { + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + if (fixed_form_sizes == NULL) + fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize(cu->GetAddressByteSize()); + + const uint32_t num_attributes = abbrevDecl->NumAttributes(); + uint32_t i; + dw_attr_t attr; + dw_form_t form; + DWARFFormValue form_value; + for (i=0; i<num_attributes; ++i) + { + abbrevDecl->GetAttrAndFormByIndexUnchecked (i, attr, form); + + // If we are tracking down DW_AT_specification or DW_AT_abstract_origin + // attributes, the depth will be non-zero. We need to omit certain + // attributes that don't make sense. + switch (attr) + { + case DW_AT_sibling: + case DW_AT_declaration: + if (curr_depth > 0) + { + // This attribute doesn't make sense when combined with + // the DIE that references this DIE. We know a DIE is + // referencing this DIE because curr_depth is not zero + break; + } + // Fall through... + default: + attributes.Append(cu, offset, attr, form); + break; + } + + if ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin)) + { + form_value.SetForm(form); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + const DWARFDebugInfoEntry* die = NULL; + dw_offset_t die_offset = form_value.Reference(cu); + if (cu->ContainsDIEOffset(die_offset)) + { + die = const_cast<DWARFCompileUnit*>(cu)->GetDIEPtr(die_offset); + if (die) + die->GetAttributes(dwarf2Data, cu, fixed_form_sizes, attributes, curr_depth + 1); + } + else + { + DWARFCompileUnitSP cu_sp_ptr; + die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(die_offset, &cu_sp_ptr); + if (die) + die->GetAttributes(dwarf2Data, cu_sp_ptr.get(), fixed_form_sizes, attributes, curr_depth + 1); + } + } + } + else + { + const uint8_t fixed_skip_size = fixed_form_sizes [form]; + if (fixed_skip_size) + offset += fixed_skip_size; + else + DWARFFormValue::SkipValue(form, debug_info_data, &offset, cu); + } + } + } + else + { + attributes.Clear(); + } + return attributes.Size(); + +} + +//---------------------------------------------------------------------- +// GetAttributeValue +// +// Get the value of an attribute and return the .debug_info offset of the +// attribute if it was properly extracted into form_value, or zero +// if we fail since an offset of zero is invalid for an attribute (it +// would be a compile unit header). +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugInfoEntry::GetAttributeValue +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DWARFFormValue& form_value, + dw_offset_t* end_attr_offset_ptr +) const +{ + lldb::offset_t offset; + const DWARFAbbreviationDeclaration* abbrevDecl = GetAbbreviationDeclarationPtr(dwarf2Data, cu, offset); + + if (abbrevDecl) + { + uint32_t attr_idx = abbrevDecl->FindAttributeIndex(attr); + + if (attr_idx != DW_INVALID_INDEX) + { + const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); + + uint32_t idx=0; + while (idx<attr_idx) + DWARFFormValue::SkipValue(abbrevDecl->GetFormByIndex(idx++), debug_info_data, &offset, cu); + + const dw_offset_t attr_offset = offset; + form_value.SetForm(abbrevDecl->GetFormByIndex(idx)); + if (form_value.ExtractValue(debug_info_data, &offset, cu)) + { + if (end_attr_offset_ptr) + *end_attr_offset_ptr = offset; + return attr_offset; + } + } + } + + return 0; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsString +// +// Get the value of an attribute as a string return it. The resulting +// pointer to the string data exists within the supplied SymbolFileDWARF +// and will only be available as long as the SymbolFileDWARF is still around +// and it's content doesn't change. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetAttributeValueAsString +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + const char* fail_value) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.AsCString(&dwarf2Data->get_debug_str_data()); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsUnsigned +// +// Get the value of an attribute as unsigned and return it. +//---------------------------------------------------------------------- +uint64_t +DWARFDebugInfoEntry::GetAttributeValueAsUnsigned +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Unsigned(); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsSigned +// +// Get the value of an attribute a signed value and return it. +//---------------------------------------------------------------------- +int64_t +DWARFDebugInfoEntry::GetAttributeValueAsSigned +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + int64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Signed(); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeValueAsReference +// +// Get the value of an attribute as reference and fix up and compile +// unit relative offsets as needed. +//---------------------------------------------------------------------- +uint64_t +DWARFDebugInfoEntry::GetAttributeValueAsReference +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) + return form_value.Reference(cu); + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeHighPC +// +// Get the hi_pc, adding hi_pc to lo_pc when specified +// as an <offset-from-low-pc>. +// +// Returns the hi_pc or fail_value. +//---------------------------------------------------------------------- +dw_addr_t +DWARFDebugInfoEntry::GetAttributeHighPC +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + dw_addr_t lo_pc, + uint64_t fail_value +) const +{ + DWARFFormValue form_value; + + if (GetAttributeValue(dwarf2Data, cu, DW_AT_high_pc, form_value)) + { + dw_addr_t hi_pc = form_value.Unsigned(); + if (form_value.Form() != DW_FORM_addr) + hi_pc += lo_pc; // DWARF4 can specify the hi_pc as an <offset-from-lowpc> + return hi_pc; + } + return fail_value; +} + +//---------------------------------------------------------------------- +// GetAttributeAddressRange +// +// Get the lo_pc and hi_pc, adding hi_pc to lo_pc when specified +// as an <offset-from-low-pc>. +// +// Returns true or sets lo_pc and hi_pc to fail_value. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::GetAttributeAddressRange +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + dw_addr_t& lo_pc, + dw_addr_t& hi_pc, + uint64_t fail_value +) const +{ + lo_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, fail_value); + if (lo_pc != fail_value) + { + hi_pc = GetAttributeHighPC(dwarf2Data, cu, lo_pc, fail_value); + if (hi_pc != fail_value) + return true; + } + lo_pc = fail_value; + hi_pc = fail_value; + return false; +} +//---------------------------------------------------------------------- +// GetAttributeValueAsLocation +// +// Get the value of an attribute as reference and fix up and compile +// unit relative offsets as needed. +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugInfoEntry::GetAttributeValueAsLocation +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DataExtractor& location_data, + uint32_t &block_size +) const +{ + block_size = 0; + DWARFFormValue form_value; + + // Empty out data in case we don't find anything + location_data.Clear(); + dw_offset_t end_addr_offset = DW_INVALID_OFFSET; + const dw_offset_t attr_offset = GetAttributeValue(dwarf2Data, cu, attr, form_value, &end_addr_offset); + if (attr_offset) + { + const uint8_t* blockData = form_value.BlockData(); + if (blockData) + { + // We have an inlined location list in the .debug_info section + const DataExtractor& debug_info = dwarf2Data->get_debug_info_data(); + dw_offset_t block_offset = blockData - debug_info.GetDataStart(); + block_size = (end_addr_offset - attr_offset) - form_value.Unsigned(); + location_data.SetData(debug_info, block_offset, block_size); + } + else + { + // We have a location list offset as the value that is + // the offset into the .debug_loc section that describes + // the value over it's lifetime + lldb::offset_t debug_loc_offset = form_value.Unsigned(); + if (dwarf2Data) + { + assert(dwarf2Data->get_debug_loc_data().GetAddressByteSize() == cu->GetAddressByteSize()); + return DWARFLocationList::Extract(dwarf2Data->get_debug_loc_data(), &debug_loc_offset, location_data); + } + } + } + return attr_offset; +} + +//---------------------------------------------------------------------- +// GetName +// +// Get value of the DW_AT_name attribute and return it if one exists, +// else return NULL. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu +) const +{ + DWARFFormValue form_value; + if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + return form_value.AsCString(&dwarf2Data->get_debug_str_data()); + else + { + if (GetAttributeValue(dwarf2Data, cu, DW_AT_specification, form_value)) + { + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(cu), &cu_sp_ptr); + if (die) + return die->GetName(dwarf2Data, cu_sp_ptr.get()); + } + } + return NULL; +} + + +//---------------------------------------------------------------------- +// GetMangledName +// +// Get value of the DW_AT_MIPS_linkage_name attribute and return it if +// one exists, else return the value of the DW_AT_name attribute +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetMangledName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + bool substitute_name_allowed +) const +{ + const char* name = NULL; + DWARFFormValue form_value; + + if (GetAttributeValue(dwarf2Data, cu, DW_AT_MIPS_linkage_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + + if (GetAttributeValue(dwarf2Data, cu, DW_AT_linkage_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + + if (substitute_name_allowed && name == NULL) + { + if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + } + return name; +} + + +//---------------------------------------------------------------------- +// GetPubname +// +// Get value the name for a DIE as it should appear for a +// .debug_pubnames or .debug_pubtypes section. +//---------------------------------------------------------------------- +const char* +DWARFDebugInfoEntry::GetPubname +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu +) const +{ + const char* name = NULL; + if (!dwarf2Data) + return name; + + DWARFFormValue form_value; + + if (GetAttributeValue(dwarf2Data, cu, DW_AT_MIPS_linkage_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + else if (GetAttributeValue(dwarf2Data, cu, DW_AT_linkage_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + else if (GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + else if (GetAttributeValue(dwarf2Data, cu, DW_AT_specification, form_value)) + { + // The specification DIE may be in another compile unit so we need + // to get a die and its compile unit. + DWARFCompileUnitSP cu_sp_ptr; + const DWARFDebugInfoEntry* die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(cu), &cu_sp_ptr); + if (die) + return die->GetPubname(dwarf2Data, cu_sp_ptr.get()); + } + return name; +} + + +//---------------------------------------------------------------------- +// GetName +// +// Get value of the DW_AT_name attribute for a debug information entry +// that exists at offset "die_offset" and place that value into the +// supplied stream object. If the DIE is a NULL object "NULL" is placed +// into the stream, and if no DW_AT_name attribute exists for the DIE +// then nothing is printed. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::GetName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_offset_t die_offset, + Stream &s +) +{ + if (dwarf2Data == NULL) + { + s.PutCString("NULL"); + return false; + } + + DWARFDebugInfoEntry die; + lldb::offset_t offset = die_offset; + if (die.Extract(dwarf2Data, cu, &offset)) + { + if (die.IsNULL()) + { + s.PutCString("NULL"); + return true; + } + else + { + DWARFFormValue form_value; + if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + { + const char* name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + if (name) + { + s.PutCString(name); + return true; + } + } + } + } + return false; +} + +//---------------------------------------------------------------------- +// AppendTypeName +// +// Follows the type name definition down through all needed tags to +// end up with a fully qualified type name and dump the results to +// the supplied stream. This is used to show the name of types given +// a type identifier. +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::AppendTypeName +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_offset_t die_offset, + Stream &s +) +{ + if (dwarf2Data == NULL) + { + s.PutCString("NULL"); + return false; + } + + DWARFDebugInfoEntry die; + lldb::offset_t offset = die_offset; + if (die.Extract(dwarf2Data, cu, &offset)) + { + if (die.IsNULL()) + { + s.PutCString("NULL"); + return true; + } + else + { + const char* name = die.GetPubname(dwarf2Data, cu); + // if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_name, form_value)) + // name = form_value.AsCString(&dwarf2Data->get_debug_str_data()); + if (name) + s.PutCString(name); + else + { + bool result = true; + const DWARFAbbreviationDeclaration* abbrevDecl = die.GetAbbreviationDeclarationPtr(dwarf2Data, cu, offset); + + if (abbrevDecl == NULL) + return false; + + switch (abbrevDecl->Tag()) + { + case DW_TAG_array_type: break; // print out a "[]" after printing the full type of the element below + case DW_TAG_base_type: s.PutCString("base "); break; + case DW_TAG_class_type: s.PutCString("class "); break; + case DW_TAG_const_type: s.PutCString("const "); break; + case DW_TAG_enumeration_type: s.PutCString("enum "); break; + case DW_TAG_file_type: s.PutCString("file "); break; + case DW_TAG_interface_type: s.PutCString("interface "); break; + case DW_TAG_packed_type: s.PutCString("packed "); break; + case DW_TAG_pointer_type: break; // print out a '*' after printing the full type below + case DW_TAG_ptr_to_member_type: break; // print out a '*' after printing the full type below + case DW_TAG_reference_type: break; // print out a '&' after printing the full type below + case DW_TAG_restrict_type: s.PutCString("restrict "); break; + case DW_TAG_set_type: s.PutCString("set "); break; + case DW_TAG_shared_type: s.PutCString("shared "); break; + case DW_TAG_string_type: s.PutCString("string "); break; + case DW_TAG_structure_type: s.PutCString("struct "); break; + case DW_TAG_subrange_type: s.PutCString("subrange "); break; + case DW_TAG_subroutine_type: s.PutCString("function "); break; + case DW_TAG_thrown_type: s.PutCString("thrown "); break; + case DW_TAG_union_type: s.PutCString("union "); break; + case DW_TAG_unspecified_type: s.PutCString("unspecified "); break; + case DW_TAG_volatile_type: s.PutCString("volatile "); break; + default: + return false; + } + + // Follow the DW_AT_type if possible + DWARFFormValue form_value; + if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_type, form_value)) + { + uint64_t next_die_offset = form_value.Reference(cu); + result = AppendTypeName(dwarf2Data, cu, next_die_offset, s); + } + + switch (abbrevDecl->Tag()) + { + case DW_TAG_array_type: s.PutCString("[]"); break; + case DW_TAG_pointer_type: s.PutChar('*'); break; + case DW_TAG_ptr_to_member_type: s.PutChar('*'); break; + case DW_TAG_reference_type: s.PutChar('&'); break; + default: + break; + } + return result; + } + } + } + return false; +} + +bool +DWARFDebugInfoEntry::Contains (const DWARFDebugInfoEntry *die) const +{ + if (die) + { + const dw_offset_t die_offset = die->GetOffset(); + if (die_offset > GetOffset()) + { + const DWARFDebugInfoEntry *sibling = GetSibling(); + assert (sibling); // TODO: take this out + if (sibling) + return die_offset < sibling->GetOffset(); + } + } + return false; +} + +//---------------------------------------------------------------------- +// BuildAddressRangeTable +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::BuildAddressRangeTable +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges +) const +{ + if (m_tag) + { + if (m_tag == DW_TAG_subprogram) + { + dw_addr_t lo_pc = LLDB_INVALID_ADDRESS; + dw_addr_t hi_pc = LLDB_INVALID_ADDRESS; + if (GetAttributeAddressRange(dwarf2Data, cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) + { + /// printf("BuildAddressRangeTable() 0x%8.8x: %30s: [0x%8.8x - 0x%8.8x)\n", m_offset, DW_TAG_value_to_name(tag), lo_pc, hi_pc); + debug_aranges->AppendRange (cu->GetOffset(), lo_pc, hi_pc); + } + } + + + const DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + child->BuildAddressRangeTable(dwarf2Data, cu, debug_aranges); + child = child->GetSibling(); + } + } +} + +//---------------------------------------------------------------------- +// BuildFunctionAddressRangeTable +// +// This function is very similar to the BuildAddressRangeTable function +// except that the actual DIE offset for the function is placed in the +// table instead of the compile unit offset (which is the way the +// standard .debug_aranges section does it). +//---------------------------------------------------------------------- +void +DWARFDebugInfoEntry::BuildFunctionAddressRangeTable +( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges +) const +{ + if (m_tag) + { + if (m_tag == DW_TAG_subprogram) + { + dw_addr_t lo_pc = LLDB_INVALID_ADDRESS; + dw_addr_t hi_pc = LLDB_INVALID_ADDRESS; + if (GetAttributeAddressRange(dwarf2Data, cu, lo_pc, hi_pc, LLDB_INVALID_ADDRESS)) + { + // printf("BuildAddressRangeTable() 0x%8.8x: [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")\n", m_offset, lo_pc, hi_pc); // DEBUG ONLY + debug_aranges->AppendRange (GetOffset(), lo_pc, hi_pc); + } + } + + const DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + child->BuildFunctionAddressRangeTable(dwarf2Data, cu, debug_aranges); + child = child->GetSibling(); + } + } +} + +void +DWARFDebugInfoEntry::GetDeclContextDIEs (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + DWARFDIECollection &decl_context_dies) const +{ + const DWARFDebugInfoEntry *parent_decl_ctx_die = GetParentDeclContextDIE (dwarf2Data, cu); + if (parent_decl_ctx_die && parent_decl_ctx_die != this) + { + decl_context_dies.Append(parent_decl_ctx_die); + parent_decl_ctx_die->GetDeclContextDIEs (dwarf2Data, cu, decl_context_dies); + } +} + +void +DWARFDebugInfoEntry::GetDWARFDeclContext (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + DWARFDeclContext &dwarf_decl_ctx) const +{ + const dw_tag_t tag = Tag(); + if (tag != DW_TAG_compile_unit) + { + dwarf_decl_ctx.AppendDeclContext(tag, GetName(dwarf2Data, cu)); + const DWARFDebugInfoEntry *parent_decl_ctx_die = GetParentDeclContextDIE (dwarf2Data, cu); + if (parent_decl_ctx_die && parent_decl_ctx_die != this) + { + if (parent_decl_ctx_die->Tag() != DW_TAG_compile_unit) + parent_decl_ctx_die->GetDWARFDeclContext (dwarf2Data, cu, dwarf_decl_ctx); + } + } +} + + +bool +DWARFDebugInfoEntry::MatchesDWARFDeclContext (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + const DWARFDeclContext &dwarf_decl_ctx) const +{ + + DWARFDeclContext this_dwarf_decl_ctx; + GetDWARFDeclContext (dwarf2Data, cu, this_dwarf_decl_ctx); + return this_dwarf_decl_ctx == dwarf_decl_ctx; +} + +const DWARFDebugInfoEntry * +DWARFDebugInfoEntry::GetParentDeclContextDIE (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu) const +{ + DWARFDebugInfoEntry::Attributes attributes; + GetAttributes(dwarf2Data, cu, NULL, attributes); + return GetParentDeclContextDIE (dwarf2Data, cu, attributes); +} + +const DWARFDebugInfoEntry * +DWARFDebugInfoEntry::GetParentDeclContextDIE (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + const DWARFDebugInfoEntry::Attributes& attributes) const +{ + const DWARFDebugInfoEntry * die = this; + + while (die != NULL) + { + // If this is the original DIE that we are searching for a declaration + // for, then don't look in the cache as we don't want our own decl + // context to be our decl context... + if (die != this) + { + switch (die->Tag()) + { + case DW_TAG_compile_unit: + case DW_TAG_namespace: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + return die; + + default: + break; + } + } + + dw_offset_t die_offset; + + die_offset = attributes.FormValueAsUnsigned(dwarf2Data, DW_AT_specification, DW_INVALID_OFFSET); + if (die_offset != DW_INVALID_OFFSET) + { + const DWARFDebugInfoEntry *spec_die = cu->GetDIEPtr (die_offset); + if (spec_die) + { + const DWARFDebugInfoEntry *spec_die_decl_ctx_die = spec_die->GetParentDeclContextDIE (dwarf2Data, cu); + if (spec_die_decl_ctx_die) + return spec_die_decl_ctx_die; + } + } + + die_offset = attributes.FormValueAsUnsigned(dwarf2Data, DW_AT_abstract_origin, DW_INVALID_OFFSET); + if (die_offset != DW_INVALID_OFFSET) + { + const DWARFDebugInfoEntry *abs_die = cu->GetDIEPtr (die_offset); + if (abs_die) + { + const DWARFDebugInfoEntry *abs_die_decl_ctx_die = abs_die->GetParentDeclContextDIE (dwarf2Data, cu); + if (abs_die_decl_ctx_die) + return abs_die_decl_ctx_die; + } + } + + die = die->GetParent(); + } + return NULL; +} + + +const char * +DWARFDebugInfoEntry::GetQualifiedName (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + std::string &storage) const +{ + DWARFDebugInfoEntry::Attributes attributes; + GetAttributes(dwarf2Data, cu, NULL, attributes); + return GetQualifiedName (dwarf2Data, cu, attributes, storage); +} + +const char* +DWARFDebugInfoEntry::GetQualifiedName (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + const DWARFDebugInfoEntry::Attributes& attributes, + std::string &storage) const +{ + + const char *name = GetName (dwarf2Data, cu); + + if (name) + { + const DWARFDebugInfoEntry *parent_decl_ctx_die = GetParentDeclContextDIE (dwarf2Data, cu); + storage.clear(); + // TODO: change this to get the correct decl context parent.... + while (parent_decl_ctx_die) + { + const dw_tag_t parent_tag = parent_decl_ctx_die->Tag(); + switch (parent_tag) + { + case DW_TAG_namespace: + { + const char *namespace_name = parent_decl_ctx_die->GetName (dwarf2Data, cu); + if (namespace_name) + { + storage.insert (0, "::"); + storage.insert (0, namespace_name); + } + else + { + storage.insert (0, "(anonymous namespace)::"); + } + parent_decl_ctx_die = parent_decl_ctx_die->GetParentDeclContextDIE(dwarf2Data, cu); + } + break; + + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + { + const char *class_union_struct_name = parent_decl_ctx_die->GetName (dwarf2Data, cu); + + if (class_union_struct_name) + { + storage.insert (0, "::"); + storage.insert (0, class_union_struct_name); + } + parent_decl_ctx_die = parent_decl_ctx_die->GetParentDeclContextDIE(dwarf2Data, cu); + } + break; + + default: + parent_decl_ctx_die = NULL; + break; + } + } + + if (storage.empty()) + storage.append ("::"); + + storage.append (name); + } + if (storage.empty()) + return NULL; + return storage.c_str(); +} + + +//---------------------------------------------------------------------- +// LookupAddress +//---------------------------------------------------------------------- +bool +DWARFDebugInfoEntry::LookupAddress +( + const dw_addr_t address, + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die +) +{ + bool found_address = false; + if (m_tag) + { + bool check_children = false; + bool match_addr_range = false; + // printf("0x%8.8x: %30s: address = 0x%8.8x - ", m_offset, DW_TAG_value_to_name(tag), address); + switch (m_tag) + { + case DW_TAG_array_type : break; + case DW_TAG_class_type : check_children = true; break; + case DW_TAG_entry_point : break; + case DW_TAG_enumeration_type : break; + case DW_TAG_formal_parameter : break; + case DW_TAG_imported_declaration : break; + case DW_TAG_label : break; + case DW_TAG_lexical_block : check_children = true; match_addr_range = true; break; + case DW_TAG_member : break; + case DW_TAG_pointer_type : break; + case DW_TAG_reference_type : break; + case DW_TAG_compile_unit : match_addr_range = true; break; + case DW_TAG_string_type : break; + case DW_TAG_structure_type : check_children = true; break; + case DW_TAG_subroutine_type : break; + case DW_TAG_typedef : break; + case DW_TAG_union_type : break; + case DW_TAG_unspecified_parameters : break; + case DW_TAG_variant : break; + case DW_TAG_common_block : check_children = true; break; + case DW_TAG_common_inclusion : break; + case DW_TAG_inheritance : break; + case DW_TAG_inlined_subroutine : check_children = true; match_addr_range = true; break; + case DW_TAG_module : match_addr_range = true; break; + case DW_TAG_ptr_to_member_type : break; + case DW_TAG_set_type : break; + case DW_TAG_subrange_type : break; + case DW_TAG_with_stmt : break; + case DW_TAG_access_declaration : break; + case DW_TAG_base_type : break; + case DW_TAG_catch_block : match_addr_range = true; break; + case DW_TAG_const_type : break; + case DW_TAG_constant : break; + case DW_TAG_enumerator : break; + case DW_TAG_file_type : break; + case DW_TAG_friend : break; + case DW_TAG_namelist : break; + case DW_TAG_namelist_item : break; + case DW_TAG_packed_type : break; + case DW_TAG_subprogram : match_addr_range = true; break; + case DW_TAG_template_type_parameter : break; + case DW_TAG_template_value_parameter : break; + case DW_TAG_thrown_type : break; + case DW_TAG_try_block : match_addr_range = true; break; + case DW_TAG_variant_part : break; + case DW_TAG_variable : break; + case DW_TAG_volatile_type : break; + case DW_TAG_dwarf_procedure : break; + case DW_TAG_restrict_type : break; + case DW_TAG_interface_type : break; + case DW_TAG_namespace : check_children = true; break; + case DW_TAG_imported_module : break; + case DW_TAG_unspecified_type : break; + case DW_TAG_partial_unit : break; + case DW_TAG_imported_unit : break; + case DW_TAG_shared_type : break; + default: break; + } + + if (match_addr_range) + { + dw_addr_t lo_pc = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_low_pc, LLDB_INVALID_ADDRESS); + if (lo_pc != LLDB_INVALID_ADDRESS) + { + dw_addr_t hi_pc = GetAttributeHighPC(dwarf2Data, cu, lo_pc, LLDB_INVALID_ADDRESS); + if (hi_pc != LLDB_INVALID_ADDRESS) + { + // printf("\n0x%8.8x: %30s: address = 0x%8.8x [0x%8.8x - 0x%8.8x) ", m_offset, DW_TAG_value_to_name(tag), address, lo_pc, hi_pc); + if ((lo_pc <= address) && (address < hi_pc)) + { + found_address = true; + // puts("***MATCH***"); + switch (m_tag) + { + case DW_TAG_compile_unit: // File + check_children = ((function_die != NULL) || (block_die != NULL)); + break; + + case DW_TAG_subprogram: // Function + if (function_die) + *function_die = this; + check_children = (block_die != NULL); + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + if (block_die) + { + *block_die = this; + check_children = true; + } + break; + + default: + check_children = true; + break; + } + } + } + else + { // compile units may not have a valid high/low pc when there + // are address gaps in subroutines so we must always search + // if there is no valid high and low PC + check_children = (m_tag == DW_TAG_compile_unit) && ((function_die != NULL) || (block_die != NULL)); + } + } + else + { + dw_offset_t debug_ranges_offset = GetAttributeValueAsUnsigned(dwarf2Data, cu, DW_AT_ranges, DW_INVALID_OFFSET); + if (debug_ranges_offset != DW_INVALID_OFFSET) + { + DWARFDebugRanges::RangeList ranges; + DWARFDebugRanges* debug_ranges = dwarf2Data->DebugRanges(); + debug_ranges->FindRanges(debug_ranges_offset, ranges); + // All DW_AT_ranges are relative to the base address of the + // compile unit. We add the compile unit base address to make + // sure all the addresses are properly fixed up. + ranges.Slide (cu->GetBaseAddress()); + if (ranges.FindEntryThatContains(address)) + { + found_address = true; + // puts("***MATCH***"); + switch (m_tag) + { + case DW_TAG_compile_unit: // File + check_children = ((function_die != NULL) || (block_die != NULL)); + break; + + case DW_TAG_subprogram: // Function + if (function_die) + *function_die = this; + check_children = (block_die != NULL); + break; + + case DW_TAG_inlined_subroutine: // Inlined Function + case DW_TAG_lexical_block: // Block { } in code + if (block_die) + { + *block_die = this; + check_children = true; + } + break; + + default: + check_children = true; + break; + } + } + else + { + check_children = false; + } + } + } + } + + + if (check_children) + { + // printf("checking children\n"); + DWARFDebugInfoEntry* child = GetFirstChild(); + while (child) + { + if (child->LookupAddress(address, dwarf2Data, cu, function_die, block_die)) + return true; + child = child->GetSibling(); + } + } + } + return found_address; +} + +const DWARFAbbreviationDeclaration* +DWARFDebugInfoEntry::GetAbbreviationDeclarationPtr (SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit *cu, + lldb::offset_t &offset) const +{ + if (dwarf2Data) + { + offset = GetOffset(); + + const DWARFAbbreviationDeclaration* abbrev_decl = cu->GetAbbreviations()->GetAbbreviationDeclaration (m_abbr_idx); + if (abbrev_decl) + { + // Make sure the abbreviation code still matches. If it doesn't and + // the DWARF data was mmap'ed, the backing file might have been modified + // which is bad news. + const uint64_t abbrev_code = dwarf2Data->get_debug_info_data().GetULEB128 (&offset); + + if (abbrev_decl->Code() == abbrev_code) + return abbrev_decl; + + dwarf2Data->GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("0x%8.8x: the DWARF debug information has been modified (abbrev code was %u, and is now %u)", + GetOffset(), + (uint32_t)abbrev_decl->Code(), + (uint32_t)abbrev_code); + } + } + offset = DW_INVALID_OFFSET; + return NULL; +} + + +bool +DWARFDebugInfoEntry::OffsetLessThan (const DWARFDebugInfoEntry& a, const DWARFDebugInfoEntry& b) +{ + return a.GetOffset() < b.GetOffset(); +} + +void +DWARFDebugInfoEntry::DumpDIECollection (Stream &strm, DWARFDebugInfoEntry::collection &die_collection) +{ + DWARFDebugInfoEntry::const_iterator pos; + DWARFDebugInfoEntry::const_iterator end = die_collection.end(); + strm.PutCString("\noffset parent sibling child\n"); + strm.PutCString("-------- -------- -------- --------\n"); + for (pos = die_collection.begin(); pos != end; ++pos) + { + const DWARFDebugInfoEntry& die_ref = *pos; + const DWARFDebugInfoEntry* p = die_ref.GetParent(); + const DWARFDebugInfoEntry* s = die_ref.GetSibling(); + const DWARFDebugInfoEntry* c = die_ref.GetFirstChild(); + strm.Printf("%.8x: %.8x %.8x %.8x 0x%4.4x %s%s\n", + die_ref.GetOffset(), + p ? p->GetOffset() : 0, + s ? s->GetOffset() : 0, + c ? c->GetOffset() : 0, + die_ref.Tag(), + DW_TAG_value_to_name(die_ref.Tag()), + die_ref.HasChildren() ? " *" : ""); + } +} + + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h new file mode 100644 index 000000000000..85f4109ae01a --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h @@ -0,0 +1,457 @@ +//===-- DWARFDebugInfoEntry.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugInfoEntry_h_ +#define SymbolFileDWARF_DWARFDebugInfoEntry_h_ + +#include "SymbolFileDWARF.h" +#include "llvm/ADT/SmallVector.h" + +#include "DWARFDebugAbbrev.h" +#include "DWARFAbbreviationDeclaration.h" +#include "DWARFDebugRanges.h" +#include <vector> +#include <map> +#include <set> + +typedef std::map<const DWARFDebugInfoEntry*, dw_addr_t> DIEToAddressMap; +typedef DIEToAddressMap::iterator DIEToAddressMapIter; +typedef DIEToAddressMap::const_iterator DIEToAddressMapConstIter; + +typedef std::map<dw_addr_t, const DWARFDebugInfoEntry*> AddressToDIEMap; +typedef AddressToDIEMap::iterator AddressToDIEMapIter; +typedef AddressToDIEMap::const_iterator AddressToDIEMapConstIter; + + +typedef std::map<dw_offset_t, dw_offset_t> DIEToDIEMap; +typedef DIEToDIEMap::iterator DIEToDIEMapIter; +typedef DIEToDIEMap::const_iterator DIEToDIEMapConstIter; + +typedef std::map<uint32_t, const DWARFDebugInfoEntry*> UInt32ToDIEMap; +typedef UInt32ToDIEMap::iterator UInt32ToDIEMapIter; +typedef UInt32ToDIEMap::const_iterator UInt32ToDIEMapConstIter; + +typedef std::multimap<uint32_t, const DWARFDebugInfoEntry*> UInt32ToDIEMMap; +typedef UInt32ToDIEMMap::iterator UInt32ToDIEMMapIter; +typedef UInt32ToDIEMMap::const_iterator UInt32ToDIEMMapConstIter; + +class DWARFDeclContext; + +#define DIE_SIBLING_IDX_BITSIZE 31 +#define DIE_ABBR_IDX_BITSIZE 15 + +class DWARFDebugInfoEntry +{ +public: + typedef std::vector<DWARFDebugInfoEntry> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + typedef std::vector<dw_offset_t> offset_collection; + typedef offset_collection::iterator offset_collection_iterator; + typedef offset_collection::const_iterator offset_collection_const_iterator; + + class Attributes + { + public: + Attributes(); + ~Attributes(); + + void Append(const DWARFCompileUnit *cu, dw_offset_t attr_die_offset, dw_attr_t attr, dw_form_t form); + const DWARFCompileUnit * CompileUnitAtIndex(uint32_t i) const { return m_infos[i].cu; } + dw_offset_t DIEOffsetAtIndex(uint32_t i) const { return m_infos[i].die_offset; } + dw_attr_t AttributeAtIndex(uint32_t i) const { return m_infos[i].attr; } + dw_attr_t FormAtIndex(uint32_t i) const { return m_infos[i].form; } + bool ExtractFormValueAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, DWARFFormValue &form_value) const; + uint64_t FormValueAsUnsignedAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, uint64_t fail_value) const; + uint64_t FormValueAsUnsigned (SymbolFileDWARF* dwarf2Data, dw_attr_t attr, uint64_t fail_value) const; + uint32_t FindAttributeIndex(dw_attr_t attr) const; + bool ContainsAttribute(dw_attr_t attr) const; + bool RemoveAttribute(dw_attr_t attr); + void Clear() { m_infos.clear(); } + size_t Size() const { return m_infos.size(); } + + protected: + struct Info + { + const DWARFCompileUnit *cu; // Keep the compile unit with each attribute in case we have DW_FORM_ref_addr values + dw_offset_t die_offset; + dw_attr_t attr; + dw_form_t form; + }; + + typedef llvm::SmallVector<Info, 32> collection; + collection m_infos; + }; + + struct CompareState + { + CompareState() : + die_offset_pairs() + { + assert(sizeof(dw_offset_t)*2 == sizeof(uint64_t)); + } + + bool AddTypePair(dw_offset_t a, dw_offset_t b) + { + uint64_t a_b_offsets = (uint64_t)a << 32 | (uint64_t)b; + // Return true if this type was inserted, false otherwise + return die_offset_pairs.insert(a_b_offsets).second; + } + std::set< uint64_t > die_offset_pairs; + }; + + DWARFDebugInfoEntry(): + m_offset (DW_INVALID_OFFSET), + m_parent_idx (0), + m_sibling_idx (0), + m_empty_children(false), + m_abbr_idx (0), + m_has_children (false), + m_tag (0) + { + } + + void Clear () + { + m_offset = DW_INVALID_OFFSET; + m_parent_idx = 0; + m_sibling_idx = 0; + m_empty_children = false; + m_abbr_idx = 0; + m_has_children = false; + m_tag = 0; + } + + bool Contains (const DWARFDebugInfoEntry *die) const; + + void BuildAddressRangeTable( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges) const; + + void BuildFunctionAddressRangeTable( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugAranges* debug_aranges) const; + + bool FastExtract( + const lldb_private::DataExtractor& debug_info_data, + const DWARFCompileUnit* cu, + const uint8_t *fixed_form_sizes, + lldb::offset_t* offset_ptr); + + bool Extract( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + lldb::offset_t* offset_ptr); + + bool LookupAddress( + const dw_addr_t address, + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + DWARFDebugInfoEntry** function_die, + DWARFDebugInfoEntry** block_die); + + size_t GetAttributes( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const uint8_t *fixed_form_sizes, + DWARFDebugInfoEntry::Attributes& attrs, + uint32_t curr_depth = 0) const; // "curr_depth" for internal use only, don't set this yourself!!! + + dw_offset_t GetAttributeValue( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + DWARFFormValue& formValue, + dw_offset_t* end_attr_offset_ptr = NULL) const; + + const char* GetAttributeValueAsString( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + const char* fail_value) const; + + uint64_t GetAttributeValueAsUnsigned( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value) const; + + uint64_t GetAttributeValueAsReference( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + uint64_t fail_value) const; + + int64_t GetAttributeValueAsSigned( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + int64_t fail_value) const; + + dw_addr_t GetAttributeHighPC( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + dw_addr_t lo_pc, + uint64_t fail_value) const; + + bool GetAttributeAddressRange( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + dw_addr_t& lo_pc, + dw_addr_t& hi_pc, + uint64_t fail_value) const; + + dw_offset_t GetAttributeValueAsLocation( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_attr_t attr, + lldb_private::DataExtractor& data, + uint32_t &block_size) const; + + const char* GetName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu) const; + + const char* GetMangledName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + bool substitute_name_allowed = true) const; + + const char* GetPubname( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu) const; + + static bool GetName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_offset_t die_offset, + lldb_private::Stream &s); + + static bool AppendTypeName( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const dw_offset_t die_offset, + lldb_private::Stream &s); + + const char * GetQualifiedName ( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + std::string &storage) const; + + const char * GetQualifiedName ( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + const DWARFDebugInfoEntry::Attributes& attributes, + std::string &storage) const; + +// static int Compare( +// SymbolFileDWARF* dwarf2Data, +// dw_offset_t a_die_offset, +// dw_offset_t b_die_offset, +// CompareState &compare_state, +// bool compare_siblings, +// bool compare_children); +// +// static int Compare( +// SymbolFileDWARF* dwarf2Data, +// DWARFCompileUnit* a_cu, const DWARFDebugInfoEntry* a_die, +// DWARFCompileUnit* b_cu, const DWARFDebugInfoEntry* b_die, +// CompareState &compare_state, +// bool compare_siblings, +// bool compare_children); + + static bool OffsetLessThan ( + const DWARFDebugInfoEntry& a, + const DWARFDebugInfoEntry& b); + + void Dump( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + lldb_private::Stream &s, + uint32_t recurse_depth) const; + + void DumpAncestry( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const DWARFDebugInfoEntry* oldest, + lldb_private::Stream &s, + uint32_t recurse_depth) const; + + static void DumpAttribute( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const lldb_private::DataExtractor& debug_info_data, + lldb::offset_t *offset_ptr, + lldb_private::Stream &s, + dw_attr_t attr, + dw_form_t form); + // This one dumps the comp unit name, objfile name and die offset for this die so the stream S. + void DumpLocation( + SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + lldb_private::Stream &s) const; + + bool GetDIENamesAndRanges( + SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit* cu, + const char * &name, + const char * &mangled, + DWARFDebugRanges::RangeList& rangeList, + int& decl_file, + int& decl_line, + int& decl_column, + int& call_file, + int& call_line, + int& call_column, + lldb_private::DWARFExpression *frame_base = NULL) const; + + const DWARFAbbreviationDeclaration* + GetAbbreviationDeclarationPtr (SymbolFileDWARF* dwarf2Data, + const DWARFCompileUnit *cu, + lldb::offset_t &offset) const; + + dw_tag_t + Tag () const + { + return m_tag; + } + + bool + IsNULL() const + { + return m_abbr_idx == 0; + } + + dw_offset_t + GetOffset () const + { + return m_offset; + } + + void + SetOffset (dw_offset_t offset) + { + m_offset = offset; + } + + bool + HasChildren () const + { + return m_has_children; + } + + void + SetHasChildren (bool b) + { + m_has_children = b; + } + + // We know we are kept in a vector of contiguous entries, so we know + // our parent will be some index behind "this". + DWARFDebugInfoEntry* GetParent() { return m_parent_idx > 0 ? this - m_parent_idx : NULL; } + const DWARFDebugInfoEntry* GetParent() const { return m_parent_idx > 0 ? this - m_parent_idx : NULL; } + // We know we are kept in a vector of contiguous entries, so we know + // our sibling will be some index after "this". + DWARFDebugInfoEntry* GetSibling() { return m_sibling_idx > 0 ? this + m_sibling_idx : NULL; } + const DWARFDebugInfoEntry* GetSibling() const { return m_sibling_idx > 0 ? this + m_sibling_idx : NULL; } + // We know we are kept in a vector of contiguous entries, so we know + // we don't need to store our child pointer, if we have a child it will + // be the next entry in the list... + DWARFDebugInfoEntry* GetFirstChild() { return (HasChildren() && !m_empty_children) ? this + 1 : NULL; } + const DWARFDebugInfoEntry* GetFirstChild() const { return (HasChildren() && !m_empty_children) ? this + 1 : NULL; } + + + void GetDeclContextDIEs (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + DWARFDIECollection &decl_context_dies) const; + + void GetDWARFDeclContext (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + DWARFDeclContext &dwarf_decl_ctx) const; + + + bool MatchesDWARFDeclContext(SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + const DWARFDeclContext &dwarf_decl_ctx) const; + + const DWARFDebugInfoEntry* GetParentDeclContextDIE (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu) const; + const DWARFDebugInfoEntry* GetParentDeclContextDIE (SymbolFileDWARF* dwarf2Data, + DWARFCompileUnit* cu, + const DWARFDebugInfoEntry::Attributes& attributes) const; + + void + SetParent (DWARFDebugInfoEntry* parent) + { + if (parent) + { + // We know we are kept in a vector of contiguous entries, so we know + // our parent will be some index behind "this". + m_parent_idx = this - parent; + } + else + m_parent_idx = 0; + } + void + SetSibling (DWARFDebugInfoEntry* sibling) + { + if (sibling) + { + // We know we are kept in a vector of contiguous entries, so we know + // our sibling will be some index after "this". + m_sibling_idx = sibling - this; + sibling->SetParent(GetParent()); + } + else + m_sibling_idx = 0; + } + + void + SetSiblingIndex (uint32_t idx) + { + m_sibling_idx = idx; + } + + void + SetParentIndex (uint32_t idx) + { + m_parent_idx = idx; + } + + bool + GetEmptyChildren () const + { + return m_empty_children; + } + + void + SetEmptyChildren (bool b) + { + m_empty_children = b; + } + + static void + DumpDIECollection (lldb_private::Stream &strm, + DWARFDebugInfoEntry::collection &die_collection); + +protected: + dw_offset_t m_offset; // Offset within the .debug_info of the start of this entry + uint32_t m_parent_idx; // How many to subtract from "this" to get the parent. If zero this die has no parent + uint32_t m_sibling_idx:31, // How many to add to "this" to get the sibling. + m_empty_children:1; // If a DIE says it had children, yet it just contained a NULL tag, this will be set. + uint32_t m_abbr_idx:DIE_ABBR_IDX_BITSIZE, + m_has_children:1, // Set to 1 if this DIE has children + m_tag:16; // A copy of the DW_TAG value so we don't have to go through the compile unit abbrev table + +}; + +#endif // SymbolFileDWARF_DWARFDebugInfoEntry_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp new file mode 100644 index 000000000000..6c9336a08426 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp @@ -0,0 +1,1436 @@ +//===-- DWARFDebugLine.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugLine.h" + +//#define ENABLE_DEBUG_PRINTF // DO NOT LEAVE THIS DEFINED: DEBUG ONLY!!! +#include <assert.h> + +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" + +#include "SymbolFileDWARF.h" +#include "LogChannelDWARF.h" + +using namespace lldb; +using namespace lldb_private; +using namespace std; + +//---------------------------------------------------------------------- +// Parse +// +// Parse all information in the debug_line_data into an internal +// representation. +//---------------------------------------------------------------------- +void +DWARFDebugLine::Parse(const DataExtractor& debug_line_data) +{ + m_lineTableMap.clear(); + lldb::offset_t offset = 0; + LineTable::shared_ptr line_table_sp(new LineTable); + while (debug_line_data.ValidOffset(offset)) + { + const lldb::offset_t debug_line_offset = offset; + + if (line_table_sp.get() == NULL) + break; + + if (ParseStatementTable(debug_line_data, &offset, line_table_sp.get())) + { + // Make sure we don't don't loop infinitely + if (offset <= debug_line_offset) + break; + //DEBUG_PRINTF("m_lineTableMap[0x%8.8x] = line_table_sp\n", debug_line_offset); + m_lineTableMap[debug_line_offset] = line_table_sp; + line_table_sp.reset(new LineTable); + } + else + ++offset; // Try next byte in line table + } +} + +void +DWARFDebugLine::ParseIfNeeded(const DataExtractor& debug_line_data) +{ + if (m_lineTableMap.empty()) + Parse(debug_line_data); +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::GetLineTable +//---------------------------------------------------------------------- +DWARFDebugLine::LineTable::shared_ptr +DWARFDebugLine::GetLineTable(const dw_offset_t offset) const +{ + DWARFDebugLine::LineTable::shared_ptr line_table_shared_ptr; + LineTableConstIter pos = m_lineTableMap.find(offset); + if (pos != m_lineTableMap.end()) + line_table_shared_ptr = pos->second; + return line_table_shared_ptr; +} + + +//---------------------------------------------------------------------- +// DumpStateToFile +//---------------------------------------------------------------------- +static void +DumpStateToFile (dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + Log *log = (Log *)userData; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // If the row is zero we are being called with the prologue only + state.prologue->Dump (log); + log->PutCString ("Address Line Column File"); + log->PutCString ("------------------ ------ ------ ------"); + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table + } + else + { + log->Printf( "0x%16.16" PRIx64 " %6u %6u %6u%s\n", state.address, state.line, state.column, state.file, state.end_sequence ? " END" : ""); + } +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::DumpLineTableRows +//---------------------------------------------------------------------- +bool +DWARFDebugLine::DumpLineTableRows(Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t debug_line_offset) +{ + const DataExtractor& debug_line_data = dwarf2Data->get_debug_line_data(); + + if (debug_line_offset == DW_INVALID_OFFSET) + { + // Dump line table to a single file only + debug_line_offset = 0; + while (debug_line_data.ValidOffset(debug_line_offset)) + debug_line_offset = DumpStatementTable (log, debug_line_data, debug_line_offset); + } + else + { + // Dump line table to a single file only + DumpStatementTable (log, debug_line_data, debug_line_offset); + } + return false; +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::DumpStatementTable +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugLine::DumpStatementTable(Log *log, const DataExtractor& debug_line_data, const dw_offset_t debug_line_offset) +{ + if (debug_line_data.ValidOffset(debug_line_offset)) + { + lldb::offset_t offset = debug_line_offset; + log->Printf( "----------------------------------------------------------------------\n" + "debug_line[0x%8.8x]\n" + "----------------------------------------------------------------------\n", debug_line_offset); + + if (ParseStatementTable(debug_line_data, &offset, DumpStateToFile, log)) + return offset; + else + return debug_line_offset + 1; // Skip to next byte in .debug_line section + } + + return DW_INVALID_OFFSET; +} + + +//---------------------------------------------------------------------- +// DumpOpcodes +//---------------------------------------------------------------------- +bool +DWARFDebugLine::DumpOpcodes(Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t debug_line_offset, uint32_t dump_flags) +{ + const DataExtractor& debug_line_data = dwarf2Data->get_debug_line_data(); + + if (debug_line_data.GetByteSize() == 0) + { + log->Printf( "< EMPTY >\n"); + return false; + } + + if (debug_line_offset == DW_INVALID_OFFSET) + { + // Dump line table to a single file only + debug_line_offset = 0; + while (debug_line_data.ValidOffset(debug_line_offset)) + debug_line_offset = DumpStatementOpcodes (log, debug_line_data, debug_line_offset, dump_flags); + } + else + { + // Dump line table to a single file only + DumpStatementOpcodes (log, debug_line_data, debug_line_offset, dump_flags); + } + return false; +} + +//---------------------------------------------------------------------- +// DumpStatementOpcodes +//---------------------------------------------------------------------- +dw_offset_t +DWARFDebugLine::DumpStatementOpcodes(Log *log, const DataExtractor& debug_line_data, const dw_offset_t debug_line_offset, uint32_t flags) +{ + lldb::offset_t offset = debug_line_offset; + if (debug_line_data.ValidOffset(offset)) + { + Prologue prologue; + + if (ParsePrologue(debug_line_data, &offset, &prologue)) + { + log->PutCString ("----------------------------------------------------------------------"); + log->Printf ("debug_line[0x%8.8x]", debug_line_offset); + log->PutCString ("----------------------------------------------------------------------\n"); + prologue.Dump (log); + } + else + { + offset = debug_line_offset; + log->Printf( "0x%8.8" PRIx64 ": skipping pad byte %2.2x", offset, debug_line_data.GetU8(&offset)); + return offset; + } + + Row row(prologue.default_is_stmt); + const dw_offset_t end_offset = debug_line_offset + prologue.total_length + sizeof(prologue.total_length); + + assert(debug_line_data.ValidOffset(end_offset-1)); + + while (offset < end_offset) + { + const uint32_t op_offset = offset; + uint8_t opcode = debug_line_data.GetU8(&offset); + switch (opcode) + { + case 0: // Extended Opcodes always start with a zero opcode followed by + { // a uleb128 length so you can skip ones you don't know about + + dw_offset_t ext_offset = offset; + dw_uleb128_t len = debug_line_data.GetULEB128(&offset); + dw_offset_t arg_size = len - (offset - ext_offset); + uint8_t sub_opcode = debug_line_data.GetU8(&offset); +// if (verbose) +// log->Printf( "Extended: <%u> %2.2x ", len, sub_opcode); + + switch (sub_opcode) + { + case DW_LNE_end_sequence : + log->Printf( "0x%8.8x: DW_LNE_end_sequence", op_offset); + row.Dump(log); + row.Reset(prologue.default_is_stmt); + break; + + case DW_LNE_set_address : + { + row.address = debug_line_data.GetMaxU64(&offset, arg_size); + log->Printf( "0x%8.8x: DW_LNE_set_address (0x%" PRIx64 ")", op_offset, row.address); + } + break; + + case DW_LNE_define_file: + { + FileNameEntry fileEntry; + fileEntry.name = debug_line_data.GetCStr(&offset); + fileEntry.dir_idx = debug_line_data.GetULEB128(&offset); + fileEntry.mod_time = debug_line_data.GetULEB128(&offset); + fileEntry.length = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNE_define_file('%s', dir=%i, mod_time=0x%8.8x, length=%i )", + op_offset, + fileEntry.name.c_str(), + fileEntry.dir_idx, + fileEntry.mod_time, + fileEntry.length); + prologue.file_names.push_back(fileEntry); + } + break; + + default: + log->Printf( "0x%8.8x: DW_LNE_??? (%2.2x) - Skipping unknown upcode", op_offset, opcode); + // Length doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that below + offset += arg_size; + break; + } + } + break; + + // Standard Opcodes + case DW_LNS_copy: + log->Printf( "0x%8.8x: DW_LNS_copy", op_offset); + row.Dump (log); + break; + + case DW_LNS_advance_pc: + { + dw_uleb128_t addr_offset_n = debug_line_data.GetULEB128(&offset); + dw_uleb128_t addr_offset = addr_offset_n * prologue.min_inst_length; + log->Printf( "0x%8.8x: DW_LNS_advance_pc (0x%x)", op_offset, addr_offset); + row.address += addr_offset; + } + break; + + case DW_LNS_advance_line: + { + dw_sleb128_t line_offset = debug_line_data.GetSLEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_advance_line (%i)", op_offset, line_offset); + row.line += line_offset; + } + break; + + case DW_LNS_set_file: + row.file = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_file (%u)", op_offset, row.file); + break; + + case DW_LNS_set_column: + row.column = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_column (%u)", op_offset, row.column); + break; + + case DW_LNS_negate_stmt: + row.is_stmt = !row.is_stmt; + log->Printf( "0x%8.8x: DW_LNS_negate_stmt", op_offset); + break; + + case DW_LNS_set_basic_block: + row.basic_block = true; + log->Printf( "0x%8.8x: DW_LNS_set_basic_block", op_offset); + break; + + case DW_LNS_const_add_pc: + { + uint8_t adjust_opcode = 255 - prologue.opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue.line_range) * prologue.min_inst_length; + log->Printf( "0x%8.8x: DW_LNS_const_add_pc (0x%8.8" PRIx64 ")", op_offset, addr_offset); + row.address += addr_offset; + } + break; + + case DW_LNS_fixed_advance_pc: + { + uint16_t pc_offset = debug_line_data.GetU16(&offset); + log->Printf( "0x%8.8x: DW_LNS_fixed_advance_pc (0x%4.4x)", op_offset, pc_offset); + row.address += pc_offset; + } + break; + + case DW_LNS_set_prologue_end: + row.prologue_end = true; + log->Printf( "0x%8.8x: DW_LNS_set_prologue_end", op_offset); + break; + + case DW_LNS_set_epilogue_begin: + row.epilogue_begin = true; + log->Printf( "0x%8.8x: DW_LNS_set_epilogue_begin", op_offset); + break; + + case DW_LNS_set_isa: + row.isa = debug_line_data.GetULEB128(&offset); + log->Printf( "0x%8.8x: DW_LNS_set_isa (%u)", op_offset, row.isa); + break; + + // Special Opcodes + default: + if (opcode < prologue.opcode_base) + { + // We have an opcode that this parser doesn't know about, skip + // the number of ULEB128 numbers that is says to skip in the + // prologue's standard_opcode_lengths array + uint8_t n = prologue.standard_opcode_lengths[opcode-1]; + log->Printf( "0x%8.8x: Special : Unknown skipping %u ULEB128 values.", op_offset, n); + while (n > 0) + { + debug_line_data.GetULEB128(&offset); + --n; + } + } + else + { + uint8_t adjust_opcode = opcode - prologue.opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue.line_range) * prologue.min_inst_length; + int32_t line_offset = prologue.line_base + (adjust_opcode % prologue.line_range); + log->Printf("0x%8.8x: address += 0x%" PRIx64 ", line += %i\n", op_offset, (uint64_t)addr_offset, line_offset); + row.address += addr_offset; + row.line += line_offset; + row.Dump (log); + } + break; + } + } + return end_offset; + } + return DW_INVALID_OFFSET; +} + + + + +//---------------------------------------------------------------------- +// Parse +// +// Parse the entire line table contents calling callback each time a +// new prologue is parsed and every time a new row is to be added to +// the line table. +//---------------------------------------------------------------------- +void +DWARFDebugLine::Parse(const DataExtractor& debug_line_data, DWARFDebugLine::State::Callback callback, void* userData) +{ + lldb::offset_t offset = 0; + if (debug_line_data.ValidOffset(offset)) + { + if (!ParseStatementTable(debug_line_data, &offset, callback, userData)) + ++offset; // Skip to next byte in .debug_line section + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::ParsePrologue +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParsePrologue(const DataExtractor& debug_line_data, lldb::offset_t* offset_ptr, Prologue* prologue) +{ + const lldb::offset_t prologue_offset = *offset_ptr; + + //DEBUG_PRINTF("0x%8.8x: ParsePrologue()\n", *offset_ptr); + + prologue->Clear(); + uint32_t i; + const char * s; + prologue->total_length = debug_line_data.GetU32(offset_ptr); + prologue->version = debug_line_data.GetU16(offset_ptr); + if (prologue->version != 2) + return false; + + prologue->prologue_length = debug_line_data.GetU32(offset_ptr); + const lldb::offset_t end_prologue_offset = prologue->prologue_length + *offset_ptr; + prologue->min_inst_length = debug_line_data.GetU8(offset_ptr); + prologue->default_is_stmt = debug_line_data.GetU8(offset_ptr); + prologue->line_base = debug_line_data.GetU8(offset_ptr); + prologue->line_range = debug_line_data.GetU8(offset_ptr); + prologue->opcode_base = debug_line_data.GetU8(offset_ptr); + + prologue->standard_opcode_lengths.reserve(prologue->opcode_base-1); + + for (i=1; i<prologue->opcode_base; ++i) + { + uint8_t op_len = debug_line_data.GetU8(offset_ptr); + prologue->standard_opcode_lengths.push_back(op_len); + } + + while (*offset_ptr < end_prologue_offset) + { + s = debug_line_data.GetCStr(offset_ptr); + if (s && s[0]) + prologue->include_directories.push_back(s); + else + break; + } + + while (*offset_ptr < end_prologue_offset) + { + const char* name = debug_line_data.GetCStr( offset_ptr ); + if (name && name[0]) + { + FileNameEntry fileEntry; + fileEntry.name = name; + fileEntry.dir_idx = debug_line_data.GetULEB128( offset_ptr ); + fileEntry.mod_time = debug_line_data.GetULEB128( offset_ptr ); + fileEntry.length = debug_line_data.GetULEB128( offset_ptr ); + prologue->file_names.push_back(fileEntry); + } + else + break; + } + + if (*offset_ptr != end_prologue_offset) + { + Host::SystemLog (Host::eSystemLogWarning, + "warning: parsing line table prologue at 0x%8.8" PRIx64 " should have ended at 0x%8.8" PRIx64 " but it ended ad 0x%8.8" PRIx64 "\n", + prologue_offset, + end_prologue_offset, + *offset_ptr); + } + return end_prologue_offset; +} + +bool +DWARFDebugLine::ParseSupportFiles (const lldb::ModuleSP &module_sp, + const DataExtractor& debug_line_data, + const char *cu_comp_dir, + dw_offset_t stmt_list, + FileSpecList &support_files) +{ + lldb::offset_t offset = stmt_list + 4; // Skip the total length + const char * s; + uint32_t version = debug_line_data.GetU16(&offset); + if (version != 2) + return false; + + const dw_offset_t end_prologue_offset = debug_line_data.GetU32(&offset) + offset; + // Skip instruction length, default is stmt, line base, line range and + // opcode base, and all opcode lengths + offset += 4; + const uint8_t opcode_base = debug_line_data.GetU8(&offset); + offset += opcode_base - 1; + std::vector<std::string> include_directories; + include_directories.push_back(""); // Directory at index zero doesn't exist + while (offset < end_prologue_offset) + { + s = debug_line_data.GetCStr(&offset); + if (s && s[0]) + include_directories.push_back(s); + else + break; + } + std::string fullpath; + std::string remapped_fullpath; + while (offset < end_prologue_offset) + { + const char* path = debug_line_data.GetCStr( &offset ); + if (path && path[0]) + { + uint32_t dir_idx = debug_line_data.GetULEB128( &offset ); + debug_line_data.Skip_LEB128(&offset); // Skip mod_time + debug_line_data.Skip_LEB128(&offset); // Skip length + + if (path[0] == '/') + { + // The path starts with a directory delimiter, so we are done. + if (module_sp->RemapSourceFile (path, fullpath)) + support_files.Append(FileSpec (fullpath.c_str(), false)); + else + support_files.Append(FileSpec (path, false)); + } + else + { + if (dir_idx > 0 && dir_idx < include_directories.size()) + { + if (cu_comp_dir && include_directories[dir_idx][0] != '/') + { + fullpath = cu_comp_dir; + + if (*fullpath.rbegin() != '/') + fullpath += '/'; + fullpath += include_directories[dir_idx]; + + } + else + fullpath = include_directories[dir_idx]; + } + else if (cu_comp_dir && cu_comp_dir[0]) + { + fullpath = cu_comp_dir; + } + + if (!fullpath.empty()) + { + if (*fullpath.rbegin() != '/') + fullpath += '/'; + } + fullpath += path; + if (module_sp->RemapSourceFile (fullpath.c_str(), remapped_fullpath)) + support_files.Append(FileSpec (remapped_fullpath.c_str(), false)); + else + support_files.Append(FileSpec (fullpath.c_str(), false)); + } + + } + } + + if (offset != end_prologue_offset) + { + Host::SystemLog (Host::eSystemLogError, + "warning: parsing line table prologue at 0x%8.8x should have ended at 0x%8.8x but it ended ad 0x%8.8" PRIx64 "\n", + stmt_list, + end_prologue_offset, + offset); + } + return end_prologue_offset; +} + +//---------------------------------------------------------------------- +// ParseStatementTable +// +// Parse a single line table (prologue and all rows) and call the +// callback function once for the prologue (row in state will be zero) +// and each time a row is to be added to the line table. +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParseStatementTable +( + const DataExtractor& debug_line_data, + lldb::offset_t* offset_ptr, + DWARFDebugLine::State::Callback callback, + void* userData +) +{ + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_LINE)); + Prologue::shared_ptr prologue(new Prologue()); + + + const dw_offset_t debug_line_offset = *offset_ptr; + + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugLine::ParseStatementTable (.debug_line[0x%8.8x])", + debug_line_offset); + + if (!ParsePrologue(debug_line_data, offset_ptr, prologue.get())) + { + if (log) + log->Error ("failed to parse DWARF line table prologue"); + // Restore our offset and return false to indicate failure! + *offset_ptr = debug_line_offset; + return false; + } + + if (log) + prologue->Dump (log); + + const dw_offset_t end_offset = debug_line_offset + prologue->total_length + sizeof(prologue->total_length); + + State state(prologue, log, callback, userData); + + while (*offset_ptr < end_offset) + { + //DEBUG_PRINTF("0x%8.8x: ", *offset_ptr); + uint8_t opcode = debug_line_data.GetU8(offset_ptr); + + if (opcode == 0) + { + // Extended Opcodes always start with a zero opcode followed by + // a uleb128 length so you can skip ones you don't know about + lldb::offset_t ext_offset = *offset_ptr; + dw_uleb128_t len = debug_line_data.GetULEB128(offset_ptr); + dw_offset_t arg_size = len - (*offset_ptr - ext_offset); + + //DEBUG_PRINTF("Extended: <%2u> ", len); + uint8_t sub_opcode = debug_line_data.GetU8(offset_ptr); + switch (sub_opcode) + { + case DW_LNE_end_sequence: + // Set the end_sequence register of the state machine to true and + // append a row to the matrix using the current values of the + // state-machine registers. Then reset the registers to the initial + // values specified above. Every statement program sequence must end + // with a DW_LNE_end_sequence instruction which creates a row whose + // address is that of the byte after the last target machine instruction + // of the sequence. + state.end_sequence = true; + state.AppendRowToMatrix(*offset_ptr); + state.Reset(); + break; + + case DW_LNE_set_address: + // Takes a single relocatable address as an operand. The size of the + // operand is the size appropriate to hold an address on the target + // machine. Set the address register to the value given by the + // relocatable address. All of the other statement program opcodes + // that affect the address register add a delta to it. This instruction + // stores a relocatable value into it instead. + state.address = debug_line_data.GetAddress(offset_ptr); + break; + + case DW_LNE_define_file: + // Takes 4 arguments. The first is a null terminated string containing + // a source file name. The second is an unsigned LEB128 number representing + // the directory index of the directory in which the file was found. The + // third is an unsigned LEB128 number representing the time of last + // modification of the file. The fourth is an unsigned LEB128 number + // representing the length in bytes of the file. The time and length + // fields may contain LEB128(0) if the information is not available. + // + // The directory index represents an entry in the include_directories + // section of the statement program prologue. The index is LEB128(0) + // if the file was found in the current directory of the compilation, + // LEB128(1) if it was found in the first directory in the + // include_directories section, and so on. The directory index is + // ignored for file names that represent full path names. + // + // The files are numbered, starting at 1, in the order in which they + // appear; the names in the prologue come before names defined by + // the DW_LNE_define_file instruction. These numbers are used in the + // the file register of the state machine. + { + FileNameEntry fileEntry; + fileEntry.name = debug_line_data.GetCStr(offset_ptr); + fileEntry.dir_idx = debug_line_data.GetULEB128(offset_ptr); + fileEntry.mod_time = debug_line_data.GetULEB128(offset_ptr); + fileEntry.length = debug_line_data.GetULEB128(offset_ptr); + state.prologue->file_names.push_back(fileEntry); + } + break; + + default: + // Length doesn't include the zero opcode byte or the length itself, but + // it does include the sub_opcode, so we have to adjust for that below + (*offset_ptr) += arg_size; + break; + } + } + else if (opcode < prologue->opcode_base) + { + switch (opcode) + { + // Standard Opcodes + case DW_LNS_copy: + // Takes no arguments. Append a row to the matrix using the + // current values of the state-machine registers. Then set + // the basic_block register to false. + state.AppendRowToMatrix(*offset_ptr); + break; + + case DW_LNS_advance_pc: + // Takes a single unsigned LEB128 operand, multiplies it by the + // min_inst_length field of the prologue, and adds the + // result to the address register of the state machine. + state.address += debug_line_data.GetULEB128(offset_ptr) * prologue->min_inst_length; + break; + + case DW_LNS_advance_line: + // Takes a single signed LEB128 operand and adds that value to + // the line register of the state machine. + state.line += debug_line_data.GetSLEB128(offset_ptr); + break; + + case DW_LNS_set_file: + // Takes a single unsigned LEB128 operand and stores it in the file + // register of the state machine. + state.file = debug_line_data.GetULEB128(offset_ptr); + break; + + case DW_LNS_set_column: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + state.column = debug_line_data.GetULEB128(offset_ptr); + break; + + case DW_LNS_negate_stmt: + // Takes no arguments. Set the is_stmt register of the state + // machine to the logical negation of its current value. + state.is_stmt = !state.is_stmt; + break; + + case DW_LNS_set_basic_block: + // Takes no arguments. Set the basic_block register of the + // state machine to true + state.basic_block = true; + break; + + case DW_LNS_const_add_pc: + // Takes no arguments. Add to the address register of the state + // machine the address increment value corresponding to special + // opcode 255. The motivation for DW_LNS_const_add_pc is this: + // when the statement program needs to advance the address by a + // small amount, it can use a single special opcode, which occupies + // a single byte. When it needs to advance the address by up to + // twice the range of the last special opcode, it can use + // DW_LNS_const_add_pc followed by a special opcode, for a total + // of two bytes. Only if it needs to advance the address by more + // than twice that range will it need to use both DW_LNS_advance_pc + // and a special opcode, requiring three or more bytes. + { + uint8_t adjust_opcode = 255 - prologue->opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) * prologue->min_inst_length; + state.address += addr_offset; + } + break; + + case DW_LNS_fixed_advance_pc: + // Takes a single uhalf operand. Add to the address register of + // the state machine the value of the (unencoded) operand. This + // is the only extended opcode that takes an argument that is not + // a variable length number. The motivation for DW_LNS_fixed_advance_pc + // is this: existing assemblers cannot emit DW_LNS_advance_pc or + // special opcodes because they cannot encode LEB128 numbers or + // judge when the computation of a special opcode overflows and + // requires the use of DW_LNS_advance_pc. Such assemblers, however, + // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. + state.address += debug_line_data.GetU16(offset_ptr); + break; + + case DW_LNS_set_prologue_end: + // Takes no arguments. Set the prologue_end register of the + // state machine to true + state.prologue_end = true; + break; + + case DW_LNS_set_epilogue_begin: + // Takes no arguments. Set the basic_block register of the + // state machine to true + state.epilogue_begin = true; + break; + + case DW_LNS_set_isa: + // Takes a single unsigned LEB128 operand and stores it in the + // column register of the state machine. + state.isa = debug_line_data.GetULEB128(offset_ptr); + break; + + default: + // Handle any unknown standard opcodes here. We know the lengths + // of such opcodes because they are specified in the prologue + // as a multiple of LEB128 operands for each opcode. + { + uint8_t i; + assert (opcode - 1 < prologue->standard_opcode_lengths.size()); + const uint8_t opcode_length = prologue->standard_opcode_lengths[opcode - 1]; + for (i=0; i<opcode_length; ++i) + debug_line_data.Skip_LEB128(offset_ptr); + } + break; + } + } + else + { + // Special Opcodes + + // A special opcode value is chosen based on the amount that needs + // to be added to the line and address registers. The maximum line + // increment for a special opcode is the value of the line_base + // field in the header, plus the value of the line_range field, + // minus 1 (line base + line range - 1). If the desired line + // increment is greater than the maximum line increment, a standard + // opcode must be used instead of a special opcode. The "address + // advance" is calculated by dividing the desired address increment + // by the minimum_instruction_length field from the header. The + // special opcode is then calculated using the following formula: + // + // opcode = (desired line increment - line_base) + (line_range * address advance) + opcode_base + // + // If the resulting opcode is greater than 255, a standard opcode + // must be used instead. + // + // To decode a special opcode, subtract the opcode_base from the + // opcode itself to give the adjusted opcode. The amount to + // increment the address register is the result of the adjusted + // opcode divided by the line_range multiplied by the + // minimum_instruction_length field from the header. That is: + // + // address increment = (adjusted opcode / line_range) * minimum_instruction_length + // + // The amount to increment the line register is the line_base plus + // the result of the adjusted opcode modulo the line_range. That is: + // + // line increment = line_base + (adjusted opcode % line_range) + + uint8_t adjust_opcode = opcode - prologue->opcode_base; + dw_addr_t addr_offset = (adjust_opcode / prologue->line_range) * prologue->min_inst_length; + int32_t line_offset = prologue->line_base + (adjust_opcode % prologue->line_range); + state.line += line_offset; + state.address += addr_offset; + state.AppendRowToMatrix(*offset_ptr); + } + } + + state.Finalize( *offset_ptr ); + + return end_offset; +} + + +//---------------------------------------------------------------------- +// ParseStatementTableCallback +//---------------------------------------------------------------------- +static void +ParseStatementTableCallback(dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + DWARFDebugLine::LineTable* line_table = (DWARFDebugLine::LineTable*)userData; + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // Just started parsing the line table, so lets keep a reference to + // the prologue using the supplied shared pointer + line_table->prologue = state.prologue; + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table, nothing to do for the cleanup + } + else + { + // We have a new row, lets append it + line_table->AppendRow(state); + } +} + +//---------------------------------------------------------------------- +// ParseStatementTable +// +// Parse a line table at offset and populate the LineTable class with +// the prologue and all rows. +//---------------------------------------------------------------------- +bool +DWARFDebugLine::ParseStatementTable(const DataExtractor& debug_line_data, lldb::offset_t *offset_ptr, LineTable* line_table) +{ + return ParseStatementTable(debug_line_data, offset_ptr, ParseStatementTableCallback, line_table); +} + + +inline bool +DWARFDebugLine::Prologue::IsValid() const +{ + return SymbolFileDWARF::SupportedVersion(version); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::Prologue::Dump +//---------------------------------------------------------------------- +void +DWARFDebugLine::Prologue::Dump(Log *log) +{ + uint32_t i; + + log->Printf( "Line table prologue:"); + log->Printf( " total_length: 0x%8.8x", total_length); + log->Printf( " version: %u", version); + log->Printf( "prologue_length: 0x%8.8x", prologue_length); + log->Printf( "min_inst_length: %u", min_inst_length); + log->Printf( "default_is_stmt: %u", default_is_stmt); + log->Printf( " line_base: %i", line_base); + log->Printf( " line_range: %u", line_range); + log->Printf( " opcode_base: %u", opcode_base); + + for (i=0; i<standard_opcode_lengths.size(); ++i) + { + log->Printf( "standard_opcode_lengths[%s] = %u", DW_LNS_value_to_name(i+1), standard_opcode_lengths[i]); + } + + if (!include_directories.empty()) + { + for (i=0; i<include_directories.size(); ++i) + { + log->Printf( "include_directories[%3u] = '%s'", i+1, include_directories[i].c_str()); + } + } + + if (!file_names.empty()) + { + log->PutCString (" Dir Mod Time File Len File Name"); + log->PutCString (" ---- ---------- ---------- ---------------------------"); + for (i=0; i<file_names.size(); ++i) + { + const FileNameEntry& fileEntry = file_names[i]; + log->Printf ("file_names[%3u] %4u 0x%8.8x 0x%8.8x %s", + i+1, + fileEntry.dir_idx, + fileEntry.mod_time, + fileEntry.length, + fileEntry.name.c_str()); + } + } +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::ParsePrologue::Append +// +// Append the contents of the prologue to the binary stream buffer +//---------------------------------------------------------------------- +//void +//DWARFDebugLine::Prologue::Append(BinaryStreamBuf& buff) const +//{ +// uint32_t i; +// +// buff.Append32(total_length); +// buff.Append16(version); +// buff.Append32(prologue_length); +// buff.Append8(min_inst_length); +// buff.Append8(default_is_stmt); +// buff.Append8(line_base); +// buff.Append8(line_range); +// buff.Append8(opcode_base); +// +// for (i=0; i<standard_opcode_lengths.size(); ++i) +// buff.Append8(standard_opcode_lengths[i]); +// +// for (i=0; i<include_directories.size(); ++i) +// buff.AppendCStr(include_directories[i].c_str()); +// buff.Append8(0); // Terminate the include directory section with empty string +// +// for (i=0; i<file_names.size(); ++i) +// { +// buff.AppendCStr(file_names[i].name.c_str()); +// buff.Append32_as_ULEB128(file_names[i].dir_idx); +// buff.Append32_as_ULEB128(file_names[i].mod_time); +// buff.Append32_as_ULEB128(file_names[i].length); +// } +// buff.Append8(0); // Terminate the file names section with empty string +//} + + +bool DWARFDebugLine::Prologue::GetFile(uint32_t file_idx, std::string& path, std::string& directory) const +{ + uint32_t idx = file_idx - 1; // File indexes are 1 based... + if (idx < file_names.size()) + { + path = file_names[idx].name; + uint32_t dir_idx = file_names[idx].dir_idx - 1; + if (dir_idx < include_directories.size()) + directory = include_directories[dir_idx]; + else + directory.clear(); + return true; + } + return false; +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::LineTable::Dump +//---------------------------------------------------------------------- +void +DWARFDebugLine::LineTable::Dump(Log *log) const +{ + if (prologue.get()) + prologue->Dump (log); + + if (!rows.empty()) + { + log->PutCString ("Address Line Column File ISA Flags"); + log->PutCString ("------------------ ------ ------ ------ --- -------------"); + Row::const_iterator pos = rows.begin(); + Row::const_iterator end = rows.end(); + while (pos != end) + { + (*pos).Dump (log); + ++pos; + } + } +} + + +void +DWARFDebugLine::LineTable::AppendRow(const DWARFDebugLine::Row& state) +{ + rows.push_back(state); +} + + + +//---------------------------------------------------------------------- +// Compare function for the binary search in DWARFDebugLine::LineTable::LookupAddress() +//---------------------------------------------------------------------- +static bool FindMatchingAddress (const DWARFDebugLine::Row& row1, const DWARFDebugLine::Row& row2) +{ + return row1.address < row2.address; +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::LineTable::LookupAddress +//---------------------------------------------------------------------- +uint32_t +DWARFDebugLine::LineTable::LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const +{ + uint32_t index = UINT32_MAX; + if (!rows.empty()) + { + // Use the lower_bound algorithm to perform a binary search since we know + // that our line table data is ordered by address. + DWARFDebugLine::Row row; + row.address = address; + Row::const_iterator begin_pos = rows.begin(); + Row::const_iterator end_pos = rows.end(); + Row::const_iterator pos = lower_bound(begin_pos, end_pos, row, FindMatchingAddress); + if (pos == end_pos) + { + if (address < cu_high_pc) + return rows.size()-1; + } + else + { + // Rely on fact that we are using a std::vector and we can do + // pointer arithmetic to find the row index (which will be one less + // that what we found since it will find the first position after + // the current address) since std::vector iterators are just + // pointers to the container type. + index = pos - begin_pos; + if (pos->address > address) + { + if (index > 0) + --index; + else + index = UINT32_MAX; + } + } + } + return index; // Failed to find address +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Row +//---------------------------------------------------------------------- +DWARFDebugLine::Row::Row(bool default_is_stmt) : + address(0), + line(1), + column(0), + file(1), + is_stmt(default_is_stmt), + basic_block(false), + end_sequence(false), + prologue_end(false), + epilogue_begin(false), + isa(0) +{ +} + +//---------------------------------------------------------------------- +// Called after a row is appended to the matrix +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::PostAppend() +{ + basic_block = false; + prologue_end = false; + epilogue_begin = false; +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Reset +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::Reset(bool default_is_stmt) +{ + address = 0; + line = 1; + column = 0; + file = 1; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + prologue_end = false; + epilogue_begin = false; + isa = 0; +} +//---------------------------------------------------------------------- +// DWARFDebugLine::Row::Dump +//---------------------------------------------------------------------- +void +DWARFDebugLine::Row::Dump(Log *log) const +{ + log->Printf( "0x%16.16" PRIx64 " %6u %6u %6u %3u %s%s%s%s%s", + address, + line, + column, + file, + isa, + is_stmt ? " is_stmt" : "", + basic_block ? " basic_block" : "", + prologue_end ? " prologue_end" : "", + epilogue_begin ? " epilogue_begin" : "", + end_sequence ? " end_sequence" : ""); +} + +//---------------------------------------------------------------------- +// Compare function LineTable structures +//---------------------------------------------------------------------- +static bool AddressLessThan (const DWARFDebugLine::Row& a, const DWARFDebugLine::Row& b) +{ + return a.address < b.address; +} + + + +// Insert a row at the correct address if the addresses can be out of +// order which can only happen when we are linking a line table that +// may have had it's contents rearranged. +void +DWARFDebugLine::Row::Insert(Row::collection& state_coll, const Row& state) +{ + // If we don't have anything yet, or if the address of the last state in our + // line table is less than the current one, just append the current state + if (state_coll.empty() || AddressLessThan(state_coll.back(), state)) + { + state_coll.push_back(state); + } + else + { + // Do a binary search for the correct entry + pair<Row::iterator, Row::iterator> range(equal_range(state_coll.begin(), state_coll.end(), state, AddressLessThan)); + + // If the addresses are equal, we can safely replace the previous entry + // with the current one if the one it is replacing is an end_sequence entry. + // We currently always place an extra end sequence when ever we exit a valid + // address range for a function in case the functions get rearranged by + // optimizations or by order specifications. These extra end sequences will + // disappear by getting replaced with valid consecutive entries within a + // compile unit if there are no gaps. + if (range.first == range.second) + { + state_coll.insert(range.first, state); + } + else + { + if ((distance(range.first, range.second) == 1) && range.first->end_sequence == true) + { + *range.first = state; + } + else + { + state_coll.insert(range.second, state); + } + } + } +} + +void +DWARFDebugLine::Row::Dump(Log *log, const Row::collection& state_coll) +{ + std::for_each (state_coll.begin(), state_coll.end(), bind2nd(std::mem_fun_ref(&Row::Dump),log)); +} + + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::State +//---------------------------------------------------------------------- +DWARFDebugLine::State::State(Prologue::shared_ptr& p, Log *l, DWARFDebugLine::State::Callback cb, void* userData) : + Row (p->default_is_stmt), + prologue (p), + log (l), + callback (cb), + callbackUserData (userData), + row (StartParsingLineTable) +{ + // Call the callback with the initial row state of zero for the prologue + if (callback) + callback(0, *this, callbackUserData); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::Reset +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::Reset() +{ + Row::Reset(prologue->default_is_stmt); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::AppendRowToMatrix +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::AppendRowToMatrix(dw_offset_t offset) +{ + // Each time we are to add an entry into the line table matrix + // call the callback function so that someone can do something with + // the current state of the state machine (like build a line table + // or dump the line table!) + if (log) + { + if (row == 0) + { + log->PutCString ("Address Line Column File ISA Flags"); + log->PutCString ("------------------ ------ ------ ------ --- -------------"); + } + Dump (log); + } + + ++row; // Increase the row number before we call our callback for a real row + if (callback) + callback(offset, *this, callbackUserData); + PostAppend(); +} + +//---------------------------------------------------------------------- +// DWARFDebugLine::State::Finalize +//---------------------------------------------------------------------- +void +DWARFDebugLine::State::Finalize(dw_offset_t offset) +{ + // Call the callback with a special row state when we are done parsing a + // line table + row = DoneParsingLineTable; + if (callback) + callback(offset, *this, callbackUserData); +} + +//void +//DWARFDebugLine::AppendLineTableData +//( +// const DWARFDebugLine::Prologue* prologue, +// const DWARFDebugLine::Row::collection& state_coll, +// const uint32_t addr_size, +// BinaryStreamBuf &debug_line_data +//) +//{ +// if (state_coll.empty()) +// { +// // We have no entries, just make an empty line table +// debug_line_data.Append8(0); +// debug_line_data.Append8(1); +// debug_line_data.Append8(DW_LNE_end_sequence); +// } +// else +// { +// DWARFDebugLine::Row::const_iterator pos; +// Row::const_iterator end = state_coll.end(); +// bool default_is_stmt = prologue->default_is_stmt; +// const DWARFDebugLine::Row reset_state(default_is_stmt); +// const DWARFDebugLine::Row* prev_state = &reset_state; +// const int32_t max_line_increment_for_special_opcode = prologue->MaxLineIncrementForSpecialOpcode(); +// for (pos = state_coll.begin(); pos != end; ++pos) +// { +// const DWARFDebugLine::Row& curr_state = *pos; +// int32_t line_increment = 0; +// dw_addr_t addr_offset = curr_state.address - prev_state->address; +// dw_addr_t addr_advance = (addr_offset) / prologue->min_inst_length; +// line_increment = (int32_t)(curr_state.line - prev_state->line); +// +// // If our previous state was the reset state, then let's emit the +// // address to keep GDB's DWARF parser happy. If we don't start each +// // sequence with a DW_LNE_set_address opcode, the line table won't +// // get slid properly in GDB. +// +// if (prev_state == &reset_state) +// { +// debug_line_data.Append8(0); // Extended opcode +// debug_line_data.Append32_as_ULEB128(addr_size + 1); // Length of opcode bytes +// debug_line_data.Append8(DW_LNE_set_address); +// debug_line_data.AppendMax64(curr_state.address, addr_size); +// addr_advance = 0; +// } +// +// if (prev_state->file != curr_state.file) +// { +// debug_line_data.Append8(DW_LNS_set_file); +// debug_line_data.Append32_as_ULEB128(curr_state.file); +// } +// +// if (prev_state->column != curr_state.column) +// { +// debug_line_data.Append8(DW_LNS_set_column); +// debug_line_data.Append32_as_ULEB128(curr_state.column); +// } +// +// // Don't do anything fancy if we are at the end of a sequence +// // as we don't want to push any extra rows since the DW_LNE_end_sequence +// // will push a row itself! +// if (curr_state.end_sequence) +// { +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Now push the end sequence on! +// debug_line_data.Append8(0); +// debug_line_data.Append8(1); +// debug_line_data.Append8(DW_LNE_end_sequence); +// +// prev_state = &reset_state; +// } +// else +// { +// if (line_increment || addr_advance) +// { +// if (line_increment > max_line_increment_for_special_opcode) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// line_increment = 0; +// } +// +// uint32_t special_opcode = (line_increment >= prologue->line_base) ? ((line_increment - prologue->line_base) + (prologue->line_range * addr_advance) + prologue->opcode_base) : 256; +// if (special_opcode > 255) +// { +// // Both the address and line won't fit in one special opcode +// // check to see if just the line advance will? +// uint32_t special_opcode_line = ((line_increment >= prologue->line_base) && (line_increment != 0)) ? +// ((line_increment - prologue->line_base) + prologue->opcode_base) : 256; +// +// +// if (special_opcode_line > 255) +// { +// // Nope, the line advance won't fit by itself, check the address increment by itself +// uint32_t special_opcode_addr = addr_advance ? +// ((0 - prologue->line_base) + (prologue->line_range * addr_advance) + prologue->opcode_base) : 256; +// +// if (special_opcode_addr > 255) +// { +// // Neither the address nor the line will fit in a +// // special opcode, we must manually enter both then +// // do a DW_LNS_copy to push a row (special opcode +// // automatically imply a new row is pushed) +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Now push a row onto the line table manually +// debug_line_data.Append8(DW_LNS_copy); +// +// } +// else +// { +// // The address increment alone will fit into a special opcode +// // so modify our line change, then issue a special opcode +// // for the address increment and it will push a row into the +// // line table +// if (line_increment != 0) +// { +// debug_line_data.Append8(DW_LNS_advance_line); +// debug_line_data.Append32_as_SLEB128(line_increment); +// } +// +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode_addr); +// } +// } +// else +// { +// // The line change alone will fit into a special opcode +// // so modify our address increment first, then issue a +// // special opcode for the line change and it will push +// // a row into the line table +// if (addr_advance > 0) +// { +// debug_line_data.Append8(DW_LNS_advance_pc); +// debug_line_data.Append32_as_ULEB128(addr_advance); +// } +// +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode_line); +// } +// } +// else +// { +// // Advance of line and address will fit into a single byte special opcode +// // and this will also push a row onto the line table +// debug_line_data.Append8(special_opcode); +// } +// } +// prev_state = &curr_state; +// } +// } +// } +//} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h new file mode 100644 index 000000000000..cfa8654ed9ba --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h @@ -0,0 +1,225 @@ +//===-- DWARFDebugLine.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugLine_h_ +#define SymbolFileDWARF_DWARFDebugLine_h_ + +#include <map> +#include <vector> +#include <string> + +#include "lldb/lldb-private.h" + +#include "DWARFDefines.h" + +class SymbolFileDWARF; +class DWARFDebugInfoEntry; + +//---------------------------------------------------------------------- +// DWARFDebugLine +//---------------------------------------------------------------------- +class DWARFDebugLine +{ +public: + //------------------------------------------------------------------ + // FileNameEntry + //------------------------------------------------------------------ + struct FileNameEntry + { + FileNameEntry() : + name(), + dir_idx(0), + mod_time(0), + length(0) + { + } + + std::string name; + dw_sleb128_t dir_idx; + dw_sleb128_t mod_time; + dw_sleb128_t length; + + }; + + //------------------------------------------------------------------ + // Prologue + //------------------------------------------------------------------ + struct Prologue + { + + Prologue() : + total_length(0), + version(0), + prologue_length(0), + min_inst_length(0), + default_is_stmt(0), + line_base(0), + line_range(0), + opcode_base(0), + standard_opcode_lengths(), + include_directories(), + file_names() + { + } + + typedef std::shared_ptr<Prologue> shared_ptr; + + uint32_t total_length; // The size in bytes of the statement information for this compilation unit (not including the total_length field itself). + uint16_t version; // Version identifier for the statement information format. + uint32_t prologue_length;// The number of bytes following the prologue_length field to the beginning of the first byte of the statement program itself. + uint8_t min_inst_length;// The size in bytes of the smallest target machine instruction. Statement program opcodes that alter the address register first multiply their operands by this value. + uint8_t default_is_stmt;// The initial value of theis_stmtregister. + int8_t line_base; // This parameter affects the meaning of the special opcodes. See below. + uint8_t line_range; // This parameter affects the meaning of the special opcodes. See below. + uint8_t opcode_base; // The number assigned to the first special opcode. + std::vector<uint8_t> standard_opcode_lengths; + std::vector<std::string> include_directories; + std::vector<FileNameEntry> file_names; + + // Length of the prologue in bytes + uint32_t Length() const { return prologue_length + sizeof(total_length) + sizeof(version) + sizeof(prologue_length); } + // Length of the line table data in bytes (not including the prologue) + uint32_t StatementTableLength() const { return total_length + sizeof(total_length) - Length(); } + int32_t MaxLineIncrementForSpecialOpcode() const { return line_base + (int8_t)line_range - 1; } + bool IsValid() const; +// void Append(BinaryStreamBuf& buff) const; + void Dump (lldb_private::Log *log); + void Clear() + { + total_length = version = prologue_length = min_inst_length = line_base = line_range = opcode_base = 0; + line_base = 0; + standard_opcode_lengths.clear(); + include_directories.clear(); + file_names.clear(); + } + bool GetFile(uint32_t file_idx, std::string& file, std::string& dir) const; + + }; + + // Standard .debug_line state machine structure + struct Row + { + typedef std::vector<Row> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + Row(bool default_is_stmt = false); + virtual ~Row() {} + void PostAppend (); + void Reset(bool default_is_stmt); + void Dump(lldb_private::Log *log) const; + static void Insert(Row::collection& state_coll, const Row& state); + static void Dump(lldb_private::Log *log, const Row::collection& state_coll); + + dw_addr_t address; // The program-counter value corresponding to a machine instruction generated by the compiler. + uint32_t line; // An unsigned integer indicating a source line number. Lines are numbered beginning at 1. The compiler may emit the value 0 in cases where an instruction cannot be attributed to any source line. + uint16_t column; // An unsigned integer indicating a column number within a source line. Columns are numbered beginning at 1. The value 0 is reserved to indicate that a statement begins at the 'left edge' of the line. + uint16_t file; // An unsigned integer indicating the identity of the source file corresponding to a machine instruction. + uint8_t is_stmt:1, // A boolean indicating that the current instruction is the beginning of a statement. + basic_block:1, // A boolean indicating that the current instruction is the beginning of a basic block. + end_sequence:1, // A boolean indicating that the current address is that of the first byte after the end of a sequence of target machine instructions. + prologue_end:1, // A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. + epilogue_begin:1;// A boolean indicating that the current address is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + uint32_t isa; // An unsigned integer whose value encodes the applicable instruction set architecture for the current instruction. + }; + + + //------------------------------------------------------------------ + // LineTable + //------------------------------------------------------------------ + struct LineTable + { + typedef std::shared_ptr<LineTable> shared_ptr; + + LineTable() : + prologue(), + rows() + { + } + + void AppendRow(const DWARFDebugLine::Row& state); + void Clear() + { + prologue.reset(); + rows.clear(); + } + + uint32_t LookupAddress(dw_addr_t address, dw_addr_t cu_high_pc) const; + void Dump(lldb_private::Log *log) const; + + Prologue::shared_ptr prologue; + Row::collection rows; + }; + + //------------------------------------------------------------------ + // State + //------------------------------------------------------------------ + struct State : public Row + { + typedef void (*Callback)(dw_offset_t offset, const State& state, void* userData); + + // Special row codes used when calling the callback + enum + { + StartParsingLineTable = 0, + DoneParsingLineTable = -1 + }; + + State (Prologue::shared_ptr& prologue_sp, + lldb_private::Log *log, + Callback callback, + void* userData); + + void + AppendRowToMatrix (dw_offset_t offset); + + void + Finalize (dw_offset_t offset); + + void + Reset (); + + Prologue::shared_ptr prologue; + lldb_private::Log *log; + Callback callback; // Callback function that gets called each time an entry is to be added to the matrix + void* callbackUserData; + int row; // The row number that starts at zero for the prologue, and increases for each row added to the matrix + private: + DISALLOW_COPY_AND_ASSIGN (State); + }; + + static bool DumpOpcodes(lldb_private::Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t line_offset = DW_INVALID_OFFSET, uint32_t dump_flags = 0); // If line_offset is invalid, dump everything + static bool DumpLineTableRows(lldb_private::Log *log, SymbolFileDWARF* dwarf2Data, dw_offset_t line_offset = DW_INVALID_OFFSET); // If line_offset is invalid, dump everything + static bool ParseSupportFiles(const lldb::ModuleSP &module_sp, const lldb_private::DataExtractor& debug_line_data, const char *cu_comp_dir, dw_offset_t stmt_list, lldb_private::FileSpecList &support_files); + static bool ParsePrologue(const lldb_private::DataExtractor& debug_line_data, lldb::offset_t* offset_ptr, Prologue* prologue); + static bool ParseStatementTable(const lldb_private::DataExtractor& debug_line_data, lldb::offset_t* offset_ptr, State::Callback callback, void* userData); + static dw_offset_t DumpStatementTable(lldb_private::Log *log, const lldb_private::DataExtractor& debug_line_data, const dw_offset_t line_offset); + static dw_offset_t DumpStatementOpcodes(lldb_private::Log *log, const lldb_private::DataExtractor& debug_line_data, const dw_offset_t line_offset, uint32_t flags); + static bool ParseStatementTable(const lldb_private::DataExtractor& debug_line_data, lldb::offset_t *offset_ptr, LineTable* line_table); + static void Parse(const lldb_private::DataExtractor& debug_line_data, DWARFDebugLine::State::Callback callback, void* userData); +// static void AppendLineTableData(const DWARFDebugLine::Prologue* prologue, const DWARFDebugLine::Row::collection& state_coll, const uint32_t addr_size, BinaryStreamBuf &debug_line_data); + + DWARFDebugLine() : + m_lineTableMap() + { + } + + void Parse(const lldb_private::DataExtractor& debug_line_data); + void ParseIfNeeded(const lldb_private::DataExtractor& debug_line_data); + LineTable::shared_ptr GetLineTable(const dw_offset_t offset) const; + +protected: + typedef std::map<dw_offset_t, LineTable::shared_ptr> LineTableMap; + typedef LineTableMap::iterator LineTableIter; + typedef LineTableMap::const_iterator LineTableConstIter; + + LineTableMap m_lineTableMap; +}; + +#endif // SymbolFileDWARF_DWARFDebugLine_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp new file mode 100644 index 000000000000..60ace9e82290 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp @@ -0,0 +1,48 @@ +//===-- DWARFDebugMacinfo.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugMacinfo.h" + +#include "DWARFDebugMacinfoEntry.h" +#include "SymbolFileDWARF.h" + +#include "lldb/Core/Stream.h" + +using namespace lldb_private; +using namespace std; + +DWARFDebugMacinfo::DWARFDebugMacinfo() +{ +} + +DWARFDebugMacinfo::~DWARFDebugMacinfo() +{ +} + +void +DWARFDebugMacinfo::Dump(Stream *s, const DataExtractor& macinfo_data, lldb::offset_t offset) +{ + DWARFDebugMacinfoEntry maninfo_entry; + if (macinfo_data.GetByteSize() == 0) + { + s->PutCString("< EMPTY >\n"); + return; + } + if (offset == LLDB_INVALID_OFFSET) + { + offset = 0; + while (maninfo_entry.Extract(macinfo_data, &offset)) + maninfo_entry.Dump(s); + } + else + { + if (maninfo_entry.Extract(macinfo_data, &offset)) + maninfo_entry.Dump(s); + } +} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h new file mode 100644 index 000000000000..5f3b437d964f --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h @@ -0,0 +1,29 @@ +//===-- DWARFDebugMacinfo.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugMacinfo_h_ +#define SymbolFileDWARF_DWARFDebugMacinfo_h_ + +#include "SymbolFileDWARF.h" + +class DWARFDebugMacinfo +{ +public: + DWARFDebugMacinfo(); + + ~DWARFDebugMacinfo(); + + static void + Dump (lldb_private::Stream *s, + const lldb_private::DataExtractor& macinfo_data, + lldb::offset_t offset = LLDB_INVALID_OFFSET); +}; + + +#endif // SymbolFileDWARF_DWARFDebugMacinfo_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp new file mode 100644 index 000000000000..5cd9cb6be474 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp @@ -0,0 +1,132 @@ +//===-- DWARFDebugMacinfoEntry.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugMacinfoEntry.h" + +#include "lldb/Core/Stream.h" + +using namespace lldb_private; +using namespace std; + +DWARFDebugMacinfoEntry::DWARFDebugMacinfoEntry() : + m_type_code(0), + m_line(0), + m_op2() +{ + m_op2.cstr = NULL; +} + +DWARFDebugMacinfoEntry::~DWARFDebugMacinfoEntry() +{ +} + +const char* +DWARFDebugMacinfoEntry::GetCString() const +{ + switch (m_type_code) + { + case 0: + case DW_MACINFO_start_file: + case DW_MACINFO_end_file: + return NULL; + default: + break; + } + return m_op2.cstr; +} + + + +void +DWARFDebugMacinfoEntry::Dump(Stream *s) const +{ + if (m_type_code) + { + s->PutCString(DW_MACINFO_value_to_name(m_type_code)); + + switch (m_type_code) + { + case DW_MACINFO_define: + s->Printf(" line:%u #define %s\n", (uint32_t)m_line, m_op2.cstr); + break; + + case DW_MACINFO_undef: + s->Printf(" line:%u #undef %s\n", (uint32_t)m_line, m_op2.cstr); + break; + + default: + s->Printf(" line:%u str: '%s'\n", (uint32_t)m_line, m_op2.cstr); + break; + + case DW_MACINFO_start_file: + s->Printf(" line:%u file index: '%u'\n", (uint32_t)m_line, (uint32_t)m_op2.file_idx); + break; + + case DW_MACINFO_end_file: + break; + } + } + else + { + s->PutCString(" END\n"); + } +} + + +bool +DWARFDebugMacinfoEntry::Extract(const DataExtractor& mac_info_data, lldb::offset_t* offset_ptr) +{ + if (mac_info_data.ValidOffset(*offset_ptr)) + { + m_type_code = mac_info_data.GetU8(offset_ptr); + + switch (m_type_code) + { + + case DW_MACINFO_define: + case DW_MACINFO_undef: + // 2 operands: + // Arg 1: operand encodes the line number of the source line on which + // the relevant defining or undefining pre-processor directives + // appeared. + m_line = mac_info_data.GetULEB128(offset_ptr); + // Arg 2: define string + m_op2.cstr = mac_info_data.GetCStr(offset_ptr); + break; + + case DW_MACINFO_start_file: + // 2 operands: + // Op 1: line number of the source line on which the inclusion + // pre-processor directive occurred. + m_line = mac_info_data.GetULEB128(offset_ptr); + // Op 2: a source file name index to a file number in the statement + // information table for the relevant compilation unit. + m_op2.file_idx = mac_info_data.GetULEB128(offset_ptr); + break; + + case 0: // End of list + case DW_MACINFO_end_file: + // No operands + m_line = DW_INVALID_OFFSET; + m_op2.cstr = NULL; + break; + default: + // Vendor specific entries always have a ULEB128 and a string + m_line = mac_info_data.GetULEB128(offset_ptr); + m_op2.cstr = mac_info_data.GetCStr(offset_ptr); + break; + } + return true; + } + else + m_type_code = 0; + + return false; +} + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h new file mode 100644 index 000000000000..46fd44a22a63 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h @@ -0,0 +1,57 @@ +//===-- DWARFDebugMacinfoEntry.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugMacinfoEntry_h_ +#define SymbolFileDWARF_DWARFDebugMacinfoEntry_h_ + +#include "SymbolFileDWARF.h" + +class DWARFDebugMacinfoEntry +{ +public: + DWARFDebugMacinfoEntry(); + + ~DWARFDebugMacinfoEntry(); + + uint8_t + TypeCode() const + { + return m_type_code; + } + + uint8_t + GetLineNumber() const + { + return m_line; + } + + void + Dump(lldb_private::Stream *s) const; + + const char* + GetCString() const; + + bool + Extract(const lldb_private::DataExtractor& mac_info_data, + lldb::offset_t* offset_ptr); + +protected: + +private: + uint8_t m_type_code; + dw_uleb128_t m_line; + union + { + dw_uleb128_t file_idx; + const char* cstr; + } m_op2; +}; + + +#endif // SymbolFileDWARF_DWARFDebugMacinfoEntry_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp new file mode 100644 index 000000000000..3e511007a1ec --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp @@ -0,0 +1,296 @@ +//===-- DWARFDebugPubnames.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugPubnames.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +#include "DWARFDebugInfo.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFCompileUnit.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" + + +using namespace lldb; +using namespace lldb_private; + +DWARFDebugPubnames::DWARFDebugPubnames() : + m_sets() +{ +} + +bool +DWARFDebugPubnames::Extract(const DataExtractor& data) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugPubnames::Extract (byte_size = %" PRIu64 ")", + (uint64_t)data.GetByteSize()); + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_PUBNAMES)); + if (log) + log->Printf("DWARFDebugPubnames::Extract (byte_size = %" PRIu64 ")", (uint64_t)data.GetByteSize()); + + if (data.ValidOffset(0)) + { + lldb::offset_t offset = 0; + + DWARFDebugPubnamesSet set; + while (data.ValidOffset(offset)) + { + if (set.Extract(data, &offset)) + { + m_sets.push_back(set); + offset = set.GetOffsetOfNextEntry(); + } + else + break; + } + if (log) + Dump (log); + return true; + } + return false; +} + + +bool +DWARFDebugPubnames::GeneratePubnames(SymbolFileDWARF* dwarf2Data) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "DWARFDebugPubnames::GeneratePubnames (data = %p)", + dwarf2Data); + + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_PUBNAMES)); + if (log) + log->Printf("DWARFDebugPubnames::GeneratePubnames (data = %p)", dwarf2Data); + + m_sets.clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + + const DataExtractor* debug_str = &dwarf2Data->get_debug_str_data(); + + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (cu->GetAddressByteSize()); + + bool clear_dies = cu->ExtractDIEsIfNeeded (false) > 1; + + DWARFDIECollection dies; + const size_t die_count = cu->AppendDIEsWithTag (DW_TAG_subprogram, dies) + + cu->AppendDIEsWithTag (DW_TAG_variable, dies); + + dw_offset_t cu_offset = cu->GetOffset(); + DWARFDebugPubnamesSet pubnames_set(DW_INVALID_OFFSET, cu_offset, cu->GetNextCompileUnitOffset() - cu_offset); + + size_t die_idx; + for (die_idx = 0; die_idx < die_count; ++die_idx) + { + const DWARFDebugInfoEntry *die = dies.GetDIEPtrAtIndex(die_idx); + DWARFDebugInfoEntry::Attributes attributes; + const char *name = NULL; + const char *mangled = NULL; + bool add_die = false; + const size_t num_attributes = die->GetAttributes(dwarf2Data, cu, fixed_form_sizes, attributes); + if (num_attributes > 0) + { + uint32_t i; + + dw_tag_t tag = die->Tag(); + + for (i=0; i<num_attributes; ++i) + { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + switch (attr) + { + case DW_AT_name: + if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) + name = form_value.AsCString(debug_str); + break; + + case DW_AT_MIPS_linkage_name: + case DW_AT_linkage_name: + if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) + mangled = form_value.AsCString(debug_str); + break; + + case DW_AT_low_pc: + case DW_AT_ranges: + case DW_AT_entry_pc: + if (tag == DW_TAG_subprogram) + add_die = true; + break; + + case DW_AT_location: + if (tag == DW_TAG_variable) + { + const DWARFDebugInfoEntry* parent_die = die->GetParent(); + while ( parent_die != NULL ) + { + switch (parent_die->Tag()) + { + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + // Even if this is a function level static, we don't add it. We could theoretically + // add these if we wanted to by introspecting into the DW_AT_location and seeing + // if the location describes a hard coded address, but we don't want the performance + // penalty of that right now. + add_die = false; +// if (attributes.ExtractFormValueAtIndex(dwarf2Data, i, form_value)) +// { +// // If we have valid block data, then we have location expression bytes +// // that are fixed (not a location list). +// const uint8_t *block_data = form_value.BlockData(); +// if (block_data) +// { +// uint32_t block_length = form_value.Unsigned(); +// if (block_length == 1 + attributes.CompileUnitAtIndex(i)->GetAddressByteSize()) +// { +// if (block_data[0] == DW_OP_addr) +// add_die = true; +// } +// } +// } + parent_die = NULL; // Terminate the while loop. + break; + + case DW_TAG_compile_unit: + add_die = true; + parent_die = NULL; // Terminate the while loop. + break; + + default: + parent_die = parent_die->GetParent(); // Keep going in the while loop. + break; + } + } + } + break; + } + } + } + + if (add_die && (name || mangled)) + { + pubnames_set.AddDescriptor(die->GetOffset() - cu_offset, mangled ? mangled : name); + } + } + + if (pubnames_set.NumDescriptors() > 0) + { + m_sets.push_back(pubnames_set); + } + + // Keep memory down by clearing DIEs if this generate function + // caused them to be parsed + if (clear_dies) + cu->ClearDIEs (true); + } + } + if (m_sets.empty()) + return false; + if (log) + Dump (log); + return true; +} + +bool +DWARFDebugPubnames::GeneratePubBaseTypes(SymbolFileDWARF* dwarf2Data) +{ + m_sets.clear(); + DWARFDebugInfo* debug_info = dwarf2Data->DebugInfo(); + if (debug_info) + { + uint32_t cu_idx = 0; + const uint32_t num_compile_units = dwarf2Data->GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); + DWARFDIECollection dies; + const size_t die_count = cu->AppendDIEsWithTag (DW_TAG_base_type, dies); + dw_offset_t cu_offset = cu->GetOffset(); + DWARFDebugPubnamesSet pubnames_set(DW_INVALID_OFFSET, cu_offset, cu->GetNextCompileUnitOffset() - cu_offset); + + size_t die_idx; + for (die_idx = 0; die_idx < die_count; ++die_idx) + { + const DWARFDebugInfoEntry *die = dies.GetDIEPtrAtIndex(die_idx); + const char *name = die->GetAttributeValueAsString(dwarf2Data, cu, DW_AT_name, NULL); + + if (name) + { + pubnames_set.AddDescriptor(die->GetOffset() - cu_offset, name); + } + } + + if (pubnames_set.NumDescriptors() > 0) + { + m_sets.push_back(pubnames_set); + } + } + } + return !m_sets.empty(); +} + +void +DWARFDebugPubnames::Dump(Log *s) const +{ + if (m_sets.empty()) + s->PutCString("< EMPTY >\n"); + else + { + const_iterator pos; + const_iterator end = m_sets.end(); + + for (pos = m_sets.begin(); pos != end; ++pos) + (*pos).Dump(s); + } +} + +bool +DWARFDebugPubnames::Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offsets) const +{ + const_iterator pos; + const_iterator end = m_sets.end(); + + die_offsets.clear(); + + for (pos = m_sets.begin(); pos != end; ++pos) + { + (*pos).Find(name, ignore_case, die_offsets); + } + + return !die_offsets.empty(); +} + +bool +DWARFDebugPubnames::Find(const RegularExpression& regex, std::vector<dw_offset_t>& die_offsets) const +{ + const_iterator pos; + const_iterator end = m_sets.end(); + + die_offsets.clear(); + + for (pos = m_sets.begin(); pos != end; ++pos) + { + (*pos).Find(regex, die_offsets); + } + + return !die_offsets.empty(); +} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h new file mode 100644 index 000000000000..09eb80ab3006 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h @@ -0,0 +1,38 @@ +//===-- DWARFDebugPubnames.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugPubnames_h_ +#define SymbolFileDWARF_DWARFDebugPubnames_h_ + +#include "SymbolFileDWARF.h" + +#include <list> + +#include "DWARFDebugPubnamesSet.h" + +class DWARFDebugPubnames +{ +public: + DWARFDebugPubnames(); + bool Extract(const lldb_private::DataExtractor& data); + bool GeneratePubnames(SymbolFileDWARF* dwarf2Data); + bool GeneratePubBaseTypes(SymbolFileDWARF* dwarf2Data); + + void Dump(lldb_private::Log *s) const; + bool Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offset_coll) const; + bool Find(const lldb_private::RegularExpression& regex, std::vector<dw_offset_t>& die_offsets) const; +protected: + typedef std::list<DWARFDebugPubnamesSet> collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_sets; +}; + +#endif // SymbolFileDWARF_DWARFDebugPubnames_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp new file mode 100644 index 000000000000..2df8d525f03f --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp @@ -0,0 +1,166 @@ +//===-- DWARFDebugPubnamesSet.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugPubnamesSet.h" + +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Log.h" + +#include "SymbolFileDWARF.h" + +using namespace lldb_private; + +DWARFDebugPubnamesSet::DWARFDebugPubnamesSet() : + m_offset(DW_INVALID_OFFSET), + m_header(), + m_descriptors(), + m_name_to_descriptor_index() +{ +} + +DWARFDebugPubnamesSet::DWARFDebugPubnamesSet(dw_offset_t debug_aranges_offset, dw_offset_t cu_die_offset, dw_offset_t cu_die_length) : + m_offset(debug_aranges_offset), + m_header(), + m_descriptors(), + m_name_to_descriptor_index() +{ + m_header.length = 10; // set the length to only include the header right for now + m_header.version = 2; // The DWARF version number + m_header.die_offset = cu_die_offset;// compile unit .debug_info offset + m_header.die_length = cu_die_length;// compile unit .debug_info length +} + +void +DWARFDebugPubnamesSet::AddDescriptor(dw_offset_t cu_rel_offset, const char* name) +{ + if (name && name[0]) + { + // Adjust our header length + m_header.length += strlen(name) + 1 + sizeof(dw_offset_t); + Descriptor pubnameDesc(cu_rel_offset, name); + m_descriptors.push_back(pubnameDesc); + } +} + +void +DWARFDebugPubnamesSet::Clear() +{ + m_offset = DW_INVALID_OFFSET; + m_header.length = 10; + m_header.version = 2; + m_header.die_offset = DW_INVALID_OFFSET; + m_header.die_length = 0; + m_descriptors.clear(); +} + + +//---------------------------------------------------------------------- +// InitNameIndexes +//---------------------------------------------------------------------- +void +DWARFDebugPubnamesSet::InitNameIndexes() const +{ + // Create the name index vector to be able to quickly search by name + const size_t count = m_descriptors.size(); + for (uint32_t idx = 0; idx < count; ++idx) + { + const char* name = m_descriptors[idx].name.c_str(); + if (name && name[0]) + m_name_to_descriptor_index.insert(cstr_to_index_mmap::value_type(name, idx)); + } +} + + +bool +DWARFDebugPubnamesSet::Extract(const DataExtractor& data, lldb::offset_t *offset_ptr) +{ + if (data.ValidOffset(*offset_ptr)) + { + m_descriptors.clear(); + m_offset = *offset_ptr; + m_header.length = data.GetU32(offset_ptr); + m_header.version = data.GetU16(offset_ptr); + m_header.die_offset = data.GetU32(offset_ptr); + m_header.die_length = data.GetU32(offset_ptr); + + Descriptor pubnameDesc; + while (data.ValidOffset(*offset_ptr)) + { + pubnameDesc.offset = data.GetU32(offset_ptr); + + if (pubnameDesc.offset) + { + const char* name = data.GetCStr(offset_ptr); + if (name && name[0]) + { + pubnameDesc.name = name; + m_descriptors.push_back(pubnameDesc); + } + } + else + break; // We are done if we get a zero 4 byte offset + } + + return !m_descriptors.empty(); + } + return false; +} + +dw_offset_t +DWARFDebugPubnamesSet::GetOffsetOfNextEntry() const +{ + return m_offset + m_header.length + 4; +} + +void +DWARFDebugPubnamesSet::Dump(Log *log) const +{ + log->Printf("Pubnames Header: length = 0x%8.8x, version = 0x%4.4x, die_offset = 0x%8.8x, die_length = 0x%8.8x", + m_header.length, + m_header.version, + m_header.die_offset, + m_header.die_length); + + bool verbose = log->GetVerbose(); + + DescriptorConstIter pos; + DescriptorConstIter end = m_descriptors.end(); + for (pos = m_descriptors.begin(); pos != end; ++pos) + { + if (verbose) + log->Printf("0x%8.8x + 0x%8.8x = 0x%8.8x: %s", pos->offset, m_header.die_offset, pos->offset + m_header.die_offset, pos->name.c_str()); + else + log->Printf("0x%8.8x: %s", pos->offset + m_header.die_offset, pos->name.c_str()); + } +} + + +void +DWARFDebugPubnamesSet::Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offset_coll) const +{ + if (!m_descriptors.empty() && m_name_to_descriptor_index.empty()) + InitNameIndexes(); + + std::pair<cstr_to_index_mmap::const_iterator, cstr_to_index_mmap::const_iterator> range(m_name_to_descriptor_index.equal_range(name)); + for (cstr_to_index_mmap::const_iterator pos = range.first; pos != range.second; ++pos) + die_offset_coll.push_back(m_header.die_offset + m_descriptors[(*pos).second].offset); +} + +void +DWARFDebugPubnamesSet::Find(const RegularExpression& regex, std::vector<dw_offset_t>& die_offset_coll) const +{ + DescriptorConstIter pos; + DescriptorConstIter end = m_descriptors.end(); + for (pos = m_descriptors.begin(); pos != end; ++pos) + { + if ( regex.Execute(pos->name.c_str()) ) + die_offset_coll.push_back(m_header.die_offset + pos->offset); + } +} + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h new file mode 100644 index 000000000000..941c83e58a44 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h @@ -0,0 +1,99 @@ +//===-- DWARFDebugPubnamesSet.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugPubnamesSet_h_ +#define SymbolFileDWARF_DWARFDebugPubnamesSet_h_ + +#include "SymbolFileDWARF.h" +#include <string> +#include <vector> +#if __cplusplus >= 201103L +#include <unordered_map> +#else +#include <ext/hash_map> +#endif + +class DWARFDebugPubnamesSet +{ +public: + struct Header + { + uint32_t length; // length of the set of entries for this compilation unit, not including the length field itself + uint16_t version; // The DWARF version number + uint32_t die_offset; // compile unit .debug_info offset + uint32_t die_length; // compile unit .debug_info length + Header() : + length(10), + version(2), + die_offset(DW_INVALID_OFFSET), + die_length(0) + { + } + }; + + struct Descriptor + { + Descriptor() : + offset(), + name() + { + } + + Descriptor(dw_offset_t the_offset, const char *the_name) : + offset(the_offset), + name(the_name ? the_name : "") + { + } + + dw_offset_t offset; + std::string name; + }; + + DWARFDebugPubnamesSet(); + DWARFDebugPubnamesSet(dw_offset_t debug_aranges_offset, dw_offset_t cu_die_offset, dw_offset_t die_length); + dw_offset_t GetOffset() const { return m_offset; } + void SetOffset(dw_offset_t offset) { m_offset = offset; } + DWARFDebugPubnamesSet::Header& GetHeader() { return m_header; } + const DWARFDebugPubnamesSet::Header& GetHeader() const { return m_header; } + const DWARFDebugPubnamesSet::Descriptor* GetDescriptor(uint32_t i) const + { + if (i < m_descriptors.size()) + return &m_descriptors[i]; + return NULL; + } + uint32_t NumDescriptors() const { return m_descriptors.size(); } + void AddDescriptor(dw_offset_t cu_rel_offset, const char* name); + void Clear(); + bool Extract(const lldb_private::DataExtractor& debug_pubnames_data, lldb::offset_t *offset_ptr); + void Dump(lldb_private::Log *s) const; + void InitNameIndexes() const; + void Find(const char* name, bool ignore_case, std::vector<dw_offset_t>& die_offset_coll) const; + void Find(const lldb_private::RegularExpression& regex, std::vector<dw_offset_t>& die_offsets) const; + dw_offset_t GetOffsetOfNextEntry() const; + + + +protected: + typedef std::vector<Descriptor> DescriptorColl; + typedef DescriptorColl::iterator DescriptorIter; + typedef DescriptorColl::const_iterator DescriptorConstIter; + + + dw_offset_t m_offset; + Header m_header; +#if __cplusplus >= 201103L + typedef std::unordered_multimap<const char*, uint32_t, std::hash<const char*>, CStringEqualBinaryPredicate> cstr_to_index_mmap; +#else + typedef __gnu_cxx::hash_multimap<const char*, uint32_t, __gnu_cxx::hash<const char*>, CStringEqualBinaryPredicate> cstr_to_index_mmap; +#endif + DescriptorColl m_descriptors; + mutable cstr_to_index_mmap m_name_to_descriptor_index; +}; + +#endif // SymbolFileDWARF_DWARFDebugPubnamesSet_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp new file mode 100644 index 000000000000..461b17fc3aba --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp @@ -0,0 +1,192 @@ +//===-- DWARFDebugRanges.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDebugRanges.h" +#include "SymbolFileDWARF.h" +#include "lldb/Core/Stream.h" +#include <assert.h> + +using namespace lldb_private; +using namespace std; + +DWARFDebugRanges::DWARFDebugRanges() : + m_range_map() +{ +} + +DWARFDebugRanges::~DWARFDebugRanges() +{ +} + +void +DWARFDebugRanges::Extract(SymbolFileDWARF* dwarf2Data) +{ + RangeList range_list; + lldb::offset_t offset = 0; + dw_offset_t debug_ranges_offset = offset; + while (Extract(dwarf2Data, &offset, range_list)) + { + m_range_map[debug_ranges_offset] = range_list; + debug_ranges_offset = offset; + } +} + +//void +//DWARFDebugRanges::RangeList::AddOffset(dw_addr_t offset) +//{ +// if (!ranges.empty()) +// { +// Range::iterator pos = ranges.begin(); +// Range::iterator end_pos = ranges.end(); +// for (pos = ranges.begin(); pos != end_pos; ++pos) +// { +// // assert for unsigned overflows +// assert (~pos->begin_offset >= offset); +// assert (~pos->end_offset >= offset); +// pos->begin_offset += offset; +// pos->end_offset += offset; +// } +// } +//} +// +//void +//DWARFDebugRanges::RangeList::SubtractOffset(dw_addr_t offset) +//{ +// if (!ranges.empty()) +// { +// Range::iterator pos = ranges.begin(); +// Range::iterator end_pos = ranges.end(); +// for (pos = ranges.begin(); pos != end_pos; ++pos) +// { +// assert (pos->begin_offset >= offset); +// assert (pos->end_offset >= offset); +// pos->begin_offset -= offset; +// pos->end_offset -= offset; +// } +// } +//} +// +// +//const DWARFDebugRanges::Range* +//DWARFDebugRanges::RangeList::RangeAtIndex(size_t i) const +//{ +// if (i < ranges.size()) +// return &ranges[i]; +// return NULL; +//} + +bool +DWARFDebugRanges::Extract(SymbolFileDWARF* dwarf2Data, lldb::offset_t *offset_ptr, RangeList &range_list) +{ + range_list.Clear(); + + lldb::offset_t range_offset = *offset_ptr; + const DataExtractor& debug_ranges_data = dwarf2Data->get_debug_ranges_data(); + uint32_t addr_size = debug_ranges_data.GetAddressByteSize(); + + while (debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) + { + dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + if (!begin && !end) + { + // End of range list + break; + } + // Extend 4 byte addresses that consists of 32 bits of 1's to be 64 bits + // of ones + switch (addr_size) + { + case 2: + if (begin == 0xFFFFull) + begin = LLDB_INVALID_ADDRESS; + break; + + case 4: + if (begin == 0xFFFFFFFFull) + begin = LLDB_INVALID_ADDRESS; + break; + + case 8: + break; + + default: + assert(!"DWARFDebugRanges::RangeList::Extract() unsupported address size."); + break; + } + + // Filter out empty ranges + if (begin < end) + range_list.Append(Range(begin, end - begin)); + } + + // Make sure we consumed at least something + return range_offset != *offset_ptr; +} + + +void +DWARFDebugRanges::Dump(Stream &s, const DataExtractor& debug_ranges_data, lldb::offset_t *offset_ptr, dw_addr_t cu_base_addr) +{ + uint32_t addr_size = s.GetAddressByteSize(); + bool verbose = s.GetVerbose(); + + dw_addr_t base_addr = cu_base_addr; + while (debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) + { + dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size); + // Extend 4 byte addresses that consits of 32 bits of 1's to be 64 bits + // of ones + if (begin == 0xFFFFFFFFull && addr_size == 4) + begin = LLDB_INVALID_ADDRESS; + + s.Indent(); + if (verbose) + { + s.AddressRange(begin, end, sizeof (dw_addr_t), " offsets = "); + } + + + if (begin == 0 && end == 0) + { + s.PutCString(" End"); + break; + } + else if (begin == LLDB_INVALID_ADDRESS) + { + // A base address selection entry + base_addr = end; + s.Address(base_addr, sizeof (dw_addr_t), " Base address = "); + } + else + { + // Convert from offset to an address + dw_addr_t begin_addr = begin + base_addr; + dw_addr_t end_addr = end + base_addr; + + s.AddressRange(begin_addr, end_addr, sizeof (dw_addr_t), verbose ? " ==> addrs = " : NULL); + } + } +} + +bool +DWARFDebugRanges::FindRanges(dw_offset_t debug_ranges_offset, RangeList& range_list) const +{ + range_map_const_iterator pos = m_range_map.find(debug_ranges_offset); + if (pos != m_range_map.end()) + { + range_list = pos->second; + return true; + } + return false; +} + + + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h new file mode 100644 index 000000000000..40899abe9c25 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h @@ -0,0 +1,46 @@ +//===-- DWARFDebugRanges.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDebugRanges_h_ +#define SymbolFileDWARF_DWARFDebugRanges_h_ + +#include "SymbolFileDWARF.h" + +#include <map> +#include <vector> + +#include "lldb/Core/RangeMap.h" + +class DWARFDebugRanges +{ +public: + typedef lldb_private::RangeArray<dw_addr_t, dw_addr_t, 2> RangeList; + typedef RangeList::Entry Range; + + DWARFDebugRanges(); + ~DWARFDebugRanges(); + void Extract(SymbolFileDWARF* dwarf2Data); + static void Dump(lldb_private::Stream &s, const lldb_private::DataExtractor& debug_ranges_data, lldb::offset_t *offset_ptr, dw_addr_t cu_base_addr); + bool FindRanges(dw_offset_t debug_ranges_offset, DWARFDebugRanges::RangeList& range_list) const; + +protected: + + bool + Extract (SymbolFileDWARF* dwarf2Data, + lldb::offset_t *offset_ptr, + RangeList &range_list); + + typedef std::map<dw_offset_t, RangeList> range_map; + typedef range_map::iterator range_map_iterator; + typedef range_map::const_iterator range_map_const_iterator; + range_map m_range_map; +}; + + +#endif // SymbolFileDWARF_DWARFDebugRanges_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp new file mode 100644 index 000000000000..abf69190c93c --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp @@ -0,0 +1,104 @@ +//===-- DWARFDeclContext.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDeclContext.h" + +const char * +DWARFDeclContext::GetQualifiedName () const +{ + if (m_qualified_name.empty()) + { + // The declaration context array for a class named "foo" in namespace + // "a::b::c" will be something like: + // [0] DW_TAG_class_type "foo" + // [1] DW_TAG_namespace "c" + // [2] DW_TAG_namespace "b" + // [3] DW_TAG_namespace "a" + if (!m_entries.empty()) + { + if (m_entries.size() == 1) + { + if (m_entries[0].name) + { + m_qualified_name.append("::"); + m_qualified_name.append(m_entries[0].name); + } + } + else + { + collection::const_reverse_iterator pos; + collection::const_reverse_iterator begin = m_entries.rbegin(); + collection::const_reverse_iterator end = m_entries.rend(); + for (pos = begin; pos != end; ++pos) + { + if (pos != begin) + m_qualified_name.append("::"); + if (pos->name == NULL) + { + if (pos->tag == DW_TAG_namespace) + m_qualified_name.append ("(anonymous namespace)"); + else if (pos->tag == DW_TAG_class_type) + m_qualified_name.append ("(anonymous class)"); + else if (pos->tag == DW_TAG_structure_type) + m_qualified_name.append ("(anonymous struct)"); + else if (pos->tag == DW_TAG_union_type) + m_qualified_name.append ("(anonymous union)"); + else + m_qualified_name.append ("(anonymous)"); + } + else + m_qualified_name.append(pos->name); + } + } + } + } + if (m_qualified_name.empty()) + return NULL; + return m_qualified_name.c_str(); +} + + +bool +DWARFDeclContext::operator==(const DWARFDeclContext& rhs) const +{ + if (m_entries.size() != rhs.m_entries.size()) + return false; + + collection::const_iterator pos; + collection::const_iterator begin = m_entries.begin(); + collection::const_iterator end = m_entries.end(); + + collection::const_iterator rhs_pos; + collection::const_iterator rhs_begin = rhs.m_entries.begin(); + // The two entry arrays have the same size + + // First compare the tags before we do expensize name compares + for (pos = begin, rhs_pos = rhs_begin; pos != end; ++pos, ++rhs_pos) + { + if (pos->tag != rhs_pos->tag) + { + // Check for DW_TAG_structure_type and DW_TAG_class_type as they are often + // used interchangeably in GCC + if (pos->tag == DW_TAG_structure_type && rhs_pos->tag == DW_TAG_class_type) + continue; + if (pos->tag == DW_TAG_class_type && rhs_pos->tag == DW_TAG_structure_type) + continue; + return false; + } + } + // The tags all match, now compare the names + for (pos = begin, rhs_pos = rhs_begin; pos != end; ++pos, ++rhs_pos) + { + if (!pos->NameMatches (*rhs_pos)) + return false; + } + // All tags and names match + return true; +} + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h b/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h new file mode 100644 index 000000000000..accd3446317a --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h @@ -0,0 +1,109 @@ +//===-- DWARFDeclContext.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDeclContext_h_ +#define SymbolFileDWARF_DWARFDeclContext_h_ + +// C Includes +// C++ Includes +#include <string> +#include <vector> +// Other libraries and framework includes +#include "lldb/Core/ConstString.h" +// Project includes +#include "DWARFDefines.h" + +//---------------------------------------------------------------------- +// DWARFDeclContext +// +// A class that represents a declaration context all the way down to a +// DIE. This is useful when trying to find a DIE in one DWARF to a DIE +// in another DWARF file. +//---------------------------------------------------------------------- + +class DWARFDeclContext +{ +public: + struct Entry + { + Entry () : + tag(0), + name(NULL) + { + } + Entry (dw_tag_t t, const char *n) : + tag(t), + name(n) + { + } + + bool + NameMatches (const Entry& rhs) const + { + if (name == rhs.name) + return true; + else if (name && rhs.name) + return strcmp(name, rhs.name) == 0; + return false; + } + + // Test operator + operator bool() const + { + return tag != 0; + } + + dw_tag_t tag; + const char *name; + }; + + DWARFDeclContext () : + m_entries() + { + } + + void + AppendDeclContext (dw_tag_t tag, const char *name) + { + m_entries.push_back(Entry(tag, name)); + } + + bool + operator ==(const DWARFDeclContext& rhs) const; + + uint32_t + GetSize() const + { + return m_entries.size(); + } + + Entry & + operator[] (uint32_t idx) + { + // "idx" must be valid + return m_entries[idx]; + } + + const Entry & + operator[] (uint32_t idx) const + { + // "idx" must be valid + return m_entries[idx]; + } + + const char * + GetQualifiedName () const; + +protected: + typedef std::vector<Entry> collection; + collection m_entries; + mutable std::string m_qualified_name; +}; + +#endif // SymbolFileDWARF_DWARFDeclContext_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp new file mode 100644 index 000000000000..fe4093bc130e --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp @@ -0,0 +1,497 @@ +//===-- DWARFDefines.c ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFDefines.h" +#include <cstdio> +#include <cstring> +#include <string> +#include "lldb/Core/ConstString.h" + +namespace lldb_private { + +const char * +DW_TAG_value_to_name (uint32_t val) +{ + static char invalid[100]; + + if (val == 0) + return "NULL"; + + const char *llvmstr = llvm::dwarf::TagString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_TAG constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_CHILDREN_value_to_name (uint8_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::ChildrenString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_CHILDREN constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_AT_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::AttributeString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_AT constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_FORM_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::FormEncodingString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_FORM constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_OP_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::OperationEncodingString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +DRC_class +DW_OP_value_to_class (uint32_t val) +{ + switch (val) { + case 0x03: return DRC_ONEOPERAND; + case 0x06: return DRC_ZEROOPERANDS; + case 0x08: return DRC_ONEOPERAND; + case 0x09: return DRC_ONEOPERAND; + case 0x0a: return DRC_ONEOPERAND; + case 0x0b: return DRC_ONEOPERAND; + case 0x0c: return DRC_ONEOPERAND; + case 0x0d: return DRC_ONEOPERAND; + case 0x0e: return DRC_ONEOPERAND; + case 0x0f: return DRC_ONEOPERAND; + case 0x10: return DRC_ONEOPERAND; + case 0x11: return DRC_ONEOPERAND; + case 0x12: return DRC_ZEROOPERANDS; + case 0x13: return DRC_ZEROOPERANDS; + case 0x14: return DRC_ZEROOPERANDS; + case 0x15: return DRC_ONEOPERAND; + case 0x16: return DRC_ZEROOPERANDS; + case 0x17: return DRC_ZEROOPERANDS; + case 0x18: return DRC_ZEROOPERANDS; + case 0x19: return DRC_ZEROOPERANDS; + case 0x1a: return DRC_ZEROOPERANDS; + case 0x1b: return DRC_ZEROOPERANDS; + case 0x1c: return DRC_ZEROOPERANDS; + case 0x1d: return DRC_ZEROOPERANDS; + case 0x1e: return DRC_ZEROOPERANDS; + case 0x1f: return DRC_ZEROOPERANDS; + case 0x20: return DRC_ZEROOPERANDS; + case 0x21: return DRC_ZEROOPERANDS; + case 0x22: return DRC_ZEROOPERANDS; + case 0x23: return DRC_ONEOPERAND; + case 0x24: return DRC_ZEROOPERANDS; + case 0x25: return DRC_ZEROOPERANDS; + case 0x26: return DRC_ZEROOPERANDS; + case 0x27: return DRC_ZEROOPERANDS; + case 0x2f: return DRC_ONEOPERAND; + case 0x28: return DRC_ONEOPERAND; + case 0x29: return DRC_ZEROOPERANDS; + case 0x2a: return DRC_ZEROOPERANDS; + case 0x2b: return DRC_ZEROOPERANDS; + case 0x2c: return DRC_ZEROOPERANDS; + case 0x2d: return DRC_ZEROOPERANDS; + case 0x2e: return DRC_ZEROOPERANDS; + case 0x30: return DRC_ZEROOPERANDS; + case 0x31: return DRC_ZEROOPERANDS; + case 0x32: return DRC_ZEROOPERANDS; + case 0x33: return DRC_ZEROOPERANDS; + case 0x34: return DRC_ZEROOPERANDS; + case 0x35: return DRC_ZEROOPERANDS; + case 0x36: return DRC_ZEROOPERANDS; + case 0x37: return DRC_ZEROOPERANDS; + case 0x38: return DRC_ZEROOPERANDS; + case 0x39: return DRC_ZEROOPERANDS; + case 0x3a: return DRC_ZEROOPERANDS; + case 0x3b: return DRC_ZEROOPERANDS; + case 0x3c: return DRC_ZEROOPERANDS; + case 0x3d: return DRC_ZEROOPERANDS; + case 0x3e: return DRC_ZEROOPERANDS; + case 0x3f: return DRC_ZEROOPERANDS; + case 0x40: return DRC_ZEROOPERANDS; + case 0x41: return DRC_ZEROOPERANDS; + case 0x42: return DRC_ZEROOPERANDS; + case 0x43: return DRC_ZEROOPERANDS; + case 0x44: return DRC_ZEROOPERANDS; + case 0x45: return DRC_ZEROOPERANDS; + case 0x46: return DRC_ZEROOPERANDS; + case 0x47: return DRC_ZEROOPERANDS; + case 0x48: return DRC_ZEROOPERANDS; + case 0x49: return DRC_ZEROOPERANDS; + case 0x4a: return DRC_ZEROOPERANDS; + case 0x4b: return DRC_ZEROOPERANDS; + case 0x4c: return DRC_ZEROOPERANDS; + case 0x4d: return DRC_ZEROOPERANDS; + case 0x4e: return DRC_ZEROOPERANDS; + case 0x4f: return DRC_ZEROOPERANDS; + case 0x50: return DRC_ZEROOPERANDS; + case 0x51: return DRC_ZEROOPERANDS; + case 0x52: return DRC_ZEROOPERANDS; + case 0x53: return DRC_ZEROOPERANDS; + case 0x54: return DRC_ZEROOPERANDS; + case 0x55: return DRC_ZEROOPERANDS; + case 0x56: return DRC_ZEROOPERANDS; + case 0x57: return DRC_ZEROOPERANDS; + case 0x58: return DRC_ZEROOPERANDS; + case 0x59: return DRC_ZEROOPERANDS; + case 0x5a: return DRC_ZEROOPERANDS; + case 0x5b: return DRC_ZEROOPERANDS; + case 0x5c: return DRC_ZEROOPERANDS; + case 0x5d: return DRC_ZEROOPERANDS; + case 0x5e: return DRC_ZEROOPERANDS; + case 0x5f: return DRC_ZEROOPERANDS; + case 0x60: return DRC_ZEROOPERANDS; + case 0x61: return DRC_ZEROOPERANDS; + case 0x62: return DRC_ZEROOPERANDS; + case 0x63: return DRC_ZEROOPERANDS; + case 0x64: return DRC_ZEROOPERANDS; + case 0x65: return DRC_ZEROOPERANDS; + case 0x66: return DRC_ZEROOPERANDS; + case 0x67: return DRC_ZEROOPERANDS; + case 0x68: return DRC_ZEROOPERANDS; + case 0x69: return DRC_ZEROOPERANDS; + case 0x6a: return DRC_ZEROOPERANDS; + case 0x6b: return DRC_ZEROOPERANDS; + case 0x6c: return DRC_ZEROOPERANDS; + case 0x6d: return DRC_ZEROOPERANDS; + case 0x6e: return DRC_ZEROOPERANDS; + case 0x6f: return DRC_ZEROOPERANDS; + case 0x70: return DRC_ONEOPERAND; + case 0x71: return DRC_ONEOPERAND; + case 0x72: return DRC_ONEOPERAND; + case 0x73: return DRC_ONEOPERAND; + case 0x74: return DRC_ONEOPERAND; + case 0x75: return DRC_ONEOPERAND; + case 0x76: return DRC_ONEOPERAND; + case 0x77: return DRC_ONEOPERAND; + case 0x78: return DRC_ONEOPERAND; + case 0x79: return DRC_ONEOPERAND; + case 0x7a: return DRC_ONEOPERAND; + case 0x7b: return DRC_ONEOPERAND; + case 0x7c: return DRC_ONEOPERAND; + case 0x7d: return DRC_ONEOPERAND; + case 0x7e: return DRC_ONEOPERAND; + case 0x7f: return DRC_ONEOPERAND; + case 0x80: return DRC_ONEOPERAND; + case 0x81: return DRC_ONEOPERAND; + case 0x82: return DRC_ONEOPERAND; + case 0x83: return DRC_ONEOPERAND; + case 0x84: return DRC_ONEOPERAND; + case 0x85: return DRC_ONEOPERAND; + case 0x86: return DRC_ONEOPERAND; + case 0x87: return DRC_ONEOPERAND; + case 0x88: return DRC_ONEOPERAND; + case 0x89: return DRC_ONEOPERAND; + case 0x8a: return DRC_ONEOPERAND; + case 0x8b: return DRC_ONEOPERAND; + case 0x8c: return DRC_ONEOPERAND; + case 0x8d: return DRC_ONEOPERAND; + case 0x8e: return DRC_ONEOPERAND; + case 0x8f: return DRC_ONEOPERAND; + case 0x90: return DRC_ONEOPERAND; + case 0x91: return DRC_ONEOPERAND; + case 0x92: return DRC_TWOOPERANDS; + case 0x93: return DRC_ONEOPERAND; + case 0x94: return DRC_ONEOPERAND; + case 0x95: return DRC_ONEOPERAND; + case 0x96: return DRC_ZEROOPERANDS; + case 0x97: return DRC_DWARFv3 | DRC_ZEROOPERANDS; + case 0x98: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0x99: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0x9a: return DRC_DWARFv3 | DRC_ONEOPERAND; + case 0xf0: return DRC_ZEROOPERANDS; /* DW_OP_APPLE_uninit */ + case 0xe0: return 0; + case 0xff: return 0; + default: return 0; + } +} + +const char * +DW_ATE_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::AttributeEncodingString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_ATE constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_ACCESS_value_to_name (uint32_t val) +{ + static char invalid[100]; + + const char *llvmstr = llvm::dwarf::AccessibilityString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_ACCESS constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_VIS_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::VisibilityString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_VIS constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_VIRTUALITY_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::VirtualityString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_VIRTUALITY constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_LANG_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::LanguageString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_LANG constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_ID_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::CaseString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_ID constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_CC_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::ConventionString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_CC constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_INL_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::InlineCodeString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_INL constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_ORD_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::ArrayOrderString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_ORD constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_DSC_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::DiscriminantString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_DSC constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_LNS_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::LNStandardString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_LNS constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_LNE_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::LNExtendedString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_LNE constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_MACINFO_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::MacinfoString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_MACINFO constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +const char * +DW_CFA_value_to_name (uint32_t val) +{ + static char invalid[100]; + const char *llvmstr = llvm::dwarf::CallFrameString (val); + if (llvmstr == NULL) + { + snprintf (invalid, sizeof (invalid), "Unknown DW_CFA constant: 0x%x", val); + return invalid; + } + return llvmstr; +} + +DW_TAG_CategoryEnum +get_tag_category (uint16_t tag) +{ + switch (tag) + { + case DW_TAG_array_type : return TagCategoryType; + case DW_TAG_class_type : return TagCategoryType; + case DW_TAG_entry_point : return TagCategoryProgram; + case DW_TAG_enumeration_type : return TagCategoryType; + case DW_TAG_formal_parameter : return TagCategoryVariable; + case DW_TAG_imported_declaration : return TagCategoryProgram; + case DW_TAG_label : return TagCategoryProgram; + case DW_TAG_lexical_block : return TagCategoryProgram; + case DW_TAG_member : return TagCategoryType; + case DW_TAG_pointer_type : return TagCategoryType; + case DW_TAG_reference_type : return TagCategoryType; + case DW_TAG_compile_unit : return TagCategoryProgram; + case DW_TAG_string_type : return TagCategoryType; + case DW_TAG_structure_type : return TagCategoryType; + case DW_TAG_subroutine_type : return TagCategoryType; + case DW_TAG_typedef : return TagCategoryType; + case DW_TAG_union_type : return TagCategoryType; + case DW_TAG_unspecified_parameters : return TagCategoryVariable; + case DW_TAG_variant : return TagCategoryType; + case DW_TAG_common_block : return TagCategoryProgram; + case DW_TAG_common_inclusion : return TagCategoryProgram; + case DW_TAG_inheritance : return TagCategoryType; + case DW_TAG_inlined_subroutine : return TagCategoryProgram; + case DW_TAG_module : return TagCategoryProgram; + case DW_TAG_ptr_to_member_type : return TagCategoryType; + case DW_TAG_set_type : return TagCategoryType; + case DW_TAG_subrange_type : return TagCategoryType; + case DW_TAG_with_stmt : return TagCategoryProgram; + case DW_TAG_access_declaration : return TagCategoryProgram; + case DW_TAG_base_type : return TagCategoryType; + case DW_TAG_catch_block : return TagCategoryProgram; + case DW_TAG_const_type : return TagCategoryType; + case DW_TAG_constant : return TagCategoryVariable; + case DW_TAG_enumerator : return TagCategoryType; + case DW_TAG_file_type : return TagCategoryType; + case DW_TAG_friend : return TagCategoryType; + case DW_TAG_namelist : return TagCategoryVariable; + case DW_TAG_namelist_item : return TagCategoryVariable; + case DW_TAG_packed_type : return TagCategoryType; + case DW_TAG_subprogram : return TagCategoryProgram; + case DW_TAG_template_type_parameter : return TagCategoryType; + case DW_TAG_template_value_parameter : return TagCategoryType; + case DW_TAG_thrown_type : return TagCategoryType; + case DW_TAG_try_block : return TagCategoryProgram; + case DW_TAG_variant_part : return TagCategoryType; + case DW_TAG_variable : return TagCategoryVariable; + case DW_TAG_volatile_type : return TagCategoryType; + case DW_TAG_dwarf_procedure : return TagCategoryProgram; + case DW_TAG_restrict_type : return TagCategoryType; + case DW_TAG_interface_type : return TagCategoryType; + case DW_TAG_namespace : return TagCategoryProgram; + case DW_TAG_imported_module : return TagCategoryProgram; + case DW_TAG_unspecified_type : return TagCategoryType; + case DW_TAG_partial_unit : return TagCategoryProgram; + case DW_TAG_imported_unit : return TagCategoryProgram; + case DW_TAG_shared_type : return TagCategoryType; + default: break; + } + return TagCategoryProgram; +} + +} // namespace lldb_private + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDefines.h b/source/Plugins/SymbolFile/DWARF/DWARFDefines.h new file mode 100644 index 000000000000..e37aefb27a72 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFDefines.h @@ -0,0 +1,116 @@ +//===-- DWARFDefines.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFDefines_h_ +#define SymbolFileDWARF_DWARFDefines_h_ + +#include <stdint.h> +#include <stdbool.h> +#include "lldb/Core/dwarf.h" + +namespace lldb_private { + +typedef uint32_t DRC_class; // Holds DRC_* class bitfields + +enum DW_TAG_Category +{ + TagCategoryVariable, + TagCategoryType, + TagCategoryProgram, + kNumTagCategories +}; + +typedef enum DW_TAG_Category DW_TAG_CategoryEnum; + +const char *DW_TAG_value_to_name (uint32_t val); + +DW_TAG_CategoryEnum get_tag_category (uint16_t tag); + +const char *DW_CHILDREN_value_to_name (uint8_t val); + +const char *DW_AT_value_to_name (uint32_t val); + +const char *DW_FORM_value_to_name (uint32_t val); + +const char *DW_OP_value_to_name (uint32_t val); + +DRC_class DW_OP_value_to_class (uint32_t val); + +const char *DW_ATE_value_to_name (uint32_t val); + +const char *DW_ACCESS_value_to_name (uint32_t val); + +const char *DW_VIS_value_to_name (uint32_t val); + +const char *DW_VIRTUALITY_value_to_name (uint32_t val); + +const char *DW_LANG_value_to_name (uint32_t val); + +const char *DW_ID_value_to_name (uint32_t val); + +const char *DW_CC_value_to_name (uint32_t val); + +const char *DW_INL_value_to_name (uint32_t val); + +const char *DW_ORD_value_to_name (uint32_t val); + +const char *DW_DSC_value_to_name (uint32_t val); + +const char *DW_LNS_value_to_name (uint32_t val); + +const char *DW_LNE_value_to_name (uint32_t val); + +const char *DW_MACINFO_value_to_name (uint32_t val); + +const char *DW_CFA_value_to_name (uint32_t val); + +const char *DW_GNU_EH_PE_value_to_name (uint32_t val); + +/* These DRC are entirely our own construction, + although they are derived from various comments in the DWARF standard. + Most of these are not useful to the parser, but the DW_AT and DW_FORM + classes should prove to be usable in some fashion. */ + +#define DRC_0x65 0x1 +#define DRC_ADDRESS 0x2 +#define DRC_BLOCK 0x4 +#define DRC_CONSTANT 0x8 +#define DRC_DWARFv3 0x10 +#define DRC_FLAG 0x20 +#define DRC_INDIRECT_SPECIAL 0x40 +#define DRC_LINEPTR 0x80 +#define DRC_LOCEXPR 0x100 +#define DRC_LOCLISTPTR 0x200 +#define DRC_MACPTR 0x400 +#define DRC_ONEOPERAND 0x800 +#define DRC_OPERANDONE_1BYTE_DELTA 0x1000 +#define DRC_OPERANDONE_2BYTE_DELTA 0x2000 +#define DRC_OPERANDONE_4BYTE_DELTA 0x4000 +#define DRC_OPERANDONE_ADDRESS 0x8000 +#define DRC_OPERANDONE_BLOCK 0x10000 +#define DRC_OPERANDONE_SLEB128_OFFSET 0x20000 +#define DRC_OPERANDONE_ULEB128_OFFSET 0x40000 +#define DRC_OPERANDONE_ULEB128_REGISTER 0x80000 +#define DRC_OPERANDTWO_BLOCK 0x100000 +#define DRC_OPERANDTWO_SLEB128_OFFSET 0x200000 +#define DRC_OPERANDTWO_ULEB128_OFFSET 0x400000 +#define DRC_OPERANDTWO_ULEB128_REGISTER 0x800000 +#define DRC_OPERNADONE_ULEB128_REGISTER 0x1000000 +#define DRC_RANGELISTPTR 0x2000000 +#define DRC_REFERENCE 0x4000000 +#define DRC_STRING 0x8000000 +#define DRC_TWOOPERANDS 0x10000000 +#define DRC_VENDOR_GNU 0x20000000 +#define DRC_VENDOR_MIPS 0x40000000 +#define DRC_ZEROOPERANDS 0x80000000 + +} // namespace lldb_private + + +#endif // SymbolFileDWARF_DWARFDefines_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp new file mode 100644 index 000000000000..6113a146c93d --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -0,0 +1,599 @@ +//===-- DWARFFormValue.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <assert.h> + +#include "lldb/Core/dwarf.h" +#include "lldb/Core/Stream.h" + +#include "DWARFFormValue.h" +#include "DWARFCompileUnit.h" + +class DWARFCompileUnit; + +using namespace lldb_private; + + +static uint8_t g_form_sizes_addr4[] = +{ + 0, // 0x00 unused + 4, // 0x01 DW_FORM_addr + 0, // 0x02 unused + 0, // 0x03 DW_FORM_block2 + 0, // 0x04 DW_FORM_block4 + 2, // 0x05 DW_FORM_data2 + 4, // 0x06 DW_FORM_data4 + 8, // 0x07 DW_FORM_data8 + 0, // 0x08 DW_FORM_string + 0, // 0x09 DW_FORM_block + 0, // 0x0a DW_FORM_block1 + 1, // 0x0b DW_FORM_data1 + 1, // 0x0c DW_FORM_flag + 0, // 0x0d DW_FORM_sdata + 4, // 0x0e DW_FORM_strp + 0, // 0x0f DW_FORM_udata + 0, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for DWARF32, 8 bytes for DWARF32 in DWARF 3 and later + 1, // 0x11 DW_FORM_ref1 + 2, // 0x12 DW_FORM_ref2 + 4, // 0x13 DW_FORM_ref4 + 8, // 0x14 DW_FORM_ref8 + 0, // 0x15 DW_FORM_ref_udata + 0, // 0x16 DW_FORM_indirect + 4, // 0x17 DW_FORM_sec_offset + 0, // 0x18 DW_FORM_exprloc + 0, // 0x19 DW_FORM_flag_present + 0, // 0x1a + 0, // 0x1b + 0, // 0x1c + 0, // 0x1d + 0, // 0x1e + 0, // 0x1f + 8, // 0x20 DW_FORM_ref_sig8 + +}; + +static uint8_t +g_form_sizes_addr8[] = +{ + 0, // 0x00 unused + 8, // 0x01 DW_FORM_addr + 0, // 0x02 unused + 0, // 0x03 DW_FORM_block2 + 0, // 0x04 DW_FORM_block4 + 2, // 0x05 DW_FORM_data2 + 4, // 0x06 DW_FORM_data4 + 8, // 0x07 DW_FORM_data8 + 0, // 0x08 DW_FORM_string + 0, // 0x09 DW_FORM_block + 0, // 0x0a DW_FORM_block1 + 1, // 0x0b DW_FORM_data1 + 1, // 0x0c DW_FORM_flag + 0, // 0x0d DW_FORM_sdata + 4, // 0x0e DW_FORM_strp + 0, // 0x0f DW_FORM_udata + 0, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for DWARF32, 8 bytes for DWARF32 in DWARF 3 and later + 1, // 0x11 DW_FORM_ref1 + 2, // 0x12 DW_FORM_ref2 + 4, // 0x13 DW_FORM_ref4 + 8, // 0x14 DW_FORM_ref8 + 0, // 0x15 DW_FORM_ref_udata + 0, // 0x16 DW_FORM_indirect + 4, // 0x17 DW_FORM_sec_offset + 0, // 0x18 DW_FORM_exprloc + 0, // 0x19 DW_FORM_flag_present + 0, // 0x1a + 0, // 0x1b + 0, // 0x1c + 0, // 0x1d + 0, // 0x1e + 0, // 0x1f + 8, // 0x20 DW_FORM_ref_sig8 +}; + +const uint8_t * +DWARFFormValue::GetFixedFormSizesForAddressSize (uint8_t addr_size) +{ + switch (addr_size) + { + case 4: return g_form_sizes_addr4; + case 8: return g_form_sizes_addr8; + } + return NULL; +} + +DWARFFormValue::DWARFFormValue(dw_form_t form) : + m_form(form), + m_value() +{ +} + +bool +DWARFFormValue::ExtractValue(const DataExtractor& data, lldb::offset_t* offset_ptr, const DWARFCompileUnit* cu) +{ + bool indirect = false; + bool is_block = false; + m_value.data = NULL; + // Read the value for the form into value and follow and DW_FORM_indirect instances we run into + do + { + indirect = false; + switch (m_form) + { + case DW_FORM_addr: m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(cu)); break; + case DW_FORM_block2: m_value.value.uval = data.GetU16(offset_ptr); is_block = true; break; + case DW_FORM_block4: m_value.value.uval = data.GetU32(offset_ptr); is_block = true; break; + case DW_FORM_data2: m_value.value.uval = data.GetU16(offset_ptr); break; + case DW_FORM_data4: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_data8: m_value.value.uval = data.GetU64(offset_ptr); break; + case DW_FORM_string: m_value.value.cstr = data.GetCStr(offset_ptr); + // Set the string value to also be the data for inlined cstr form values only + // so we can tell the differnence between DW_FORM_string and DW_FORM_strp form + // values; + m_value.data = (uint8_t*)m_value.value.cstr; break; + case DW_FORM_exprloc: + case DW_FORM_block: m_value.value.uval = data.GetULEB128(offset_ptr); is_block = true; break; + case DW_FORM_block1: m_value.value.uval = data.GetU8(offset_ptr); is_block = true; break; + case DW_FORM_data1: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_flag: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_sdata: m_value.value.sval = data.GetSLEB128(offset_ptr); break; + case DW_FORM_strp: m_value.value.uval = data.GetU32(offset_ptr); break; + // case DW_FORM_APPLE_db_str: + case DW_FORM_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; + case DW_FORM_ref_addr: + if (cu->GetVersion() <= 2) + m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(cu)); + else + m_value.value.uval = data.GetU32(offset_ptr); // 4 for DWARF32, 8 for DWARF64, but we don't support DWARF64 yet + break; + case DW_FORM_ref1: m_value.value.uval = data.GetU8(offset_ptr); break; + case DW_FORM_ref2: m_value.value.uval = data.GetU16(offset_ptr); break; + case DW_FORM_ref4: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_ref8: m_value.value.uval = data.GetU64(offset_ptr); break; + case DW_FORM_ref_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; + case DW_FORM_indirect: + m_form = data.GetULEB128(offset_ptr); + indirect = true; + break; + + case DW_FORM_sec_offset: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_flag_present: m_value.value.uval = 1; break; + case DW_FORM_ref_sig8: m_value.value.uval = data.GetU64(offset_ptr); break; + default: + return false; + break; + } + } while (indirect); + + if (is_block) + { + m_value.data = data.PeekData(*offset_ptr, m_value.value.uval); + if (m_value.data != NULL) + { + *offset_ptr += m_value.value.uval; + } + } + + return true; +} + +bool +DWARFFormValue::SkipValue(const DataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu) const +{ + return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, cu); +} + +bool +DWARFFormValue::SkipValue(dw_form_t form, const DataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu) +{ + switch (form) + { + // Blocks if inlined data that have a length field and the data bytes + // inlined in the .debug_info + case DW_FORM_exprloc: + case DW_FORM_block: { dw_uleb128_t size = debug_info_data.GetULEB128(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block1: { dw_uleb128_t size = debug_info_data.GetU8(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block2: { dw_uleb128_t size = debug_info_data.GetU16(offset_ptr); *offset_ptr += size; } return true; + case DW_FORM_block4: { dw_uleb128_t size = debug_info_data.GetU32(offset_ptr); *offset_ptr += size; } return true; + + // Inlined NULL terminated C-strings + case DW_FORM_string: + debug_info_data.GetCStr(offset_ptr); + return true; + + // Compile unit address sized values + case DW_FORM_addr: + *offset_ptr += DWARFCompileUnit::GetAddressByteSize(cu); + return true; + + case DW_FORM_ref_addr: + if (cu->GetVersion() <= 2) + *offset_ptr += DWARFCompileUnit::GetAddressByteSize(cu); + else + *offset_ptr += 4;// 4 for DWARF32, 8 for DWARF64, but we don't support DWARF64 yet + return true; + + // 0 bytes values (implied from DW_FORM) + case DW_FORM_flag_present: + return true; + + // 1 byte values + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + *offset_ptr += 1; + return true; + + // 2 byte values + case DW_FORM_data2: + case DW_FORM_ref2: + *offset_ptr += 2; + return true; + + // 32 bit for DWARF 32, 64 for DWARF 64 + case DW_FORM_sec_offset: + *offset_ptr += 4; + return true; + + // 4 byte values + case DW_FORM_strp: + case DW_FORM_data4: + case DW_FORM_ref4: + *offset_ptr += 4; + return true; + + // 8 byte values + case DW_FORM_data8: + case DW_FORM_ref8: + case DW_FORM_ref_sig8: + *offset_ptr += 8; + return true; + + // signed or unsigned LEB 128 values + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + debug_info_data.Skip_LEB128(offset_ptr); + return true; + + case DW_FORM_indirect: + { + dw_form_t indirect_form = debug_info_data.GetULEB128(offset_ptr); + return DWARFFormValue::SkipValue (indirect_form, + debug_info_data, + offset_ptr, + cu); + } + + default: + break; + } + return false; +} + + +void +DWARFFormValue::Dump(Stream &s, const DataExtractor* debug_str_data, const DWARFCompileUnit* cu) const +{ + uint64_t uvalue = Unsigned(); + bool cu_relative_offset = false; + + bool verbose = s.GetVerbose(); + + switch (m_form) + { + case DW_FORM_addr: s.Address(uvalue, sizeof (uint64_t)); break; + case DW_FORM_flag: + case DW_FORM_data1: s.PutHex8(uvalue); break; + case DW_FORM_data2: s.PutHex16(uvalue); break; + case DW_FORM_sec_offset: + case DW_FORM_data4: s.PutHex32(uvalue); break; + case DW_FORM_ref_sig8: + case DW_FORM_data8: s.PutHex64(uvalue); break; + case DW_FORM_string: s.QuotedCString(AsCString(NULL)); break; + case DW_FORM_exprloc: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + if (uvalue > 0) + { + switch (m_form) + { + case DW_FORM_exprloc: + case DW_FORM_block: s.Printf("<0x%" PRIx64 "> ", uvalue); break; + case DW_FORM_block1: s.Printf("<0x%2.2x> ", (uint8_t)uvalue); break; + case DW_FORM_block2: s.Printf("<0x%4.4x> ", (uint16_t)uvalue); break; + case DW_FORM_block4: s.Printf("<0x%8.8x> ", (uint32_t)uvalue); break; + default: break; + } + + const uint8_t* data_ptr = m_value.data; + if (data_ptr) + { + const uint8_t* end_data_ptr = data_ptr + uvalue; // uvalue contains size of block + while (data_ptr < end_data_ptr) + { + s.Printf("%2.2x ", *data_ptr); + ++data_ptr; + } + } + else + s.PutCString("NULL"); + } + break; + + case DW_FORM_sdata: s.PutSLEB128(uvalue); break; + case DW_FORM_udata: s.PutULEB128(uvalue); break; + case DW_FORM_strp: + if (debug_str_data) + { + if (verbose) + s.Printf(" .debug_str[0x%8.8x] = ", (uint32_t)uvalue); + + const char* dbg_str = AsCString(debug_str_data); + if (dbg_str) + s.QuotedCString(dbg_str); + } + else + { + s.PutHex32(uvalue); + } + break; + + case DW_FORM_ref_addr: + { + if (cu->GetVersion() <= 2) + s.Address(uvalue, sizeof (uint64_t) * 2); + else + s.Address(uvalue, 4 * 2);// 4 for DWARF32, 8 for DWARF64, but we don't support DWARF64 yet + break; + } + case DW_FORM_ref1: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%2.2x", (uint8_t)uvalue); break; + case DW_FORM_ref2: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%4.4x", (uint16_t)uvalue); break; + case DW_FORM_ref4: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%4.4x", (uint32_t)uvalue); break; + case DW_FORM_ref8: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%8.8" PRIx64, uvalue); break; + case DW_FORM_ref_udata: cu_relative_offset = true; if (verbose) s.Printf("cu + 0x%" PRIx64, uvalue); break; + + // All DW_FORM_indirect attributes should be resolved prior to calling this function + case DW_FORM_indirect: s.PutCString("DW_FORM_indirect"); break; + case DW_FORM_flag_present: break; + default: + s.Printf("DW_FORM(0x%4.4x)", m_form); + break; + } + + if (cu_relative_offset) + { + if (verbose) + s.PutCString(" => "); + + s.Printf("{0x%8.8" PRIx64 "}", (uvalue + (cu ? cu->GetOffset() : 0))); + } +} + +const char* +DWARFFormValue::AsCString(const DataExtractor* debug_str_data_ptr) const +{ + if (IsInlinedCStr()) + return m_value.value.cstr; + else if (debug_str_data_ptr) + return debug_str_data_ptr->PeekCStr(m_value.value.uval); + return NULL; +} + +uint64_t +DWARFFormValue::Reference(const DWARFCompileUnit* cu) const +{ + uint64_t die_offset = m_value.value.uval; + switch (m_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + die_offset += (cu ? cu->GetOffset() : 0); + break; + + default: + break; + } + + return die_offset; +} + +uint64_t +DWARFFormValue::Reference (dw_offset_t base_offset) const +{ + uint64_t die_offset = m_value.value.uval; + switch (m_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + die_offset += base_offset; + break; + + default: + break; + } + + return die_offset; +} + +//---------------------------------------------------------------------- +// Resolve any compile unit specific references so that we don't need +// the compile unit at a later time in order to work with the form +// value. +//---------------------------------------------------------------------- +bool +DWARFFormValue::ResolveCompileUnitReferences(const DWARFCompileUnit* cu) +{ + switch (m_form) + { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + m_value.value.uval += cu->GetOffset(); + m_form = DW_FORM_ref_addr; + return true; + break; + + default: + break; + } + + return false; +} + +const uint8_t* +DWARFFormValue::BlockData() const +{ + if (!IsInlinedCStr()) + return m_value.data; + return NULL; +} + + +bool +DWARFFormValue::IsBlockForm(const dw_form_t form) +{ + switch (form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + return true; + } + return false; +} + +bool +DWARFFormValue::IsDataForm(const dw_form_t form) +{ + switch (form) + { + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + return true; + } + return false; +} + +int +DWARFFormValue::Compare (const DWARFFormValue& a_value, const DWARFFormValue& b_value, const DWARFCompileUnit* a_cu, const DWARFCompileUnit* b_cu, const DataExtractor* debug_str_data_ptr) +{ + dw_form_t a_form = a_value.Form(); + dw_form_t b_form = b_value.Form(); + if (a_form < b_form) + return -1; + if (a_form > b_form) + return 1; + switch (a_form) + { + case DW_FORM_addr: + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_udata: + case DW_FORM_ref_addr: + case DW_FORM_sec_offset: + case DW_FORM_flag_present: + case DW_FORM_ref_sig8: + { + uint64_t a = a_value.Unsigned(); + uint64_t b = b_value.Unsigned(); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_sdata: + { + int64_t a = a_value.Signed(); + int64_t b = b_value.Signed(); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_string: + case DW_FORM_strp: + { + const char *a_string = a_value.AsCString(debug_str_data_ptr); + const char *b_string = b_value.AsCString(debug_str_data_ptr); + if (a_string == b_string) + return 0; + else if (a_string && b_string) + return strcmp(a_string, b_string); + else if (a_string == NULL) + return -1; // A string is NULL, and B is valid + else + return 1; // A string valid, and B is NULL + } + + + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_exprloc: + { + uint64_t a_len = a_value.Unsigned(); + uint64_t b_len = b_value.Unsigned(); + if (a_len < b_len) + return -1; + if (a_len > b_len) + return 1; + // The block lengths are the same + return memcmp(a_value.BlockData(), b_value.BlockData(), a_value.Unsigned()); + } + break; + + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + { + uint64_t a = a_value.Reference(a_cu); + uint64_t b = b_value.Reference(b_cu); + if (a < b) + return -1; + if (a > b) + return 1; + return 0; + } + + case DW_FORM_indirect: + assert(!"This shouldn't happen after the form has been extracted..."); + break; + + default: + assert(!"Unhandled DW_FORM"); + break; + } + return -1; +} + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h new file mode 100644 index 000000000000..4c400b2df58e --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h @@ -0,0 +1,81 @@ +//===-- DWARFFormValue.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFFormValue_h_ +#define SymbolFileDWARF_DWARFFormValue_h_ + +#include <stddef.h> // for NULL + +class DWARFCompileUnit; + +class DWARFFormValue +{ +public: + typedef struct ValueTypeTag + { + ValueTypeTag() : + value(), + data(NULL) + { + value.uval = 0; + } + + union + { + uint64_t uval; + int64_t sval; + const char* cstr; + } value; + const uint8_t* data; + } ValueType; + + enum + { + eValueTypeInvalid = 0, + eValueTypeUnsigned, + eValueTypeSigned, + eValueTypeCStr, + eValueTypeBlock + }; + + DWARFFormValue(dw_form_t form = 0); + dw_form_t Form() const { return m_form; } + void SetForm(dw_form_t form) { m_form = form; } + const ValueType& Value() const { return m_value; } + void Dump(lldb_private::Stream &s, const lldb_private::DataExtractor* debug_str_data, const DWARFCompileUnit* cu) const; + bool ExtractValue(const lldb_private::DataExtractor& data, + lldb::offset_t* offset_ptr, + const DWARFCompileUnit* cu); + bool IsInlinedCStr() const { return (m_value.data != NULL) && m_value.data == (uint8_t*)m_value.value.cstr; } + const uint8_t* BlockData() const; + uint64_t Reference(const DWARFCompileUnit* cu) const; + uint64_t Reference (dw_offset_t offset) const; + bool ResolveCompileUnitReferences(const DWARFCompileUnit* cu); + bool Boolean() const { return m_value.value.uval != 0; } + uint64_t Unsigned() const { return m_value.value.uval; } + void SetUnsigned(uint64_t uval) { m_value.value.uval = uval; } + int64_t Signed() const { return m_value.value.sval; } + void SetSigned(int64_t sval) { m_value.value.sval = sval; } + const char* AsCString(const lldb_private::DataExtractor* debug_str_data_ptr) const; + bool SkipValue(const lldb_private::DataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu) const; + static bool SkipValue(const dw_form_t form, const lldb_private::DataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu); +// static bool TransferValue(dw_form_t form, const lldb_private::DataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff); +// static bool TransferValue(const DWARFFormValue& formValue, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff); +// static bool PutUnsigned(dw_form_t form, dw_offset_t offset, uint64_t value, BinaryStreamBuf& out_buff, const DWARFCompileUnit* cu, bool fixup_cu_relative_refs); + static bool IsBlockForm(const dw_form_t form); + static bool IsDataForm(const dw_form_t form); + static const uint8_t * GetFixedFormSizesForAddressSize (uint8_t addr_size); + static int Compare (const DWARFFormValue& a, const DWARFFormValue& b, const DWARFCompileUnit* a_cu, const DWARFCompileUnit* b_cu, const lldb_private::DataExtractor* debug_str_data_ptr); +protected: + dw_form_t m_form; // Form for this value + ValueType m_value; // Contains all data for the form +}; + + +#endif // SymbolFileDWARF_DWARFFormValue_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp b/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp new file mode 100644 index 000000000000..fdc07836b88d --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp @@ -0,0 +1,172 @@ +//===-- DWARFLocationDescription.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFLocationDescription.h" +#include "DWARFDefines.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/Stream.h" + + +using namespace lldb_private; + +static int print_dwarf_exp_op (Stream &s, const DataExtractor& data, lldb::offset_t *offset_ptr, int address_size, int dwarf_ref_size); + +int +print_dwarf_expression (Stream &s, + const DataExtractor& data, + int address_size, + int dwarf_ref_size, + bool location_expression) +{ + int op_count = 0; + lldb::offset_t offset = 0; + while (data.ValidOffset(offset)) + { + if (location_expression && op_count > 0) + { + // err (baton, "Dwarf location expressions may only have one operand!"); + return 1; + } + if (op_count > 0) + { + s.PutCString(", "); + } + if (print_dwarf_exp_op (s, data, &offset, address_size, dwarf_ref_size) == 1) + return 1; + op_count++; + } + + return 0; +} + +static int +print_dwarf_exp_op (Stream &s, + const DataExtractor& data, + lldb::offset_t *offset_ptr, + int address_size, + int dwarf_ref_size) +{ + uint8_t opcode = data.GetU8(offset_ptr); + DRC_class opcode_class; + uint64_t uint; + int64_t sint; + + int size; + + opcode_class = DW_OP_value_to_class (opcode) & (~DRC_DWARFv3); + + s.Printf("%s ", DW_OP_value_to_name (opcode)); + + /* Does this take zero parameters? If so we can shortcut this function. */ + if (opcode_class == DRC_ZEROOPERANDS) + return 0; + + if (opcode_class == DRC_TWOOPERANDS && opcode == DW_OP_bregx) + { + uint = data.GetULEB128(offset_ptr); + sint = data.GetSLEB128(offset_ptr); + s.Printf("%" PRIu64 " %" PRIi64, uint, sint); + return 0; + } + if (opcode_class != DRC_ONEOPERAND) + { + s.Printf("UNKNOWN OP %u", opcode); + return 1; + } + + switch (opcode) + { + case DW_OP_addr: size = address_size; break; + case DW_OP_const1u: size = 1; break; + case DW_OP_const1s: size = -1; break; + case DW_OP_const2u: size = 2; break; + case DW_OP_const2s: size = -2; break; + case DW_OP_const4u: size = 4; break; + case DW_OP_const4s: size = -4; break; + case DW_OP_const8u: size = 8; break; + case DW_OP_const8s: size = -8; break; + case DW_OP_constu: size = 128; break; + case DW_OP_consts: size = -128; break; + case DW_OP_fbreg: size = -128; break; + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + size = -128; break; + case DW_OP_pick: + size = 1; break; + case DW_OP_deref_size: + size = 1; break; + case DW_OP_xderef_size: + size = 1; break; + case DW_OP_plus_uconst: + size = 128; break; + case DW_OP_skip: + size = -2; break; + case DW_OP_bra: + size = -2; break; + case DW_OP_call2: + size = 2; break; + case DW_OP_call4: + size = 4; break; + case DW_OP_call_ref: + size = dwarf_ref_size; break; + case DW_OP_piece: + size = 128; break; + case DW_OP_regx: + size = 128; break; + default: + s.Printf("UNKNOWN ONE-OPERAND OPCODE, #%u", opcode); + return 1; + } + + switch (size) + { + case -1: sint = (int8_t) data.GetU8(offset_ptr); s.Printf("%+" PRIi64, sint); break; + case -2: sint = (int16_t) data.GetU16(offset_ptr); s.Printf("%+" PRIi64, sint); break; + case -4: sint = (int32_t) data.GetU32(offset_ptr); s.Printf("%+" PRIi64, sint); break; + case -8: sint = (int64_t) data.GetU64(offset_ptr); s.Printf("%+" PRIi64, sint); break; + case -128: sint = data.GetSLEB128(offset_ptr); s.Printf("%+" PRIi64, sint); break; + case 1: uint = data.GetU8(offset_ptr); s.Printf("0x%2.2" PRIx64, uint); break; + case 2: uint = data.GetU16(offset_ptr); s.Printf("0x%4.4" PRIx64, uint); break; + case 4: uint = data.GetU32(offset_ptr); s.Printf("0x%8.8" PRIx64, uint); break; + case 8: uint = data.GetU64(offset_ptr); s.Printf("0x%16.16" PRIx64, uint); break; + case 128: uint = data.GetULEB128(offset_ptr); s.Printf("0x%" PRIx64, uint); break; + } + + return 0; +} diff --git a/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h b/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h new file mode 100644 index 000000000000..ff7c907e921c --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h @@ -0,0 +1,24 @@ +//===-- DWARFLocationDescription.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFLocationDescription_h_ +#define SymbolFileDWARF_DWARFLocationDescription_h_ + +#include "SymbolFileDWARF.h" + +int +print_dwarf_expression (lldb_private::Stream &s, + const lldb_private::DataExtractor& data, + int address_size, + int dwarf_ref_size, + bool location_expression); + + + +#endif // SymbolFileDWARF_DWARFLocationDescription_h_ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp b/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp new file mode 100644 index 000000000000..dad5691267df --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp @@ -0,0 +1,94 @@ +//===-- DWARFLocationList.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DWARFLocationList.h" + +#include "lldb/Core/Stream.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugInfo.h" +#include "DWARFLocationDescription.h" + +using namespace lldb_private; + +dw_offset_t +DWARFLocationList::Dump(Stream &s, const DWARFCompileUnit* cu, const DataExtractor& debug_loc_data, lldb::offset_t offset) +{ + uint64_t start_addr, end_addr; + uint32_t addr_size = DWARFCompileUnit::GetAddressByteSize(cu); + s.SetAddressByteSize(DWARFCompileUnit::GetAddressByteSize(cu)); + dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0; + while (debug_loc_data.ValidOffset(offset)) + { + start_addr = debug_loc_data.GetMaxU64(&offset,addr_size); + end_addr = debug_loc_data.GetMaxU64(&offset,addr_size); + + if (start_addr == 0 && end_addr == 0) + break; + + s.PutCString("\n "); + s.Indent(); + if (cu) + s.AddressRange (start_addr + base_addr, + end_addr + base_addr, + cu->GetAddressByteSize(), + NULL, + ": "); + uint32_t loc_length = debug_loc_data.GetU16(&offset); + + DataExtractor locationData(debug_loc_data, offset, loc_length); + // if ( dump_flags & DWARFDebugInfo::eDumpFlag_Verbose ) *ostrm_ptr << " ( "; + print_dwarf_expression (s, locationData, addr_size, 4, false); + offset += loc_length; + } + + return offset; +} + +bool +DWARFLocationList::Extract(const DataExtractor& debug_loc_data, lldb::offset_t* offset_ptr, DataExtractor& location_list_data) +{ + // Initialize with no data just in case we don't find anything + location_list_data.Clear(); + + size_t loc_list_length = Size(debug_loc_data, *offset_ptr); + if (loc_list_length > 0) + { + location_list_data.SetData(debug_loc_data, *offset_ptr, loc_list_length); + *offset_ptr += loc_list_length; + return true; + } + + return false; +} + +size_t +DWARFLocationList::Size(const DataExtractor& debug_loc_data, lldb::offset_t offset) +{ + const dw_offset_t debug_loc_offset = offset; + + while (debug_loc_data.ValidOffset(offset)) + { + dw_addr_t start_addr = debug_loc_data.GetAddress(&offset); + dw_addr_t end_addr = debug_loc_data.GetAddress(&offset); + + if (start_addr == 0 && end_addr == 0) + break; + + uint16_t loc_length = debug_loc_data.GetU16(&offset); + offset += loc_length; + } + + if (offset > debug_loc_offset) + return offset - debug_loc_offset; + return 0; +} + + + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h b/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h new file mode 100644 index 000000000000..85e11d90b36b --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/DWARFLocationList.h @@ -0,0 +1,34 @@ +//===-- DWARFLocationList.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_DWARFLocationList_h_ +#define SymbolFileDWARF_DWARFLocationList_h_ + +#include "SymbolFileDWARF.h" + +class DWARFLocationList +{ +public: + static dw_offset_t + Dump (lldb_private::Stream &s, + const DWARFCompileUnit* cu, + const lldb_private::DataExtractor& debug_loc_data, + lldb::offset_t offset); + + static bool + Extract (const lldb_private::DataExtractor& debug_loc_data, + lldb::offset_t* offset_ptr, + lldb_private::DataExtractor& location_list_data); + + static size_t + Size (const lldb_private::DataExtractor& debug_loc_data, + lldb::offset_t offset); + +}; +#endif // SymbolFileDWARF_DWARFLocationList_h_ diff --git a/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h b/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h new file mode 100644 index 000000000000..c31cbaf3ca20 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h @@ -0,0 +1,933 @@ +//===-- HashedNameToDIE.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_HashedNameToDIE_h_ +#define SymbolFileDWARF_HashedNameToDIE_h_ + +#include <vector> + +#include "DWARFDefines.h" +#include "DWARFFormValue.h" + +#include "lldb/lldb-defines.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/MappedHash.h" + + +class SymbolFileDWARF; +class DWARFCompileUnit; +class DWARFDebugInfoEntry; + +struct DWARFMappedHash +{ + struct DIEInfo + { + dw_offset_t offset; // The DIE offset + dw_tag_t tag; + uint32_t type_flags; // Any flags for this DIEInfo + uint32_t qualified_name_hash; // A 32 bit hash of the fully qualified name + + DIEInfo () : + offset (DW_INVALID_OFFSET), + tag (0), + type_flags (0), + qualified_name_hash (0) + { + } + + DIEInfo (dw_offset_t o, dw_tag_t t, uint32_t f, uint32_t h) : + offset(o), + tag (t), + type_flags (f), + qualified_name_hash (h) + { + } + + void + Clear() + { + offset = DW_INVALID_OFFSET; + tag = 0; + type_flags = 0; + qualified_name_hash = 0; + } + }; + + typedef std::vector<DIEInfo> DIEInfoArray; + typedef std::vector<uint32_t> DIEArray; + + static void + ExtractDIEArray (const DIEInfoArray &die_info_array, + DIEArray &die_offsets) + { + const size_t count = die_info_array.size(); + for (size_t i=0; i<count; ++i) + { + die_offsets.push_back (die_info_array[i].offset); + } + } + + static void + ExtractDIEArray (const DIEInfoArray &die_info_array, + const dw_tag_t tag, + DIEArray &die_offsets) + { + if (tag == 0) + { + ExtractDIEArray (die_info_array, die_offsets); + } + else + { + const size_t count = die_info_array.size(); + for (size_t i=0; i<count; ++i) + { + const dw_tag_t die_tag = die_info_array[i].tag; + bool tag_matches = die_tag == 0 || tag == die_tag; + if (!tag_matches) + { + if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) + tag_matches = tag == DW_TAG_structure_type || tag == DW_TAG_class_type; + } + if (tag_matches) + die_offsets.push_back (die_info_array[i].offset); + } + } + } + + static void + ExtractDIEArray (const DIEInfoArray &die_info_array, + const dw_tag_t tag, + const uint32_t qualified_name_hash, + DIEArray &die_offsets) + { + if (tag == 0) + { + ExtractDIEArray (die_info_array, die_offsets); + } + else + { + const size_t count = die_info_array.size(); + for (size_t i=0; i<count; ++i) + { + if (qualified_name_hash != die_info_array[i].qualified_name_hash) + continue; + const dw_tag_t die_tag = die_info_array[i].tag; + bool tag_matches = die_tag == 0 || tag == die_tag; + if (!tag_matches) + { + if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) + tag_matches = tag == DW_TAG_structure_type || tag == DW_TAG_class_type; + } + if (tag_matches) + die_offsets.push_back (die_info_array[i].offset); + } + } + } + + enum AtomType + { + eAtomTypeNULL = 0u, + eAtomTypeDIEOffset = 1u, // DIE offset, check form for encoding + eAtomTypeCUOffset = 2u, // DIE offset of the compiler unit header that contains the item in question + eAtomTypeTag = 3u, // DW_TAG_xxx value, should be encoded as DW_FORM_data1 (if no tags exceed 255) or DW_FORM_data2 + eAtomTypeNameFlags = 4u, // Flags from enum NameFlags + eAtomTypeTypeFlags = 5u, // Flags from enum TypeFlags, + eAtomTypeQualNameHash = 6u // A 32 bit hash of the full qualified name (since all hash entries are basename only) + // For example a type like "std::vector<int>::iterator" would have a name of "iterator" + // and a 32 bit hash for "std::vector<int>::iterator" to allow us to not have to pull + // in debug info for a type when we know the fully qualified name. + }; + + // Bit definitions for the eAtomTypeTypeFlags flags + enum TypeFlags + { + // Always set for C++, only set for ObjC if this is the + // @implementation for class + eTypeFlagClassIsImplementation = ( 1u << 1 ) + }; + + + static void + ExtractClassOrStructDIEArray (const DIEInfoArray &die_info_array, + bool return_implementation_only_if_available, + DIEArray &die_offsets) + { + const size_t count = die_info_array.size(); + for (size_t i=0; i<count; ++i) + { + const dw_tag_t die_tag = die_info_array[i].tag; + if (die_tag == 0 || die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) + { + if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) + { + if (return_implementation_only_if_available) + { + // We found the one true definiton for this class, so + // only return that + die_offsets.clear(); + die_offsets.push_back (die_info_array[i].offset); + return; + } + else + { + // Put the one true definition as the first entry so it + // matches first + die_offsets.insert (die_offsets.begin(), die_info_array[i].offset); + } + } + else + { + die_offsets.push_back (die_info_array[i].offset); + } + } + } + } + + static void + ExtractTypesFromDIEArray (const DIEInfoArray &die_info_array, + uint32_t type_flag_mask, + uint32_t type_flag_value, + DIEArray &die_offsets) + { + const size_t count = die_info_array.size(); + for (size_t i=0; i<count; ++i) + { + if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value) + die_offsets.push_back (die_info_array[i].offset); + } + } + + struct Atom + { + uint16_t type; + dw_form_t form; + + Atom (uint16_t t = eAtomTypeNULL, dw_form_t f = 0) : + type (t), + form (f) + { + } + }; + + typedef std::vector<Atom> AtomArray; + + static uint32_t + GetTypeFlags (SymbolFileDWARF *dwarf2Data, + const DWARFCompileUnit* cu, + const DWARFDebugInfoEntry* die); + + + static const char * + GetAtomTypeName (uint16_t atom) + { + switch (atom) + { + case eAtomTypeNULL: return "NULL"; + case eAtomTypeDIEOffset: return "die-offset"; + case eAtomTypeCUOffset: return "cu-offset"; + case eAtomTypeTag: return "die-tag"; + case eAtomTypeNameFlags: return "name-flags"; + case eAtomTypeTypeFlags: return "type-flags"; + case eAtomTypeQualNameHash: return "qualified-name-hash"; + } + return "<invalid>"; + } + struct Prologue + { + // DIE offset base so die offsets in hash_data can be CU relative + dw_offset_t die_base_offset; + AtomArray atoms; + uint32_t atom_mask; + size_t min_hash_data_byte_size; + bool hash_data_has_fixed_byte_size; + + Prologue (dw_offset_t _die_base_offset = 0) : + die_base_offset (_die_base_offset), + atoms(), + atom_mask (0), + min_hash_data_byte_size(0), + hash_data_has_fixed_byte_size(true) + { + // Define an array of DIE offsets by first defining an array, + // and then define the atom type for the array, in this case + // we have an array of DIE offsets + AppendAtom (eAtomTypeDIEOffset, DW_FORM_data4); + } + + virtual ~Prologue() + { + } + + void + ClearAtoms () + { + hash_data_has_fixed_byte_size = true; + min_hash_data_byte_size = 0; + atom_mask = 0; + atoms.clear(); + } + + bool + ContainsAtom (AtomType atom_type) const + { + return (atom_mask & (1u << atom_type)) != 0; + } + + virtual void + Clear () + { + die_base_offset = 0; + ClearAtoms (); + } + + void + AppendAtom (AtomType type, dw_form_t form) + { + atoms.push_back (Atom(type, form)); + atom_mask |= 1u << type; + switch (form) + { + case DW_FORM_indirect: + case DW_FORM_exprloc: + case DW_FORM_flag_present: + case DW_FORM_ref_sig8: + assert (!"Unhandled atom form"); + break; + + case DW_FORM_string: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + hash_data_has_fixed_byte_size = false; + // Fall through to the cases below... + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_ref1: + case DW_FORM_sec_offset: + min_hash_data_byte_size += 1; + break; + + case DW_FORM_block2: + hash_data_has_fixed_byte_size = false; + // Fall through to the cases below... + case DW_FORM_data2: + case DW_FORM_ref2: + min_hash_data_byte_size += 2; + break; + + case DW_FORM_block4: + hash_data_has_fixed_byte_size = false; + // Fall through to the cases below... + case DW_FORM_data4: + case DW_FORM_ref4: + case DW_FORM_addr: + case DW_FORM_ref_addr: + case DW_FORM_strp: + min_hash_data_byte_size += 4; + break; + + case DW_FORM_data8: + case DW_FORM_ref8: + min_hash_data_byte_size += 8; + break; + + } + } + +// void +// Dump (std::ostream* ostrm_ptr); + + lldb::offset_t + Read (const lldb_private::DataExtractor &data, + lldb::offset_t offset) + { + ClearAtoms (); + + die_base_offset = data.GetU32 (&offset); + + const uint32_t atom_count = data.GetU32 (&offset); + if (atom_count == 0x00060003u) + { + // Old format, deal with contents of old pre-release format + while (data.GetU32(&offset)) + /* do nothing */; + + // Hardcode to the only known value for now. + AppendAtom (eAtomTypeDIEOffset, DW_FORM_data4); + } + else + { + for (uint32_t i=0; i<atom_count; ++i) + { + AtomType type = (AtomType)data.GetU16 (&offset); + dw_form_t form = (dw_form_t)data.GetU16 (&offset); + AppendAtom (type, form); + } + } + return offset; + } + +// virtual void +// Write (BinaryStreamBuf &s); + + size_t + GetByteSize () const + { + // Add an extra count to the atoms size for the zero termination Atom that gets + // written to disk + return sizeof(die_base_offset) + sizeof(uint32_t) + atoms.size() * sizeof(Atom); + } + + size_t + GetMinumumHashDataByteSize () const + { + return min_hash_data_byte_size; + } + + bool + HashDataHasFixedByteSize() const + { + return hash_data_has_fixed_byte_size; + } + }; + + struct Header : public MappedHash::Header<Prologue> + { + Header (dw_offset_t _die_base_offset = 0) + { + } + + virtual + ~Header() + { + } + + virtual size_t + GetByteSize (const HeaderData &header_data) + { + return header_data.GetByteSize(); + } + + // virtual void + // Dump (std::ostream* ostrm_ptr); + // + virtual lldb::offset_t + Read (lldb_private::DataExtractor &data, lldb::offset_t offset) + { + offset = MappedHash::Header<Prologue>::Read (data, offset); + if (offset != UINT32_MAX) + { + offset = header_data.Read (data, offset); + } + return offset; + } + + bool + Read (const lldb_private::DataExtractor &data, + lldb::offset_t *offset_ptr, + DIEInfo &hash_data) const + { + const size_t num_atoms = header_data.atoms.size(); + if (num_atoms == 0) + return false; + + for (size_t i=0; i<num_atoms; ++i) + { + DWARFFormValue form_value (header_data.atoms[i].form); + + if (!form_value.ExtractValue(data, offset_ptr, NULL)) + return false; + + switch (header_data.atoms[i].type) + { + case eAtomTypeDIEOffset: // DIE offset, check form for encoding + hash_data.offset = (dw_offset_t)form_value.Reference (header_data.die_base_offset); + break; + + case eAtomTypeTag: // DW_TAG value for the DIE + hash_data.tag = (dw_tag_t)form_value.Unsigned (); + + case eAtomTypeTypeFlags: // Flags from enum TypeFlags + hash_data.type_flags = (uint32_t)form_value.Unsigned (); + break; + + case eAtomTypeQualNameHash: // Flags from enum TypeFlags + hash_data.qualified_name_hash = form_value.Unsigned (); + break; + + default: + // We can always skip atomes we don't know about + break; + } + } + return true; + } + + void + Dump (lldb_private::Stream& strm, const DIEInfo &hash_data) const + { + const size_t num_atoms = header_data.atoms.size(); + for (size_t i=0; i<num_atoms; ++i) + { + if (i > 0) + strm.PutCString (", "); + + DWARFFormValue form_value (header_data.atoms[i].form); + switch (header_data.atoms[i].type) + { + case eAtomTypeDIEOffset: // DIE offset, check form for encoding + strm.Printf ("{0x%8.8x}", hash_data.offset); + break; + + case eAtomTypeTag: // DW_TAG value for the DIE + { + const char *tag_cstr = lldb_private::DW_TAG_value_to_name (hash_data.tag); + if (tag_cstr) + strm.PutCString (tag_cstr); + else + strm.Printf ("DW_TAG_(0x%4.4x)", hash_data.tag); + } + break; + + case eAtomTypeTypeFlags: // Flags from enum TypeFlags + strm.Printf ("0x%2.2x", hash_data.type_flags); + if (hash_data.type_flags) + { + strm.PutCString (" ("); + if (hash_data.type_flags & eTypeFlagClassIsImplementation) + strm.PutCString (" implementation"); + strm.PutCString (" )"); + } + break; + + case eAtomTypeQualNameHash: // Flags from enum TypeFlags + strm.Printf ("0x%8.8x", hash_data.qualified_name_hash); + break; + + default: + strm.Printf ("AtomType(0x%x)", header_data.atoms[i].type); + break; + } + } + } + }; + +// class ExportTable +// { +// public: +// ExportTable (); +// +// void +// AppendNames (DWARFDebugPubnamesSet &pubnames_set, +// StringTable &string_table); +// +// void +// AppendNamesEntry (SymbolFileDWARF *dwarf2Data, +// const DWARFCompileUnit* cu, +// const DWARFDebugInfoEntry* die, +// StringTable &string_table); +// +// void +// AppendTypesEntry (DWARFData *dwarf2Data, +// const DWARFCompileUnit* cu, +// const DWARFDebugInfoEntry* die, +// StringTable &string_table); +// +// size_t +// Save (BinaryStreamBuf &names_data, const StringTable &string_table); +// +// void +// AppendName (const char *name, +// uint32_t die_offset, +// StringTable &string_table, +// dw_offset_t name_debug_str_offset = DW_INVALID_OFFSET); // If "name" has already been looked up, then it can be supplied +// void +// AppendType (const char *name, +// uint32_t die_offset, +// StringTable &string_table); +// +// +// protected: +// struct Entry +// { +// uint32_t hash; +// uint32_t str_offset; +// uint32_t die_offset; +// }; +// +// // Map uniqued .debug_str offset to the corresponding DIE offsets +// typedef std::map<uint32_t, DIEInfoArray> NameInfo; +// // Map a name hash to one or more name infos +// typedef std::map<uint32_t, NameInfo> BucketEntry; +// +// static uint32_t +// GetByteSize (const NameInfo &name_info); +// +// typedef std::vector<BucketEntry> BucketEntryColl; +// typedef std::vector<Entry> EntryColl; +// EntryColl m_entries; +// +// }; + + + // A class for reading and using a saved hash table from a block of data + // in memory + class MemoryTable : public MappedHash::MemoryTable<uint32_t, DWARFMappedHash::Header, DIEInfoArray> + { + public: + + MemoryTable (lldb_private::DataExtractor &table_data, + const lldb_private::DataExtractor &string_table, + const char *name) : + MappedHash::MemoryTable<uint32_t, Header, DIEInfoArray> (table_data), + m_data (table_data), + m_string_table (string_table), + m_name (name) + { + } + + virtual + ~MemoryTable () + { + } + + virtual const char * + GetStringForKeyType (KeyType key) const + { + // The key in the DWARF table is the .debug_str offset for the string + return m_string_table.PeekCStr (key); + } + + virtual bool + ReadHashData (uint32_t hash_data_offset, + HashData &hash_data) const + { + lldb::offset_t offset = hash_data_offset; + offset += 4; // Skip string table offset that contains offset of hash name in .debug_str + const uint32_t count = m_data.GetU32 (&offset); + if (count > 0) + { + hash_data.resize(count); + for (uint32_t i=0; i<count; ++i) + { + if (!m_header.Read(m_data, &offset, hash_data[i])) + return false; + } + } + else + hash_data.clear(); + return true; + } + + virtual Result + GetHashDataForName (const char *name, + lldb::offset_t* hash_data_offset_ptr, + Pair &pair) const + { + pair.key = m_data.GetU32 (hash_data_offset_ptr); + pair.value.clear(); + + // If the key is zero, this terminates our chain of HashData objects + // for this hash value. + if (pair.key == 0) + return eResultEndOfHashData; + + // There definitely should be a string for this string offset, if + // there isn't, there is something wrong, return and error + const char *strp_cstr = m_string_table.PeekCStr (pair.key); + if (strp_cstr == NULL) + { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + + const uint32_t count = m_data.GetU32 (hash_data_offset_ptr); + const size_t min_total_hash_data_size = count * m_header.header_data.GetMinumumHashDataByteSize(); + if (count > 0 && m_data.ValidOffsetForDataOfSize (*hash_data_offset_ptr, min_total_hash_data_size)) + { + // We have at least one HashData entry, and we have enough + // data to parse at leats "count" HashData enties. + + // First make sure the entire C string matches... + const bool match = strcmp (name, strp_cstr) == 0; + + if (!match && m_header.header_data.HashDataHasFixedByteSize()) + { + // If the string doesn't match and we have fixed size data, + // we can just add the total byte size of all HashData objects + // to the hash data offset and be done... + *hash_data_offset_ptr += min_total_hash_data_size; + } + else + { + // If the string does match, or we don't have fixed size data + // then we need to read the hash data as a stream. If the + // string matches we also append all HashData objects to the + // value array. + for (uint32_t i=0; i<count; ++i) + { + DIEInfo die_info; + if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) + { + // Only happend the HashData if the string matched... + if (match) + pair.value.push_back (die_info); + } + else + { + // Something went wrong while reading the data + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + } + } + // Return the correct response depending on if the string matched + // or not... + if (match) + return eResultKeyMatch; // The key (cstring) matches and we have lookup results! + else + return eResultKeyMismatch; // The key doesn't match, this function will get called + // again for the next key/value or the key terminator + // which in our case is a zero .debug_str offset. + } + else + { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + } + + virtual Result + AppendHashDataForRegularExpression (const lldb_private::RegularExpression& regex, + lldb::offset_t* hash_data_offset_ptr, + Pair &pair) const + { + pair.key = m_data.GetU32 (hash_data_offset_ptr); + // If the key is zero, this terminates our chain of HashData objects + // for this hash value. + if (pair.key == 0) + return eResultEndOfHashData; + + // There definitely should be a string for this string offset, if + // there isn't, there is something wrong, return and error + const char *strp_cstr = m_string_table.PeekCStr (pair.key); + if (strp_cstr == NULL) + return eResultError; + + const uint32_t count = m_data.GetU32 (hash_data_offset_ptr); + const size_t min_total_hash_data_size = count * m_header.header_data.GetMinumumHashDataByteSize(); + if (count > 0 && m_data.ValidOffsetForDataOfSize (*hash_data_offset_ptr, min_total_hash_data_size)) + { + const bool match = regex.Execute(strp_cstr); + + if (!match && m_header.header_data.HashDataHasFixedByteSize()) + { + // If the regex doesn't match and we have fixed size data, + // we can just add the total byte size of all HashData objects + // to the hash data offset and be done... + *hash_data_offset_ptr += min_total_hash_data_size; + } + else + { + // If the string does match, or we don't have fixed size data + // then we need to read the hash data as a stream. If the + // string matches we also append all HashData objects to the + // value array. + for (uint32_t i=0; i<count; ++i) + { + DIEInfo die_info; + if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) + { + // Only happend the HashData if the string matched... + if (match) + pair.value.push_back (die_info); + } + else + { + // Something went wrong while reading the data + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + } + } + // Return the correct response depending on if the string matched + // or not... + if (match) + return eResultKeyMatch; // The key (cstring) matches and we have lookup results! + else + return eResultKeyMismatch; // The key doesn't match, this function will get called + // again for the next key/value or the key terminator + // which in our case is a zero .debug_str offset. + } + else + { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + } + + size_t + AppendAllDIEsThatMatchingRegex (const lldb_private::RegularExpression& regex, + DIEInfoArray &die_info_array) const + { + const uint32_t hash_count = m_header.hashes_count; + Pair pair; + for (uint32_t offset_idx=0; offset_idx<hash_count; ++offset_idx) + { + lldb::offset_t hash_data_offset = GetHashDataOffset (offset_idx); + while (hash_data_offset != UINT32_MAX) + { + const lldb::offset_t prev_hash_data_offset = hash_data_offset; + Result hash_result = AppendHashDataForRegularExpression (regex, &hash_data_offset, pair); + if (prev_hash_data_offset == hash_data_offset) + break; + + // Check the result of getting our hash data + switch (hash_result) + { + case eResultKeyMatch: + case eResultKeyMismatch: + // Whether we matches or not, it doesn't matter, we + // keep looking. + break; + + case eResultEndOfHashData: + case eResultError: + hash_data_offset = UINT32_MAX; + break; + } + } + } + die_info_array.swap (pair.value); + return die_info_array.size(); + } + + size_t + AppendAllDIEsInRange (const uint32_t die_offset_start, + const uint32_t die_offset_end, + DIEInfoArray &die_info_array) const + { + const uint32_t hash_count = m_header.hashes_count; + for (uint32_t offset_idx=0; offset_idx<hash_count; ++offset_idx) + { + bool done = false; + lldb::offset_t hash_data_offset = GetHashDataOffset (offset_idx); + while (!done && hash_data_offset != UINT32_MAX) + { + KeyType key = m_data.GetU32 (&hash_data_offset); + // If the key is zero, this terminates our chain of HashData objects + // for this hash value. + if (key == 0) + break; + + const uint32_t count = m_data.GetU32 (&hash_data_offset); + for (uint32_t i=0; i<count; ++i) + { + DIEInfo die_info; + if (m_header.Read(m_data, &hash_data_offset, die_info)) + { + if (die_info.offset == 0) + done = true; + if (die_offset_start <= die_info.offset && die_info.offset < die_offset_end) + die_info_array.push_back(die_info); + } + } + } + } + return die_info_array.size(); + } + + size_t + FindByName (const char *name, DIEArray &die_offsets) + { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray (die_info_array, die_offsets); + return die_info_array.size(); + } + + size_t + FindByNameAndTag (const char *name, + const dw_tag_t tag, + DIEArray &die_offsets) + { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray (die_info_array, tag, die_offsets); + return die_info_array.size(); + } + + size_t + FindByNameAndTagAndQualifiedNameHash (const char *name, + const dw_tag_t tag, + const uint32_t qualified_name_hash, + DIEArray &die_offsets) + { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray (die_info_array, tag, qualified_name_hash, die_offsets); + return die_info_array.size(); + } + + size_t + FindCompleteObjCClassByName (const char *name, DIEArray &die_offsets, bool must_be_implementation) + { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + { + if (must_be_implementation && GetHeader().header_data.ContainsAtom (eAtomTypeTypeFlags)) + { + // If we have two atoms, then we have the DIE offset and + // the type flags so we can find the objective C class + // efficiently. + DWARFMappedHash::ExtractTypesFromDIEArray (die_info_array, + UINT32_MAX, + eTypeFlagClassIsImplementation, + die_offsets); + } + else + { + // We don't only want the one true definition, so try and see + // what we can find, and only return class or struct DIEs. + // If we do have the full implementation, then return it alone, + // else return all possible matches. + const bool return_implementation_only_if_available = true; + DWARFMappedHash::ExtractClassOrStructDIEArray (die_info_array, + return_implementation_only_if_available, + die_offsets); + } + } + return die_offsets.size(); + } + + size_t + FindByName (const char *name, DIEInfoArray &die_info_array) + { + Pair kv_pair; + size_t old_size = die_info_array.size(); + if (Find (name, kv_pair)) + { + die_info_array.swap(kv_pair.value); + return die_info_array.size() - old_size; + } + return 0; + } + + protected: + const lldb_private::DataExtractor &m_data; + const lldb_private::DataExtractor &m_string_table; + std::string m_name; + }; +}; + + +#endif // SymbolFileDWARF_HashedNameToDIE_h_ diff --git a/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp b/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp new file mode 100644 index 000000000000..27fa261813bb --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp @@ -0,0 +1,232 @@ +//===-- LogChannelDWARF.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LogChannelDWARF.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "SymbolFileDWARF.h" + +using namespace lldb; +using namespace lldb_private; + + +// when the one and only logging channel is abled, then this will be non NULL. +static LogChannelDWARF* g_log_channel = NULL; + +LogChannelDWARF::LogChannelDWARF () : + LogChannel () +{ +} + +LogChannelDWARF::~LogChannelDWARF () +{ +} + + +void +LogChannelDWARF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + LogChannelDWARF::CreateInstance); +} + +void +LogChannelDWARF::Terminate() +{ + PluginManager::UnregisterPlugin (LogChannelDWARF::CreateInstance); +} + +LogChannel* +LogChannelDWARF::CreateInstance () +{ + return new LogChannelDWARF (); +} + +lldb_private::ConstString +LogChannelDWARF::GetPluginNameStatic() +{ + return SymbolFileDWARF::GetPluginNameStatic(); +} + +const char * +LogChannelDWARF::GetPluginDescriptionStatic() +{ + return "DWARF log channel for debugging plug-in issues."; +} + +lldb_private::ConstString +LogChannelDWARF::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +LogChannelDWARF::GetPluginVersion() +{ + return 1; +} + + +void +LogChannelDWARF::Delete () +{ + g_log_channel = NULL; +} + + +void +LogChannelDWARF::Disable (const char **categories, Stream *feedback_strm) +{ + if (m_log_ap.get() == NULL) + return; + + uint32_t flag_bits = m_log_ap->GetMask().Get(); + for (size_t i = 0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + if (::strcasecmp (arg, "all") == 0) flag_bits &= ~DWARF_LOG_ALL; + else if (::strcasecmp (arg, "info") == 0) flag_bits &= ~DWARF_LOG_DEBUG_INFO; + else if (::strcasecmp (arg, "line") == 0) flag_bits &= ~DWARF_LOG_DEBUG_LINE; + else if (::strcasecmp (arg, "pubnames") == 0) flag_bits &= ~DWARF_LOG_DEBUG_PUBNAMES; + else if (::strcasecmp (arg, "pubtypes") == 0) flag_bits &= ~DWARF_LOG_DEBUG_PUBTYPES; + else if (::strcasecmp (arg, "aranges") == 0) flag_bits &= ~DWARF_LOG_DEBUG_ARANGES; + else if (::strcasecmp (arg, "lookups") == 0) flag_bits &= ~DWARF_LOG_LOOKUPS; + else if (::strcasecmp (arg, "map") == 0) flag_bits &= ~DWARF_LOG_DEBUG_MAP; + else if (::strcasecmp (arg, "default") == 0) flag_bits &= ~DWARF_LOG_DEFAULT; + else if (::strncasecmp(arg, "comp", 4) == 0) flag_bits &= ~DWARF_LOG_TYPE_COMPLETION; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListCategories (feedback_strm); + } + } + + if (flag_bits == 0) + Delete (); + else + m_log_ap->GetMask().Reset (flag_bits); + + return; +} + +bool +LogChannelDWARF::Enable +( + StreamSP &log_stream_sp, + uint32_t log_options, + Stream *feedback_strm, // Feedback stream for argument errors etc + const char **categories // The categories to enable within this logging stream, if empty, enable default set +) +{ + Delete (); + + if (m_log_ap) + m_log_ap->SetStream(log_stream_sp); + else + m_log_ap.reset(new Log (log_stream_sp)); + + g_log_channel = this; + uint32_t flag_bits = 0; + bool got_unknown_category = false; + for (size_t i = 0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + if (::strcasecmp (arg, "all") == 0) flag_bits |= DWARF_LOG_ALL; + else if (::strcasecmp (arg, "info") == 0) flag_bits |= DWARF_LOG_DEBUG_INFO; + else if (::strcasecmp (arg, "line") == 0) flag_bits |= DWARF_LOG_DEBUG_LINE; + else if (::strcasecmp (arg, "pubnames") == 0) flag_bits |= DWARF_LOG_DEBUG_PUBNAMES; + else if (::strcasecmp (arg, "pubtypes") == 0) flag_bits |= DWARF_LOG_DEBUG_PUBTYPES; + else if (::strcasecmp (arg, "aranges") == 0) flag_bits |= DWARF_LOG_DEBUG_ARANGES; + else if (::strcasecmp (arg, "lookups") == 0) flag_bits |= DWARF_LOG_LOOKUPS; + else if (::strcasecmp (arg, "map") == 0) flag_bits |= DWARF_LOG_DEBUG_MAP; + else if (::strcasecmp (arg, "default") == 0) flag_bits |= DWARF_LOG_DEFAULT; + else if (::strncasecmp(arg, "comp", 4) == 0) flag_bits |= DWARF_LOG_TYPE_COMPLETION; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + if (got_unknown_category == false) + { + got_unknown_category = true; + ListCategories (feedback_strm); + } + } + } + if (flag_bits == 0) + flag_bits = DWARF_LOG_DEFAULT; + m_log_ap->GetMask().Reset(flag_bits); + m_log_ap->GetOptions().Reset(log_options); + return m_log_ap.get() != NULL; +} + +void +LogChannelDWARF::ListCategories (Stream *strm) +{ + strm->Printf ("Logging categories for '%s':\n" + " all - turn on all available logging categories\n" + " info - log the parsing if .debug_info\n" + " line - log the parsing if .debug_line\n" + " pubnames - log the parsing if .debug_pubnames\n" + " pubtypes - log the parsing if .debug_pubtypes\n" + " lookups - log any lookups that happen by name, regex, or address\n" + " completion - log struct/unions/class type completions\n" + " map - log insertions of object files into DWARF debug maps\n", + SymbolFileDWARF::GetPluginNameStatic().GetCString()); +} + +Log * +LogChannelDWARF::GetLog () +{ + if (g_log_channel) + return g_log_channel->m_log_ap.get(); + + return NULL; +} + +Log * +LogChannelDWARF::GetLogIfAll (uint32_t mask) +{ + if (g_log_channel && g_log_channel->m_log_ap.get()) + { + if (g_log_channel->m_log_ap->GetMask().AllSet(mask)) + return g_log_channel->m_log_ap.get(); + } + return NULL; +} + +Log * +LogChannelDWARF::GetLogIfAny (uint32_t mask) +{ + if (g_log_channel && g_log_channel->m_log_ap.get()) + { + if (g_log_channel->m_log_ap->GetMask().AnySet(mask)) + return g_log_channel->m_log_ap.get(); + } + return NULL; +} + +void +LogChannelDWARF::LogIf (uint32_t mask, const char *format, ...) +{ + if (g_log_channel) + { + Log *log = g_log_channel->m_log_ap.get(); + if (log && log->GetMask().AnySet(mask)) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } + } +} diff --git a/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h b/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h new file mode 100644 index 000000000000..2091a8414f58 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h @@ -0,0 +1,89 @@ +//===-- LogChannelDWARF.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_LogChannelDWARF_h_ +#define SymbolFileDWARF_LogChannelDWARF_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +// Project includes +#include "lldb/Core/Log.h" + +#define DWARF_LOG_VERBOSE (1u << 0) +#define DWARF_LOG_DEBUG_INFO (1u << 1) +#define DWARF_LOG_DEBUG_LINE (1u << 2) +#define DWARF_LOG_DEBUG_PUBNAMES (1u << 3) +#define DWARF_LOG_DEBUG_PUBTYPES (1u << 4) +#define DWARF_LOG_DEBUG_ARANGES (1u << 5) +#define DWARF_LOG_LOOKUPS (1u << 6) +#define DWARF_LOG_TYPE_COMPLETION (1u << 7) +#define DWARF_LOG_DEBUG_MAP (1u << 8) +#define DWARF_LOG_ALL (UINT32_MAX) +#define DWARF_LOG_DEFAULT (DWARF_LOG_DEBUG_INFO) + +class LogChannelDWARF : public lldb_private::LogChannel +{ +public: + LogChannelDWARF (); + + virtual + ~LogChannelDWARF (); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::LogChannel * + CreateInstance (); + + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + Disable (const char** categories, lldb_private::Stream *feedback_strm); + + void + Delete (); + + virtual bool + Enable (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + lldb_private::Stream *feedback_strm, // Feedback stream for argument errors etc + const char **categories); // The categories to enable within this logging stream, if empty, enable default set + + virtual void + ListCategories (lldb_private::Stream *strm); + + static lldb_private::Log * + GetLog (); + + static lldb_private::Log * + GetLogIfAll (uint32_t mask); + + static lldb_private::Log * + GetLogIfAny (uint32_t mask); + + static void + LogIf (uint32_t mask, const char *format, ...); +}; + +#endif // SymbolFileDWARF_LogChannelDWARF_h_ diff --git a/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp b/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp new file mode 100644 index 000000000000..5514469d0758 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/NameToDIE.cpp @@ -0,0 +1,87 @@ +//===-- NameToDIE.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NameToDIE.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugInfo.h" +#include "DWARFDebugInfoEntry.h" +#include "SymbolFileDWARF.h" +using namespace lldb; +using namespace lldb_private; + +void +NameToDIE::Finalize() +{ + m_map.Sort (); + m_map.SizeToFit (); +} + +void +NameToDIE::Insert (const ConstString& name, uint32_t die_offset) +{ + m_map.Append(name.GetCString(), die_offset); +} + +size_t +NameToDIE::Find (const ConstString &name, DIEArray &info_array) const +{ + return m_map.GetValues (name.GetCString(), info_array); +} + +size_t +NameToDIE::Find (const RegularExpression& regex, DIEArray &info_array) const +{ + return m_map.GetValues (regex, info_array); +} + +size_t +NameToDIE::FindAllEntriesForCompileUnit (uint32_t cu_offset, + uint32_t cu_end_offset, + DIEArray &info_array) const +{ + const size_t initial_size = info_array.size(); + const uint32_t size = m_map.GetSize(); + for (uint32_t i=0; i<size; ++i) + { + const uint32_t die_offset = m_map.GetValueAtIndexUnchecked(i); + if (cu_offset < die_offset && die_offset < cu_end_offset) + info_array.push_back (die_offset); + } + return info_array.size() - initial_size; +} + +void +NameToDIE::Dump (Stream *s) +{ + const uint32_t size = m_map.GetSize(); + for (uint32_t i=0; i<size; ++i) + { + const char *cstr = m_map.GetCStringAtIndex(i); + s->Printf("%p: {0x%8.8x} \"%s\"\n", cstr, m_map.GetValueAtIndexUnchecked(i), cstr); + } +} + +void +NameToDIE::ForEach (std::function <bool(const char *name, uint32_t die_offset)> const &callback) const +{ + const uint32_t size = m_map.GetSize(); + for (uint32_t i=0; i<size; ++i) + { + if (!callback(m_map.GetCStringAtIndexUnchecked(i), + m_map.GetValueAtIndexUnchecked (i))) + break; + } +} diff --git a/source/Plugins/SymbolFile/DWARF/NameToDIE.h b/source/Plugins/SymbolFile/DWARF/NameToDIE.h new file mode 100644 index 000000000000..f9a12736bf9e --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/NameToDIE.h @@ -0,0 +1,65 @@ +//===-- NameToDIE.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_NameToDIE_h_ +#define SymbolFileDWARF_NameToDIE_h_ + +#include "lldb/Core/UniqueCStringMap.h" + +#include <functional> + +#include "lldb/lldb-defines.h" + +class SymbolFileDWARF; + +typedef std::vector<uint32_t> DIEArray; + +class NameToDIE +{ +public: + NameToDIE () : + m_map() + { + } + + ~NameToDIE () + { + } + + void + Dump (lldb_private::Stream *s); + + void + Insert (const lldb_private::ConstString& name, uint32_t die_offset); + + void + Finalize(); + + size_t + Find (const lldb_private::ConstString &name, + DIEArray &info_array) const; + + size_t + Find (const lldb_private::RegularExpression& regex, + DIEArray &info_array) const; + + size_t + FindAllEntriesForCompileUnit (uint32_t cu_offset, + uint32_t cu_end_offset, + DIEArray &info_array) const; + + void + ForEach (std::function <bool(const char *name, uint32_t die_offset)> const &callback) const; + +protected: + lldb_private::UniqueCStringMap<uint32_t> m_map; + +}; + +#endif // SymbolFileDWARF_NameToDIE_h_ diff --git a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp new file mode 100644 index 000000000000..f265af837eef --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -0,0 +1,7973 @@ +//===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARF.h" + +// Other libraries and framework includes +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Specifiers.h" +#include "clang/Sema/DeclSpec.h" + +#include "llvm/Support/Casting.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Value.h" + +#include "lldb/Host/Host.h" + +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ClangExternalASTSourceCallbacks.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/CPPLanguageRuntime.h" + +#include "DWARFCompileUnit.h" +#include "DWARFDebugAbbrev.h" +#include "DWARFDebugAranges.h" +#include "DWARFDebugInfo.h" +#include "DWARFDebugInfoEntry.h" +#include "DWARFDebugLine.h" +#include "DWARFDebugPubnames.h" +#include "DWARFDebugRanges.h" +#include "DWARFDeclContext.h" +#include "DWARFDIECollection.h" +#include "DWARFFormValue.h" +#include "DWARFLocationList.h" +#include "LogChannelDWARF.h" +#include "SymbolFileDWARFDebugMap.h" + +#include <map> + +//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN + +#ifdef ENABLE_DEBUG_PRINTF +#include <stdio.h> +#define DEBUG_PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_PRINTF(fmt, ...) +#endif + +#define DIE_IS_BEING_PARSED ((lldb_private::Type*)1) + +using namespace lldb; +using namespace lldb_private; + +//static inline bool +//child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag) +//{ +// switch (tag) +// { +// default: +// break; +// case DW_TAG_subprogram: +// case DW_TAG_inlined_subroutine: +// case DW_TAG_class_type: +// case DW_TAG_structure_type: +// case DW_TAG_union_type: +// return true; +// } +// return false; +//} +// +static AccessType +DW_ACCESS_to_AccessType (uint32_t dwarf_accessibility) +{ + switch (dwarf_accessibility) + { + case DW_ACCESS_public: return eAccessPublic; + case DW_ACCESS_private: return eAccessPrivate; + case DW_ACCESS_protected: return eAccessProtected; + default: break; + } + return eAccessNone; +} + +#if defined(LLDB_CONFIGURATION_DEBUG) or defined(LLDB_CONFIGURATION_RELEASE) + +class DIEStack +{ +public: + + void Push (DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die) + { + m_dies.push_back (DIEInfo(cu, die)); + } + + + void LogDIEs (Log *log, SymbolFileDWARF *dwarf) + { + StreamString log_strm; + const size_t n = m_dies.size(); + log_strm.Printf("DIEStack[%" PRIu64 "]:\n", (uint64_t)n); + for (size_t i=0; i<n; i++) + { + DWARFCompileUnit *cu = m_dies[i].cu; + const DWARFDebugInfoEntry *die = m_dies[i].die; + std::string qualified_name; + die->GetQualifiedName(dwarf, cu, qualified_name); + log_strm.Printf ("[%" PRIu64 "] 0x%8.8x: %s name='%s'\n", + (uint64_t)i, + die->GetOffset(), + DW_TAG_value_to_name(die->Tag()), + qualified_name.c_str()); + } + log->PutCString(log_strm.GetData()); + } + void Pop () + { + m_dies.pop_back(); + } + + class ScopedPopper + { + public: + ScopedPopper (DIEStack &die_stack) : + m_die_stack (die_stack), + m_valid (false) + { + } + + void + Push (DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die) + { + m_valid = true; + m_die_stack.Push (cu, die); + } + + ~ScopedPopper () + { + if (m_valid) + m_die_stack.Pop(); + } + + + + protected: + DIEStack &m_die_stack; + bool m_valid; + }; + +protected: + struct DIEInfo { + DIEInfo (DWARFCompileUnit *c, const DWARFDebugInfoEntry *d) : + cu(c), + die(d) + { + } + DWARFCompileUnit *cu; + const DWARFDebugInfoEntry *die; + }; + typedef std::vector<DIEInfo> Stack; + Stack m_dies; +}; +#endif + +void +SymbolFileDWARF::Initialize() +{ + LogChannelDWARF::Initialize(); + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileDWARF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); + LogChannelDWARF::Initialize(); +} + + +lldb_private::ConstString +SymbolFileDWARF::GetPluginNameStatic() +{ + static ConstString g_name("dwarf"); + return g_name; +} + +const char * +SymbolFileDWARF::GetPluginDescriptionStatic() +{ + return "DWARF and DWARF3 debug symbol file reader."; +} + + +SymbolFile* +SymbolFileDWARF::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileDWARF(obj_file); +} + +TypeList * +SymbolFileDWARF::GetTypeList () +{ + if (GetDebugMapSymfile ()) + return m_debug_map_symfile->GetTypeList(); + return m_obj_file->GetModule()->GetTypeList(); + +} +void +SymbolFileDWARF::GetTypes (DWARFCompileUnit* cu, + const DWARFDebugInfoEntry *die, + dw_offset_t min_die_offset, + dw_offset_t max_die_offset, + uint32_t type_mask, + TypeSet &type_set) +{ + if (cu) + { + if (die) + { + const dw_offset_t die_offset = die->GetOffset(); + + if (die_offset >= max_die_offset) + return; + + if (die_offset >= min_die_offset) + { + const dw_tag_t tag = die->Tag(); + + bool add_type = false; + + switch (tag) + { + case DW_TAG_array_type: add_type = (type_mask & eTypeClassArray ) != 0; break; + case DW_TAG_unspecified_type: + case DW_TAG_base_type: add_type = (type_mask & eTypeClassBuiltin ) != 0; break; + case DW_TAG_class_type: add_type = (type_mask & eTypeClassClass ) != 0; break; + case DW_TAG_structure_type: add_type = (type_mask & eTypeClassStruct ) != 0; break; + case DW_TAG_union_type: add_type = (type_mask & eTypeClassUnion ) != 0; break; + case DW_TAG_enumeration_type: add_type = (type_mask & eTypeClassEnumeration ) != 0; break; + case DW_TAG_subroutine_type: + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: add_type = (type_mask & eTypeClassFunction ) != 0; break; + case DW_TAG_pointer_type: add_type = (type_mask & eTypeClassPointer ) != 0; break; + case DW_TAG_rvalue_reference_type: + case DW_TAG_reference_type: add_type = (type_mask & eTypeClassReference ) != 0; break; + case DW_TAG_typedef: add_type = (type_mask & eTypeClassTypedef ) != 0; break; + case DW_TAG_ptr_to_member_type: add_type = (type_mask & eTypeClassMemberPointer ) != 0; break; + } + + if (add_type) + { + const bool assert_not_being_parsed = true; + Type *type = ResolveTypeUID (cu, die, assert_not_being_parsed); + if (type) + { + if (type_set.find(type) == type_set.end()) + type_set.insert(type); + } + } + } + + for (const DWARFDebugInfoEntry *child_die = die->GetFirstChild(); + child_die != NULL; + child_die = child_die->GetSibling()) + { + GetTypes (cu, child_die, min_die_offset, max_die_offset, type_mask, type_set); + } + } + } +} + +size_t +SymbolFileDWARF::GetTypes (SymbolContextScope *sc_scope, + uint32_t type_mask, + TypeList &type_list) + +{ + TypeSet type_set; + + CompileUnit *comp_unit = NULL; + DWARFCompileUnit* dwarf_cu = NULL; + if (sc_scope) + comp_unit = sc_scope->CalculateSymbolContextCompileUnit(); + + if (comp_unit) + { + dwarf_cu = GetDWARFCompileUnit(comp_unit); + if (dwarf_cu == 0) + return 0; + GetTypes (dwarf_cu, + dwarf_cu->DIE(), + dwarf_cu->GetOffset(), + dwarf_cu->GetNextCompileUnitOffset(), + type_mask, + type_set); + } + else + { + DWARFDebugInfo* info = DebugInfo(); + if (info) + { + const size_t num_cus = info->GetNumCompileUnits(); + for (size_t cu_idx=0; cu_idx<num_cus; ++cu_idx) + { + dwarf_cu = info->GetCompileUnitAtIndex(cu_idx); + if (dwarf_cu) + { + GetTypes (dwarf_cu, + dwarf_cu->DIE(), + 0, + UINT32_MAX, + type_mask, + type_set); + } + } + } + } +// if (m_using_apple_tables) +// { +// DWARFMappedHash::MemoryTable *apple_types = m_apple_types_ap.get(); +// if (apple_types) +// { +// apple_types->ForEach([this, &type_set, apple_types, type_mask](const DWARFMappedHash::DIEInfoArray &die_info_array) -> bool { +// +// for (auto die_info: die_info_array) +// { +// bool add_type = TagMatchesTypeMask (type_mask, 0); +// if (!add_type) +// { +// dw_tag_t tag = die_info.tag; +// if (tag == 0) +// { +// const DWARFDebugInfoEntry *die = DebugInfo()->GetDIEPtr(die_info.offset, NULL); +// tag = die->Tag(); +// } +// add_type = TagMatchesTypeMask (type_mask, tag); +// } +// if (add_type) +// { +// Type *type = ResolveTypeUID(die_info.offset); +// +// if (type_set.find(type) == type_set.end()) +// type_set.insert(type); +// } +// } +// return true; // Keep iterating +// }); +// } +// } +// else +// { +// if (!m_indexed) +// Index (); +// +// m_type_index.ForEach([this, &type_set, type_mask](const char *name, uint32_t die_offset) -> bool { +// +// bool add_type = TagMatchesTypeMask (type_mask, 0); +// +// if (!add_type) +// { +// const DWARFDebugInfoEntry *die = DebugInfo()->GetDIEPtr(die_offset, NULL); +// if (die) +// { +// const dw_tag_t tag = die->Tag(); +// add_type = TagMatchesTypeMask (type_mask, tag); +// } +// } +// +// if (add_type) +// { +// Type *type = ResolveTypeUID(die_offset); +// +// if (type_set.find(type) == type_set.end()) +// type_set.insert(type); +// } +// return true; // Keep iterating +// }); +// } + + std::set<ClangASTType> clang_type_set; + size_t num_types_added = 0; + for (Type *type : type_set) + { + ClangASTType clang_type = type->GetClangForwardType(); + if (clang_type_set.find(clang_type) == clang_type_set.end()) + { + clang_type_set.insert(clang_type); + type_list.Insert (type->shared_from_this()); + ++num_types_added; + } + } + return num_types_added; +} + + +//---------------------------------------------------------------------- +// Gets the first parent that is a lexical block, function or inlined +// subroutine, or compile unit. +//---------------------------------------------------------------------- +static const DWARFDebugInfoEntry * +GetParentSymbolContextDIE(const DWARFDebugInfoEntry *child_die) +{ + const DWARFDebugInfoEntry *die; + for (die = child_die->GetParent(); die != NULL; die = die->GetParent()) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_compile_unit: + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + return die; + } + } + return NULL; +} + + +SymbolFileDWARF::SymbolFileDWARF(ObjectFile* objfile) : + SymbolFile (objfile), + UserID (0), // Used by SymbolFileDWARFDebugMap to when this class parses .o files to contain the .o file index/ID + m_debug_map_module_wp (), + m_debug_map_symfile (NULL), + m_clang_tu_decl (NULL), + m_flags(), + m_data_debug_abbrev (), + m_data_debug_aranges (), + m_data_debug_frame (), + m_data_debug_info (), + m_data_debug_line (), + m_data_debug_loc (), + m_data_debug_ranges (), + m_data_debug_str (), + m_data_apple_names (), + m_data_apple_types (), + m_data_apple_namespaces (), + m_abbr(), + m_info(), + m_line(), + m_apple_names_ap (), + m_apple_types_ap (), + m_apple_namespaces_ap (), + m_apple_objc_ap (), + m_function_basename_index(), + m_function_fullname_index(), + m_function_method_index(), + m_function_selector_index(), + m_objc_class_selectors_index(), + m_global_index(), + m_type_index(), + m_namespace_index(), + m_indexed (false), + m_is_external_ast_source (false), + m_using_apple_tables (false), + m_supports_DW_AT_APPLE_objc_complete_type (eLazyBoolCalculate), + m_ranges(), + m_unique_ast_type_map () +{ +} + +SymbolFileDWARF::~SymbolFileDWARF() +{ + if (m_is_external_ast_source) + { + ModuleSP module_sp (m_obj_file->GetModule()); + if (module_sp) + module_sp->GetClangASTContext().RemoveExternalSource (); + } +} + +static const ConstString & +GetDWARFMachOSegmentName () +{ + static ConstString g_dwarf_section_name ("__DWARF"); + return g_dwarf_section_name; +} + +UniqueDWARFASTTypeMap & +SymbolFileDWARF::GetUniqueDWARFASTTypeMap () +{ + if (GetDebugMapSymfile ()) + return m_debug_map_symfile->GetUniqueDWARFASTTypeMap (); + return m_unique_ast_type_map; +} + +ClangASTContext & +SymbolFileDWARF::GetClangASTContext () +{ + if (GetDebugMapSymfile ()) + return m_debug_map_symfile->GetClangASTContext (); + + ClangASTContext &ast = m_obj_file->GetModule()->GetClangASTContext(); + if (!m_is_external_ast_source) + { + m_is_external_ast_source = true; + llvm::OwningPtr<clang::ExternalASTSource> ast_source_ap ( + new ClangExternalASTSourceCallbacks (SymbolFileDWARF::CompleteTagDecl, + SymbolFileDWARF::CompleteObjCInterfaceDecl, + SymbolFileDWARF::FindExternalVisibleDeclsByName, + SymbolFileDWARF::LayoutRecordType, + this)); + ast.SetExternalSource (ast_source_ap); + } + return ast; +} + +void +SymbolFileDWARF::InitializeObject() +{ + // Install our external AST source callbacks so we can complete Clang types. + ModuleSP module_sp (m_obj_file->GetModule()); + if (module_sp) + { + const SectionList *section_list = module_sp->GetSectionList(); + + const Section* section = section_list->FindSectionByName(GetDWARFMachOSegmentName ()).get(); + + // Memory map the DWARF mach-o segment so we have everything mmap'ed + // to keep our heap memory usage down. + if (section) + m_obj_file->MemoryMapSectionData(section, m_dwarf_data); + } + get_apple_names_data(); + if (m_data_apple_names.GetByteSize() > 0) + { + m_apple_names_ap.reset (new DWARFMappedHash::MemoryTable (m_data_apple_names, get_debug_str_data(), ".apple_names")); + if (m_apple_names_ap->IsValid()) + m_using_apple_tables = true; + else + m_apple_names_ap.reset(); + } + get_apple_types_data(); + if (m_data_apple_types.GetByteSize() > 0) + { + m_apple_types_ap.reset (new DWARFMappedHash::MemoryTable (m_data_apple_types, get_debug_str_data(), ".apple_types")); + if (m_apple_types_ap->IsValid()) + m_using_apple_tables = true; + else + m_apple_types_ap.reset(); + } + + get_apple_namespaces_data(); + if (m_data_apple_namespaces.GetByteSize() > 0) + { + m_apple_namespaces_ap.reset (new DWARFMappedHash::MemoryTable (m_data_apple_namespaces, get_debug_str_data(), ".apple_namespaces")); + if (m_apple_namespaces_ap->IsValid()) + m_using_apple_tables = true; + else + m_apple_namespaces_ap.reset(); + } + + get_apple_objc_data(); + if (m_data_apple_objc.GetByteSize() > 0) + { + m_apple_objc_ap.reset (new DWARFMappedHash::MemoryTable (m_data_apple_objc, get_debug_str_data(), ".apple_objc")); + if (m_apple_objc_ap->IsValid()) + m_using_apple_tables = true; + else + m_apple_objc_ap.reset(); + } +} + +bool +SymbolFileDWARF::SupportedVersion(uint16_t version) +{ + return version == 2 || version == 3 || version == 4; +} + +uint32_t +SymbolFileDWARF::CalculateAbilities () +{ + uint32_t abilities = 0; + if (m_obj_file != NULL) + { + const Section* section = NULL; + const SectionList *section_list = m_obj_file->GetSectionList(); + if (section_list == NULL) + return 0; + + uint64_t debug_abbrev_file_size = 0; + uint64_t debug_info_file_size = 0; + uint64_t debug_line_file_size = 0; + + section = section_list->FindSectionByName(GetDWARFMachOSegmentName ()).get(); + + if (section) + section_list = §ion->GetChildren (); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugInfo, true).get(); + if (section != NULL) + { + debug_info_file_size = section->GetFileSize(); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugAbbrev, true).get(); + if (section) + debug_abbrev_file_size = section->GetFileSize(); + else + m_flags.Set (flagsGotDebugAbbrevData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugAranges, true).get(); + if (!section) + m_flags.Set (flagsGotDebugArangesData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugFrame, true).get(); + if (!section) + m_flags.Set (flagsGotDebugFrameData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugLine, true).get(); + if (section) + debug_line_file_size = section->GetFileSize(); + else + m_flags.Set (flagsGotDebugLineData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugLoc, true).get(); + if (!section) + m_flags.Set (flagsGotDebugLocData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugMacInfo, true).get(); + if (!section) + m_flags.Set (flagsGotDebugMacInfoData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugPubNames, true).get(); + if (!section) + m_flags.Set (flagsGotDebugPubNamesData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugPubTypes, true).get(); + if (!section) + m_flags.Set (flagsGotDebugPubTypesData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugRanges, true).get(); + if (!section) + m_flags.Set (flagsGotDebugRangesData); + + section = section_list->FindSectionByType (eSectionTypeDWARFDebugStr, true).get(); + if (!section) + m_flags.Set (flagsGotDebugStrData); + } + else + { + const char *symfile_dir_cstr = m_obj_file->GetFileSpec().GetDirectory().GetCString(); + if (symfile_dir_cstr) + { + if (strcasestr(symfile_dir_cstr, ".dsym")) + { + if (m_obj_file->GetType() == ObjectFile::eTypeDebugInfo) + { + // We have a dSYM file that didn't have a any debug info. + // If the string table has a size of 1, then it was made from + // an executable with no debug info, or from an executable that + // was stripped. + section = section_list->FindSectionByType (eSectionTypeDWARFDebugStr, true).get(); + if (section && section->GetFileSize() == 1) + { + m_obj_file->GetModule()->ReportWarning ("empty dSYM file detected, dSYM was created with an executable with no debug info."); + } + } + } + } + } + + if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) + abilities |= CompileUnits | Functions | Blocks | GlobalVariables | LocalVariables | VariableTypes; + + if (debug_line_file_size > 0) + abilities |= LineTables; + } + return abilities; +} + +const DataExtractor& +SymbolFileDWARF::GetCachedSectionData (uint32_t got_flag, SectionType sect_type, DataExtractor &data) +{ + if (m_flags.IsClear (got_flag)) + { + ModuleSP module_sp (m_obj_file->GetModule()); + m_flags.Set (got_flag); + const SectionList *section_list = module_sp->GetSectionList(); + if (section_list) + { + SectionSP section_sp (section_list->FindSectionByType(sect_type, true)); + if (section_sp) + { + // See if we memory mapped the DWARF segment? + if (m_dwarf_data.GetByteSize()) + { + data.SetData(m_dwarf_data, section_sp->GetOffset (), section_sp->GetFileSize()); + } + else + { + if (m_obj_file->ReadSectionData (section_sp.get(), data) == 0) + data.Clear(); + } + } + } + } + return data; +} + +const DataExtractor& +SymbolFileDWARF::get_debug_abbrev_data() +{ + return GetCachedSectionData (flagsGotDebugAbbrevData, eSectionTypeDWARFDebugAbbrev, m_data_debug_abbrev); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_aranges_data() +{ + return GetCachedSectionData (flagsGotDebugArangesData, eSectionTypeDWARFDebugAranges, m_data_debug_aranges); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_frame_data() +{ + return GetCachedSectionData (flagsGotDebugFrameData, eSectionTypeDWARFDebugFrame, m_data_debug_frame); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_info_data() +{ + return GetCachedSectionData (flagsGotDebugInfoData, eSectionTypeDWARFDebugInfo, m_data_debug_info); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_line_data() +{ + return GetCachedSectionData (flagsGotDebugLineData, eSectionTypeDWARFDebugLine, m_data_debug_line); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_loc_data() +{ + return GetCachedSectionData (flagsGotDebugLocData, eSectionTypeDWARFDebugLoc, m_data_debug_loc); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_ranges_data() +{ + return GetCachedSectionData (flagsGotDebugRangesData, eSectionTypeDWARFDebugRanges, m_data_debug_ranges); +} + +const DataExtractor& +SymbolFileDWARF::get_debug_str_data() +{ + return GetCachedSectionData (flagsGotDebugStrData, eSectionTypeDWARFDebugStr, m_data_debug_str); +} + +const DataExtractor& +SymbolFileDWARF::get_apple_names_data() +{ + return GetCachedSectionData (flagsGotAppleNamesData, eSectionTypeDWARFAppleNames, m_data_apple_names); +} + +const DataExtractor& +SymbolFileDWARF::get_apple_types_data() +{ + return GetCachedSectionData (flagsGotAppleTypesData, eSectionTypeDWARFAppleTypes, m_data_apple_types); +} + +const DataExtractor& +SymbolFileDWARF::get_apple_namespaces_data() +{ + return GetCachedSectionData (flagsGotAppleNamespacesData, eSectionTypeDWARFAppleNamespaces, m_data_apple_namespaces); +} + +const DataExtractor& +SymbolFileDWARF::get_apple_objc_data() +{ + return GetCachedSectionData (flagsGotAppleObjCData, eSectionTypeDWARFAppleObjC, m_data_apple_objc); +} + + +DWARFDebugAbbrev* +SymbolFileDWARF::DebugAbbrev() +{ + if (m_abbr.get() == NULL) + { + const DataExtractor &debug_abbrev_data = get_debug_abbrev_data(); + if (debug_abbrev_data.GetByteSize() > 0) + { + m_abbr.reset(new DWARFDebugAbbrev()); + if (m_abbr.get()) + m_abbr->Parse(debug_abbrev_data); + } + } + return m_abbr.get(); +} + +const DWARFDebugAbbrev* +SymbolFileDWARF::DebugAbbrev() const +{ + return m_abbr.get(); +} + + +DWARFDebugInfo* +SymbolFileDWARF::DebugInfo() +{ + if (m_info.get() == NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); + if (get_debug_info_data().GetByteSize() > 0) + { + m_info.reset(new DWARFDebugInfo()); + if (m_info.get()) + { + m_info->SetDwarfData(this); + } + } + } + return m_info.get(); +} + +const DWARFDebugInfo* +SymbolFileDWARF::DebugInfo() const +{ + return m_info.get(); +} + +DWARFCompileUnit* +SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) +{ + DWARFDebugInfo* info = DebugInfo(); + if (info) + { + if (GetDebugMapSymfile ()) + { + // The debug map symbol file made the compile units for this DWARF + // file which is .o file with DWARF in it, and we should have + // only 1 compile unit which is at offset zero in the DWARF. + // TODO: modify to support LTO .o files where each .o file might + // have multiple DW_TAG_compile_unit tags. + return info->GetCompileUnit(0).get(); + } + else + { + // Just a normal DWARF file whose user ID for the compile unit is + // the DWARF offset itself + return info->GetCompileUnit((dw_offset_t)comp_unit->GetID()).get(); + } + } + return NULL; +} + + +DWARFDebugRanges* +SymbolFileDWARF::DebugRanges() +{ + if (m_ranges.get() == NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, "%s this = %p", __PRETTY_FUNCTION__, this); + if (get_debug_ranges_data().GetByteSize() > 0) + { + m_ranges.reset(new DWARFDebugRanges()); + if (m_ranges.get()) + m_ranges->Extract(this); + } + } + return m_ranges.get(); +} + +const DWARFDebugRanges* +SymbolFileDWARF::DebugRanges() const +{ + return m_ranges.get(); +} + +lldb::CompUnitSP +SymbolFileDWARF::ParseCompileUnit (DWARFCompileUnit* dwarf_cu, uint32_t cu_idx) +{ + CompUnitSP cu_sp; + if (dwarf_cu) + { + CompileUnit *comp_unit = (CompileUnit*)dwarf_cu->GetUserData(); + if (comp_unit) + { + // We already parsed this compile unit, had out a shared pointer to it + cu_sp = comp_unit->shared_from_this(); + } + else + { + if (GetDebugMapSymfile ()) + { + // Let the debug map create the compile unit + cu_sp = m_debug_map_symfile->GetCompileUnit(this); + dwarf_cu->SetUserData(cu_sp.get()); + } + else + { + ModuleSP module_sp (m_obj_file->GetModule()); + if (module_sp) + { + const DWARFDebugInfoEntry * cu_die = dwarf_cu->GetCompileUnitDIEOnly (); + if (cu_die) + { + const char * cu_die_name = cu_die->GetName(this, dwarf_cu); + const char * cu_comp_dir = cu_die->GetAttributeValueAsString(this, dwarf_cu, DW_AT_comp_dir, NULL); + LanguageType cu_language = (LanguageType)cu_die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_language, 0); + if (cu_die_name) + { + std::string ramapped_file; + FileSpec cu_file_spec; + + if (cu_die_name[0] == '/' || cu_comp_dir == NULL || cu_comp_dir[0] == '\0') + { + // If we have a full path to the compile unit, we don't need to resolve + // the file. This can be expensive e.g. when the source files are NFS mounted. + if (module_sp->RemapSourceFile(cu_die_name, ramapped_file)) + cu_file_spec.SetFile (ramapped_file.c_str(), false); + else + cu_file_spec.SetFile (cu_die_name, false); + } + else + { + std::string fullpath(cu_comp_dir); + if (*fullpath.rbegin() != '/') + fullpath += '/'; + fullpath += cu_die_name; + if (module_sp->RemapSourceFile (fullpath.c_str(), ramapped_file)) + cu_file_spec.SetFile (ramapped_file.c_str(), false); + else + cu_file_spec.SetFile (fullpath.c_str(), false); + } + + cu_sp.reset(new CompileUnit (module_sp, + dwarf_cu, + cu_file_spec, + MakeUserID(dwarf_cu->GetOffset()), + cu_language)); + if (cu_sp) + { + dwarf_cu->SetUserData(cu_sp.get()); + + // Figure out the compile unit index if we weren't given one + if (cu_idx == UINT32_MAX) + DebugInfo()->GetCompileUnit(dwarf_cu->GetOffset(), &cu_idx); + + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(cu_idx, cu_sp); + } + } + } + } + } + } + } + return cu_sp; +} + +uint32_t +SymbolFileDWARF::GetNumCompileUnits() +{ + DWARFDebugInfo* info = DebugInfo(); + if (info) + return info->GetNumCompileUnits(); + return 0; +} + +CompUnitSP +SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) +{ + CompUnitSP cu_sp; + DWARFDebugInfo* info = DebugInfo(); + if (info) + { + DWARFCompileUnit* dwarf_cu = info->GetCompileUnitAtIndex(cu_idx); + if (dwarf_cu) + cu_sp = ParseCompileUnit(dwarf_cu, cu_idx); + } + return cu_sp; +} + +static void +AddRangesToBlock (Block& block, + DWARFDebugRanges::RangeList& ranges, + addr_t block_base_addr) +{ + const size_t num_ranges = ranges.GetSize(); + for (size_t i = 0; i<num_ranges; ++i) + { + const DWARFDebugRanges::Range &range = ranges.GetEntryRef (i); + const addr_t range_base = range.GetRangeBase(); + assert (range_base >= block_base_addr); + block.AddRange(Block::Range (range_base - block_base_addr, range.GetByteSize()));; + } + block.FinalizeRanges (); +} + + +Function * +SymbolFileDWARF::ParseCompileUnitFunction (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die) +{ + DWARFDebugRanges::RangeList func_ranges; + const char *name = NULL; + const char *mangled = NULL; + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + DWARFExpression frame_base; + + assert (die->Tag() == DW_TAG_subprogram); + + if (die->Tag() != DW_TAG_subprogram) + return NULL; + + if (die->GetDIENamesAndRanges (this, + dwarf_cu, + name, + mangled, + func_ranges, + decl_file, + decl_line, + decl_column, + call_file, + call_line, + call_column, + &frame_base)) + { + // Union of all ranges in the function DIE (if the function is discontiguous) + AddressRange func_range; + lldb::addr_t lowest_func_addr = func_ranges.GetMinRangeBase (0); + lldb::addr_t highest_func_addr = func_ranges.GetMaxRangeEnd (0); + if (lowest_func_addr != LLDB_INVALID_ADDRESS && lowest_func_addr <= highest_func_addr) + { + ModuleSP module_sp (m_obj_file->GetModule()); + func_range.GetBaseAddress().ResolveAddressUsingFileSections (lowest_func_addr, module_sp->GetSectionList()); + if (func_range.GetBaseAddress().IsValid()) + func_range.SetByteSize(highest_func_addr - lowest_func_addr); + } + + if (func_range.GetBaseAddress().IsValid()) + { + Mangled func_name; + if (mangled) + func_name.SetValue(ConstString(mangled), true); + else if (name) + func_name.SetValue(ConstString(name), false); + + FunctionSP func_sp; + std::unique_ptr<Declaration> decl_ap; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_ap.reset(new Declaration (sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), + decl_line, + decl_column)); + + // Supply the type _only_ if it has already been parsed + Type *func_type = m_die_to_type.lookup (die); + + assert(func_type == NULL || func_type != DIE_IS_BEING_PARSED); + + if (FixupAddress (func_range.GetBaseAddress())) + { + const user_id_t func_user_id = MakeUserID(die->GetOffset()); + func_sp.reset(new Function (sc.comp_unit, + MakeUserID(func_user_id), // UserID is the DIE offset + MakeUserID(func_user_id), + func_name, + func_type, + func_range)); // first address range + + if (func_sp.get() != NULL) + { + if (frame_base.IsValid()) + func_sp->GetFrameBaseExpression() = frame_base; + sc.comp_unit->AddFunction(func_sp); + return func_sp.get(); + } + } + } + } + return NULL; +} + +bool +SymbolFileDWARF::FixupAddress (Address &addr) +{ + SymbolFileDWARFDebugMap * debug_map_symfile = GetDebugMapSymfile (); + if (debug_map_symfile) + { + return debug_map_symfile->LinkOSOAddress(addr); + } + // This is a normal DWARF file, no address fixups need to happen + return true; +} +lldb::LanguageType +SymbolFileDWARF::ParseCompileUnitLanguage (const SymbolContext& sc) +{ + assert (sc.comp_unit); + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); + if (dwarf_cu) + { + const DWARFDebugInfoEntry *die = dwarf_cu->GetCompileUnitDIEOnly(); + if (die) + { + const uint32_t language = die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_language, 0); + if (language) + return (lldb::LanguageType)language; + } + } + return eLanguageTypeUnknown; +} + +size_t +SymbolFileDWARF::ParseCompileUnitFunctions(const SymbolContext &sc) +{ + assert (sc.comp_unit); + size_t functions_added = 0; + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); + if (dwarf_cu) + { + DWARFDIECollection function_dies; + const size_t num_functions = dwarf_cu->AppendDIEsWithTag (DW_TAG_subprogram, function_dies); + size_t func_idx; + for (func_idx = 0; func_idx < num_functions; ++func_idx) + { + const DWARFDebugInfoEntry *die = function_dies.GetDIEPtrAtIndex(func_idx); + if (sc.comp_unit->FindFunctionByUID (MakeUserID(die->GetOffset())).get() == NULL) + { + if (ParseCompileUnitFunction(sc, dwarf_cu, die)) + ++functions_added; + } + } + //FixupTypes(); + } + return functions_added; +} + +bool +SymbolFileDWARF::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList& support_files) +{ + assert (sc.comp_unit); + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); + if (dwarf_cu) + { + const DWARFDebugInfoEntry * cu_die = dwarf_cu->GetCompileUnitDIEOnly(); + + if (cu_die) + { + const char * cu_comp_dir = cu_die->GetAttributeValueAsString(this, dwarf_cu, DW_AT_comp_dir, NULL); + dw_offset_t stmt_list = cu_die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_stmt_list, DW_INVALID_OFFSET); + + // All file indexes in DWARF are one based and a file of index zero is + // supposed to be the compile unit itself. + support_files.Append (*sc.comp_unit); + + return DWARFDebugLine::ParseSupportFiles(sc.comp_unit->GetModule(), get_debug_line_data(), cu_comp_dir, stmt_list, support_files); + } + } + return false; +} + +struct ParseDWARFLineTableCallbackInfo +{ + LineTable* line_table; + std::unique_ptr<LineSequence> sequence_ap; +}; + +//---------------------------------------------------------------------- +// ParseStatementTableCallback +//---------------------------------------------------------------------- +static void +ParseDWARFLineTableCallback(dw_offset_t offset, const DWARFDebugLine::State& state, void* userData) +{ + if (state.row == DWARFDebugLine::State::StartParsingLineTable) + { + // Just started parsing the line table + } + else if (state.row == DWARFDebugLine::State::DoneParsingLineTable) + { + // Done parsing line table, nothing to do for the cleanup + } + else + { + ParseDWARFLineTableCallbackInfo* info = (ParseDWARFLineTableCallbackInfo*)userData; + LineTable* line_table = info->line_table; + + // If this is our first time here, we need to create a + // sequence container. + if (!info->sequence_ap.get()) + { + info->sequence_ap.reset(line_table->CreateLineSequenceContainer()); + assert(info->sequence_ap.get()); + } + line_table->AppendLineEntryToSequence (info->sequence_ap.get(), + state.address, + state.line, + state.column, + state.file, + state.is_stmt, + state.basic_block, + state.prologue_end, + state.epilogue_begin, + state.end_sequence); + if (state.end_sequence) + { + // First, put the current sequence into the line table. + line_table->InsertSequence(info->sequence_ap.get()); + // Then, empty it to prepare for the next sequence. + info->sequence_ap->Clear(); + } + } +} + +bool +SymbolFileDWARF::ParseCompileUnitLineTable (const SymbolContext &sc) +{ + assert (sc.comp_unit); + if (sc.comp_unit->GetLineTable() != NULL) + return true; + + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); + if (dwarf_cu) + { + const DWARFDebugInfoEntry *dwarf_cu_die = dwarf_cu->GetCompileUnitDIEOnly(); + if (dwarf_cu_die) + { + const dw_offset_t cu_line_offset = dwarf_cu_die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_stmt_list, DW_INVALID_OFFSET); + if (cu_line_offset != DW_INVALID_OFFSET) + { + std::unique_ptr<LineTable> line_table_ap(new LineTable(sc.comp_unit)); + if (line_table_ap.get()) + { + ParseDWARFLineTableCallbackInfo info; + info.line_table = line_table_ap.get(); + lldb::offset_t offset = cu_line_offset; + DWARFDebugLine::ParseStatementTable(get_debug_line_data(), &offset, ParseDWARFLineTableCallback, &info); + if (m_debug_map_symfile) + { + // We have an object file that has a line table with addresses + // that are not linked. We need to link the line table and convert + // the addresses that are relative to the .o file into addresses + // for the main executable. + sc.comp_unit->SetLineTable (m_debug_map_symfile->LinkOSOLineTable (this, line_table_ap.get())); + } + else + { + sc.comp_unit->SetLineTable(line_table_ap.release()); + return true; + } + } + } + } + } + return false; +} + +size_t +SymbolFileDWARF::ParseFunctionBlocks +( + const SymbolContext& sc, + Block *parent_block, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + addr_t subprogram_low_pc, + uint32_t depth +) +{ + size_t blocks_added = 0; + while (die != NULL) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_inlined_subroutine: + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + { + Block *block = NULL; + if (tag == DW_TAG_subprogram) + { + // Skip any DW_TAG_subprogram DIEs that are inside + // of a normal or inlined functions. These will be + // parsed on their own as separate entities. + + if (depth > 0) + break; + + block = parent_block; + } + else + { + BlockSP block_sp(new Block (MakeUserID(die->GetOffset()))); + parent_block->AddChild(block_sp); + block = block_sp.get(); + } + DWARFDebugRanges::RangeList ranges; + const char *name = NULL; + const char *mangled_name = NULL; + + int decl_file = 0; + int decl_line = 0; + int decl_column = 0; + int call_file = 0; + int call_line = 0; + int call_column = 0; + if (die->GetDIENamesAndRanges (this, + dwarf_cu, + name, + mangled_name, + ranges, + decl_file, decl_line, decl_column, + call_file, call_line, call_column)) + { + if (tag == DW_TAG_subprogram) + { + assert (subprogram_low_pc == LLDB_INVALID_ADDRESS); + subprogram_low_pc = ranges.GetMinRangeBase(0); + } + else if (tag == DW_TAG_inlined_subroutine) + { + // We get called here for inlined subroutines in two ways. + // The first time is when we are making the Function object + // for this inlined concrete instance. Since we're creating a top level block at + // here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we need to + // adjust the containing address. + // The second time is when we are parsing the blocks inside the function that contains + // the inlined concrete instance. Since these will be blocks inside the containing "real" + // function the offset will be for that function. + if (subprogram_low_pc == LLDB_INVALID_ADDRESS) + { + subprogram_low_pc = ranges.GetMinRangeBase(0); + } + } + + AddRangesToBlock (*block, ranges, subprogram_low_pc); + + if (tag != DW_TAG_subprogram && (name != NULL || mangled_name != NULL)) + { + std::unique_ptr<Declaration> decl_ap; + if (decl_file != 0 || decl_line != 0 || decl_column != 0) + decl_ap.reset(new Declaration(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(decl_file), + decl_line, decl_column)); + + std::unique_ptr<Declaration> call_ap; + if (call_file != 0 || call_line != 0 || call_column != 0) + call_ap.reset(new Declaration(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(call_file), + call_line, call_column)); + + block->SetInlinedFunctionInfo (name, mangled_name, decl_ap.get(), call_ap.get()); + } + + ++blocks_added; + + if (die->HasChildren()) + { + blocks_added += ParseFunctionBlocks (sc, + block, + dwarf_cu, + die->GetFirstChild(), + subprogram_low_pc, + depth + 1); + } + } + } + break; + default: + break; + } + + // Only parse siblings of the block if we are not at depth zero. A depth + // of zero indicates we are currently parsing the top level + // DW_TAG_subprogram DIE + + if (depth == 0) + die = NULL; + else + die = die->GetSibling(); + } + return blocks_added; +} + +bool +SymbolFileDWARF::ParseTemplateDIE (DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + ClangASTContext::TemplateParameterInfos &template_param_infos) +{ + const dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: + { + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes (this, + dwarf_cu, + fixed_form_sizes, + attributes); + const char *name = NULL; + Type *lldb_type = NULL; + ClangASTType clang_type; + uint64_t uval64 = 0; + bool uval64_valid = false; + if (num_attributes > 0) + { + DWARFFormValue form_value; + for (size_t i=0; i<num_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + + switch (attr) + { + case DW_AT_name: + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + name = form_value.AsCString(&get_debug_str_data()); + break; + + case DW_AT_type: + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + const dw_offset_t type_die_offset = form_value.Reference(dwarf_cu); + lldb_type = ResolveTypeUID(type_die_offset); + if (lldb_type) + clang_type = lldb_type->GetClangForwardType(); + } + break; + + case DW_AT_const_value: + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + uval64_valid = true; + uval64 = form_value.Unsigned(); + } + break; + default: + break; + } + } + + clang::ASTContext *ast = GetClangASTContext().getASTContext(); + if (!clang_type) + clang_type = GetClangASTContext().GetBasicType(eBasicTypeVoid); + + if (clang_type) + { + bool is_signed = false; + if (name && name[0]) + template_param_infos.names.push_back(name); + else + template_param_infos.names.push_back(NULL); + + if (tag == DW_TAG_template_value_parameter && + lldb_type != NULL && + clang_type.IsIntegerType (is_signed) && + uval64_valid) + { + llvm::APInt apint (lldb_type->GetByteSize() * 8, uval64, is_signed); + template_param_infos.args.push_back (clang::TemplateArgument (*ast, + llvm::APSInt(apint), + clang_type.GetQualType())); + } + else + { + template_param_infos.args.push_back (clang::TemplateArgument (clang_type.GetQualType())); + } + } + else + { + return false; + } + + } + } + return true; + + default: + break; + } + return false; +} + +bool +SymbolFileDWARF::ParseTemplateParameterInfos (DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + ClangASTContext::TemplateParameterInfos &template_param_infos) +{ + + if (parent_die == NULL) + return false; + + Args template_parameter_names; + for (const DWARFDebugInfoEntry *die = parent_die->GetFirstChild(); + die != NULL; + die = die->GetSibling()) + { + const dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: + ParseTemplateDIE (dwarf_cu, die, template_param_infos); + break; + + default: + break; + } + } + if (template_param_infos.args.empty()) + return false; + return template_param_infos.args.size() == template_param_infos.names.size(); +} + +clang::ClassTemplateDecl * +SymbolFileDWARF::ParseClassTemplateDecl (clang::DeclContext *decl_ctx, + lldb::AccessType access_type, + const char *parent_name, + int tag_decl_kind, + const ClangASTContext::TemplateParameterInfos &template_param_infos) +{ + if (template_param_infos.IsValid()) + { + std::string template_basename(parent_name); + template_basename.erase (template_basename.find('<')); + ClangASTContext &ast = GetClangASTContext(); + + return ast.CreateClassTemplateDecl (decl_ctx, + access_type, + template_basename.c_str(), + tag_decl_kind, + template_param_infos); + } + return NULL; +} + +class SymbolFileDWARF::DelayedAddObjCClassProperty +{ +public: + DelayedAddObjCClassProperty + ( + const ClangASTType &class_opaque_type, + const char *property_name, + const ClangASTType &property_opaque_type, // The property type is only required if you don't have an ivar decl + clang::ObjCIvarDecl *ivar_decl, + const char *property_setter_name, + const char *property_getter_name, + uint32_t property_attributes, + const ClangASTMetadata *metadata + ) : + m_class_opaque_type (class_opaque_type), + m_property_name (property_name), + m_property_opaque_type (property_opaque_type), + m_ivar_decl (ivar_decl), + m_property_setter_name (property_setter_name), + m_property_getter_name (property_getter_name), + m_property_attributes (property_attributes) + { + if (metadata != NULL) + { + m_metadata_ap.reset(new ClangASTMetadata()); + *m_metadata_ap = *metadata; + } + } + + DelayedAddObjCClassProperty (const DelayedAddObjCClassProperty &rhs) + { + *this = rhs; + } + + DelayedAddObjCClassProperty& operator= (const DelayedAddObjCClassProperty &rhs) + { + m_class_opaque_type = rhs.m_class_opaque_type; + m_property_name = rhs.m_property_name; + m_property_opaque_type = rhs.m_property_opaque_type; + m_ivar_decl = rhs.m_ivar_decl; + m_property_setter_name = rhs.m_property_setter_name; + m_property_getter_name = rhs.m_property_getter_name; + m_property_attributes = rhs.m_property_attributes; + + if (rhs.m_metadata_ap.get()) + { + m_metadata_ap.reset (new ClangASTMetadata()); + *m_metadata_ap = *rhs.m_metadata_ap; + } + return *this; + } + + bool + Finalize() + { + return m_class_opaque_type.AddObjCClassProperty (m_property_name, + m_property_opaque_type, + m_ivar_decl, + m_property_setter_name, + m_property_getter_name, + m_property_attributes, + m_metadata_ap.get()); + } +private: + ClangASTType m_class_opaque_type; + const char *m_property_name; + ClangASTType m_property_opaque_type; + clang::ObjCIvarDecl *m_ivar_decl; + const char *m_property_setter_name; + const char *m_property_getter_name; + uint32_t m_property_attributes; + std::unique_ptr<ClangASTMetadata> m_metadata_ap; +}; + +struct BitfieldInfo +{ + uint64_t bit_size; + uint64_t bit_offset; + + BitfieldInfo () : + bit_size (LLDB_INVALID_ADDRESS), + bit_offset (LLDB_INVALID_ADDRESS) + { + } + + bool IsValid () + { + return (bit_size != LLDB_INVALID_ADDRESS) && + (bit_offset != LLDB_INVALID_ADDRESS); + } +}; + + +bool +SymbolFileDWARF::ClassOrStructIsVirtual (DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die) +{ + if (parent_die) + { + for (const DWARFDebugInfoEntry *die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + dw_tag_t tag = die->Tag(); + bool check_virtuality = false; + switch (tag) + { + case DW_TAG_inheritance: + case DW_TAG_subprogram: + check_virtuality = true; + break; + default: + break; + } + if (check_virtuality) + { + if (die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_virtuality, 0) != 0) + return true; + } + } + } + return false; +} + +size_t +SymbolFileDWARF::ParseChildMembers +( + const SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + ClangASTType &class_clang_type, + const LanguageType class_language, + std::vector<clang::CXXBaseSpecifier *>& base_classes, + std::vector<int>& member_accessibilities, + DWARFDIECollection& member_function_dies, + DelayedPropertyList& delayed_properties, + AccessType& default_accessibility, + bool &is_a_class, + LayoutInfo &layout_info +) +{ + if (parent_die == NULL) + return 0; + + size_t count = 0; + const DWARFDebugInfoEntry *die; + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + uint32_t member_idx = 0; + BitfieldInfo last_field_info; + + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + dw_tag_t tag = die->Tag(); + + switch (tag) + { + case DW_TAG_member: + case DW_TAG_APPLE_property: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes (this, + dwarf_cu, + fixed_form_sizes, + attributes); + if (num_attributes > 0) + { + Declaration decl; + //DWARFExpression location; + const char *name = NULL; + const char *prop_name = NULL; + const char *prop_getter_name = NULL; + const char *prop_setter_name = NULL; + uint32_t prop_attributes = 0; + + + bool is_artificial = false; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + AccessType accessibility = eAccessNone; + uint32_t member_byte_offset = UINT32_MAX; + size_t byte_size = 0; + size_t bit_offset = 0; + size_t bit_size = 0; + bool is_external = false; // On DW_TAG_members, this means the member is static + uint32_t i; + for (i=0; i<num_attributes && !is_artificial; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_bit_offset: bit_offset = form_value.Unsigned(); break; + case DW_AT_bit_size: bit_size = form_value.Unsigned(); break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_data_member_location: + if (form_value.BlockData()) + { + Value initialValue(0); + Value memberOffset(0); + const DataExtractor& debug_info_data = get_debug_info_data(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate(NULL, // ExecutionContext * + NULL, // ClangExpressionVariableList * + NULL, // ClangExpressionDeclMap * + NULL, // RegisterContext * + debug_info_data, + block_offset, + block_length, + eRegisterKindDWARF, + &initialValue, + memberOffset, + NULL)) + { + member_byte_offset = memberOffset.ResolveValue(NULL).UInt(); + } + } + else + { + // With DWARF 3 and later, if the value is an integer constant, + // this form value is the offset in bytes from the beginning + // of the containing entity. + member_byte_offset = form_value.Unsigned(); + } + break; + + case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType (form_value.Unsigned()); break; + case DW_AT_artificial: is_artificial = form_value.Boolean(); break; + case DW_AT_APPLE_property_name: prop_name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_APPLE_property_getter: prop_getter_name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_APPLE_property_setter: prop_setter_name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_APPLE_property_attribute: prop_attributes = form_value.Unsigned(); break; + case DW_AT_external: is_external = form_value.Boolean(); break; + + default: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_mutable: + case DW_AT_visibility: + case DW_AT_sibling: + break; + } + } + } + + if (prop_name) + { + ConstString fixed_getter; + ConstString fixed_setter; + + // Check if the property getter/setter were provided as full + // names. We want basenames, so we extract them. + + if (prop_getter_name && prop_getter_name[0] == '-') + { + ObjCLanguageRuntime::MethodName prop_getter_method(prop_getter_name, true); + prop_getter_name = prop_getter_method.GetSelector().GetCString(); + } + + if (prop_setter_name && prop_setter_name[0] == '-') + { + ObjCLanguageRuntime::MethodName prop_setter_method(prop_setter_name, true); + prop_setter_name = prop_setter_method.GetSelector().GetCString(); + } + + // If the names haven't been provided, they need to be + // filled in. + + if (!prop_getter_name) + { + prop_getter_name = prop_name; + } + if (!prop_setter_name && prop_name[0] && !(prop_attributes & DW_APPLE_PROPERTY_readonly)) + { + StreamString ss; + + ss.Printf("set%c%s:", + toupper(prop_name[0]), + &prop_name[1]); + + fixed_setter.SetCString(ss.GetData()); + prop_setter_name = fixed_setter.GetCString(); + } + } + + // Clang has a DWARF generation bug where sometimes it + // represents fields that are references with bad byte size + // and bit size/offset information such as: + // + // DW_AT_byte_size( 0x00 ) + // DW_AT_bit_size( 0x40 ) + // DW_AT_bit_offset( 0xffffffffffffffc0 ) + // + // So check the bit offset to make sure it is sane, and if + // the values are not sane, remove them. If we don't do this + // then we will end up with a crash if we try to use this + // type in an expression when clang becomes unhappy with its + // recycled debug info. + + if (bit_offset > 128) + { + bit_size = 0; + bit_offset = 0; + } + + // FIXME: Make Clang ignore Objective-C accessibility for expressions + if (class_language == eLanguageTypeObjC || + class_language == eLanguageTypeObjC_plus_plus) + accessibility = eAccessNone; + + if (member_idx == 0 && !is_artificial && name && (strstr (name, "_vptr$") == name)) + { + // Not all compilers will mark the vtable pointer + // member as artificial (llvm-gcc). We can't have + // the virtual members in our classes otherwise it + // throws off all child offsets since we end up + // having and extra pointer sized member in our + // class layouts. + is_artificial = true; + } + + // Handle static members + if (is_external && member_byte_offset == UINT32_MAX) + { + Type *var_type = ResolveTypeUID(encoding_uid); + + if (var_type) + { + if (accessibility == eAccessNone) + accessibility = eAccessPublic; + class_clang_type.AddVariableToRecordType (name, + var_type->GetClangLayoutType(), + accessibility); + } + break; + } + + if (is_artificial == false) + { + Type *member_type = ResolveTypeUID(encoding_uid); + + clang::FieldDecl *field_decl = NULL; + if (tag == DW_TAG_member) + { + if (member_type) + { + if (accessibility == eAccessNone) + accessibility = default_accessibility; + member_accessibilities.push_back(accessibility); + + BitfieldInfo this_field_info; + + this_field_info.bit_size = bit_size; + + if (member_byte_offset != UINT32_MAX || bit_size != 0) + { + ///////////////////////////////////////////////////////////// + // How to locate a field given the DWARF debug information + // + // AT_byte_size indicates the size of the word in which the + // bit offset must be interpreted. + // + // AT_data_member_location indicates the byte offset of the + // word from the base address of the structure. + // + // AT_bit_offset indicates how many bits into the word + // (according to the host endianness) the low-order bit of + // the field starts. AT_bit_offset can be negative. + // + // AT_bit_size indicates the size of the field in bits. + ///////////////////////////////////////////////////////////// + + this_field_info.bit_offset = 0; + + this_field_info.bit_offset += (member_byte_offset == UINT32_MAX ? 0 : (member_byte_offset * 8)); + + if (GetObjectFile()->GetByteOrder() == eByteOrderLittle) + { + this_field_info.bit_offset += byte_size * 8; + this_field_info.bit_offset -= (bit_offset + bit_size); + } + else + { + this_field_info.bit_offset += bit_offset; + } + } + + // If the member to be emitted did not start on a character boundary and there is + // empty space between the last field and this one, then we need to emit an + // anonymous member filling up the space up to its start. There are three cases + // here: + // + // 1 If the previous member ended on a character boundary, then we can emit an + // anonymous member starting at the most recent character boundary. + // + // 2 If the previous member did not end on a character boundary and the distance + // from the end of the previous member to the current member is less than a + // word width, then we can emit an anonymous member starting right after the + // previous member and right before this member. + // + // 3 If the previous member did not end on a character boundary and the distance + // from the end of the previous member to the current member is greater than + // or equal a word width, then we act as in Case 1. + + const uint64_t character_width = 8; + const uint64_t word_width = 32; + + if (this_field_info.IsValid()) + { + // Objective-C has invalid DW_AT_bit_offset values in older versions + // of clang, so we have to be careful and only insert unnammed bitfields + // if we have a new enough clang. + bool detect_unnamed_bitfields = true; + + if (class_language == eLanguageTypeObjC || class_language == eLanguageTypeObjC_plus_plus) + detect_unnamed_bitfields = dwarf_cu->Supports_unnamed_objc_bitfields (); + + if (detect_unnamed_bitfields) + { + BitfieldInfo anon_field_info; + + if ((this_field_info.bit_offset % character_width) != 0) // not char aligned + { + uint64_t last_field_end = 0; + + if (last_field_info.IsValid()) + last_field_end = last_field_info.bit_offset + last_field_info.bit_size; + + if (this_field_info.bit_offset != last_field_end) + { + if (((last_field_end % character_width) == 0) || // case 1 + (this_field_info.bit_offset - last_field_end >= word_width)) // case 3 + { + anon_field_info.bit_size = this_field_info.bit_offset % character_width; + anon_field_info.bit_offset = this_field_info.bit_offset - anon_field_info.bit_size; + } + else // case 2 + { + anon_field_info.bit_size = this_field_info.bit_offset - last_field_end; + anon_field_info.bit_offset = last_field_end; + } + } + } + + if (anon_field_info.IsValid()) + { + clang::FieldDecl *unnamed_bitfield_decl = class_clang_type.AddFieldToRecordType (NULL, + GetClangASTContext().GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, word_width), + accessibility, + anon_field_info.bit_size); + + layout_info.field_offsets.insert(std::make_pair(unnamed_bitfield_decl, anon_field_info.bit_offset)); + } + } + } + + ClangASTType member_clang_type = member_type->GetClangLayoutType(); + + { + // Older versions of clang emit array[0] and array[1] in the same way (<rdar://problem/12566646>). + // If the current field is at the end of the structure, then there is definitely no room for extra + // elements and we override the type to array[0]. + + ClangASTType member_array_element_type; + uint64_t member_array_size; + bool member_array_is_incomplete; + + if (member_clang_type.IsArrayType(&member_array_element_type, + &member_array_size, + &member_array_is_incomplete) && + !member_array_is_incomplete) + { + uint64_t parent_byte_size = parent_die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_byte_size, UINT64_MAX); + + if (member_byte_offset >= parent_byte_size) + { + if (member_array_size != 1) + { + GetObjectFile()->GetModule()->ReportError ("0x%8.8" PRIx64 ": DW_TAG_member '%s' refers to type 0x%8.8" PRIx64 " which extends beyond the bounds of 0x%8.8" PRIx64, + MakeUserID(die->GetOffset()), + name, + encoding_uid, + MakeUserID(parent_die->GetOffset())); + } + + member_clang_type = GetClangASTContext().CreateArrayType(member_array_element_type, 0, false); + } + } + } + + field_decl = class_clang_type.AddFieldToRecordType (name, + member_clang_type, + accessibility, + bit_size); + + GetClangASTContext().SetMetadataAsUserID (field_decl, MakeUserID(die->GetOffset())); + + if (this_field_info.IsValid()) + { + layout_info.field_offsets.insert(std::make_pair(field_decl, this_field_info.bit_offset)); + last_field_info = this_field_info; + } + } + else + { + if (name) + GetObjectFile()->GetModule()->ReportError ("0x%8.8" PRIx64 ": DW_TAG_member '%s' refers to type 0x%8.8" PRIx64 " which was unable to be parsed", + MakeUserID(die->GetOffset()), + name, + encoding_uid); + else + GetObjectFile()->GetModule()->ReportError ("0x%8.8" PRIx64 ": DW_TAG_member refers to type 0x%8.8" PRIx64 " which was unable to be parsed", + MakeUserID(die->GetOffset()), + encoding_uid); + } + } + + if (prop_name != NULL) + { + clang::ObjCIvarDecl *ivar_decl = NULL; + + if (field_decl) + { + ivar_decl = clang::dyn_cast<clang::ObjCIvarDecl>(field_decl); + assert (ivar_decl != NULL); + } + + ClangASTMetadata metadata; + metadata.SetUserID (MakeUserID(die->GetOffset())); + delayed_properties.push_back(DelayedAddObjCClassProperty(class_clang_type, + prop_name, + member_type->GetClangLayoutType(), + ivar_decl, + prop_setter_name, + prop_getter_name, + prop_attributes, + &metadata)); + + if (ivar_decl) + GetClangASTContext().SetMetadataAsUserID (ivar_decl, MakeUserID(die->GetOffset())); + } + } + } + ++member_idx; + } + break; + + case DW_TAG_subprogram: + // Let the type parsing code handle this one for us. + member_function_dies.Append (die); + break; + + case DW_TAG_inheritance: + { + is_a_class = true; + if (default_accessibility == eAccessNone) + default_accessibility = eAccessPrivate; + // TODO: implement DW_TAG_inheritance type parsing + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes (this, + dwarf_cu, + fixed_form_sizes, + attributes); + if (num_attributes > 0) + { + Declaration decl; + DWARFExpression location; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + AccessType accessibility = default_accessibility; + bool is_virtual = false; + bool is_base_of_class = true; + off_t member_byte_offset = 0; + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_data_member_location: + if (form_value.BlockData()) + { + Value initialValue(0); + Value memberOffset(0); + const DataExtractor& debug_info_data = get_debug_info_data(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate (NULL, + NULL, + NULL, + NULL, + debug_info_data, + block_offset, + block_length, + eRegisterKindDWARF, + &initialValue, + memberOffset, + NULL)) + { + member_byte_offset = memberOffset.ResolveValue(NULL).UInt(); + } + } + else + { + // With DWARF 3 and later, if the value is an integer constant, + // this form value is the offset in bytes from the beginning + // of the containing entity. + member_byte_offset = form_value.Unsigned(); + } + break; + + case DW_AT_accessibility: + accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); + break; + + case DW_AT_virtuality: + is_virtual = form_value.Boolean(); + break; + + case DW_AT_sibling: + break; + + default: + break; + } + } + } + + Type *base_class_type = ResolveTypeUID(encoding_uid); + assert(base_class_type); + + ClangASTType base_class_clang_type = base_class_type->GetClangFullType(); + assert (base_class_clang_type); + if (class_language == eLanguageTypeObjC) + { + class_clang_type.SetObjCSuperClass(base_class_clang_type); + } + else + { + base_classes.push_back (base_class_clang_type.CreateBaseClassSpecifier (accessibility, + is_virtual, + is_base_of_class)); + + if (is_virtual) + { + layout_info.vbase_offsets.insert(std::make_pair(class_clang_type.GetAsCXXRecordDecl(), + clang::CharUnits::fromQuantity(member_byte_offset))); + } + else + { + layout_info.base_offsets.insert(std::make_pair(class_clang_type.GetAsCXXRecordDecl(), + clang::CharUnits::fromQuantity(member_byte_offset))); + } + } + } + } + break; + + default: + break; + } + } + + return count; +} + + +clang::DeclContext* +SymbolFileDWARF::GetClangDeclContextContainingTypeUID (lldb::user_id_t type_uid) +{ + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info && UserIDMatches(type_uid)) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = debug_info->GetDIEPtr(type_uid, &cu_sp); + if (die) + return GetClangDeclContextContainingDIE (cu_sp.get(), die, NULL); + } + return NULL; +} + +clang::DeclContext* +SymbolFileDWARF::GetClangDeclContextForTypeUID (const lldb_private::SymbolContext &sc, lldb::user_id_t type_uid) +{ + if (UserIDMatches(type_uid)) + return GetClangDeclContextForDIEOffset (sc, type_uid); + return NULL; +} + +Type* +SymbolFileDWARF::ResolveTypeUID (lldb::user_id_t type_uid) +{ + if (UserIDMatches(type_uid)) + { + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* type_die = debug_info->GetDIEPtr(type_uid, &cu_sp); + const bool assert_not_being_parsed = true; + return ResolveTypeUID (cu_sp.get(), type_die, assert_not_being_parsed); + } + } + return NULL; +} + +Type* +SymbolFileDWARF::ResolveTypeUID (DWARFCompileUnit* cu, const DWARFDebugInfoEntry* die, bool assert_not_being_parsed) +{ + if (die != NULL) + { + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); + if (log) + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'", + die->GetOffset(), + DW_TAG_value_to_name(die->Tag()), + die->GetName(this, cu)); + + // We might be coming in in the middle of a type tree (a class + // withing a class, an enum within a class), so parse any needed + // parent DIEs before we get to this one... + const DWARFDebugInfoEntry *decl_ctx_die = GetDeclContextDIEContainingDIE (cu, die); + switch (decl_ctx_die->Tag()) + { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + { + // Get the type, which could be a forward declaration + if (log) + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' resolve parent forward type for 0x%8.8x", + die->GetOffset(), + DW_TAG_value_to_name(die->Tag()), + die->GetName(this, cu), + decl_ctx_die->GetOffset()); +// +// Type *parent_type = ResolveTypeUID (cu, decl_ctx_die, assert_not_being_parsed); +// if (child_requires_parent_class_union_or_struct_to_be_completed(die->Tag())) +// { +// if (log) +// GetObjectFile()->GetModule()->LogMessage (log, +// "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' resolve parent full type for 0x%8.8x since die is a function", +// die->GetOffset(), +// DW_TAG_value_to_name(die->Tag()), +// die->GetName(this, cu), +// decl_ctx_die->GetOffset()); +// // Ask the type to complete itself if it already hasn't since if we +// // want a function (method or static) from a class, the class must +// // create itself and add it's own methods and class functions. +// if (parent_type) +// parent_type->GetClangFullType(); +// } + } + break; + + default: + break; + } + return ResolveType (cu, die); + } + return NULL; +} + +// This function is used when SymbolFileDWARFDebugMap owns a bunch of +// SymbolFileDWARF objects to detect if this DWARF file is the one that +// can resolve a clang_type. +bool +SymbolFileDWARF::HasForwardDeclForClangType (const ClangASTType &clang_type) +{ + ClangASTType clang_type_no_qualifiers = clang_type.RemoveFastQualifiers(); + const DWARFDebugInfoEntry* die = m_forward_decl_clang_type_to_die.lookup (clang_type_no_qualifiers.GetOpaqueQualType()); + return die != NULL; +} + + +bool +SymbolFileDWARF::ResolveClangOpaqueTypeDefinition (ClangASTType &clang_type) +{ + // We have a struct/union/class/enum that needs to be fully resolved. + ClangASTType clang_type_no_qualifiers = clang_type.RemoveFastQualifiers(); + const DWARFDebugInfoEntry* die = m_forward_decl_clang_type_to_die.lookup (clang_type_no_qualifiers.GetOpaqueQualType()); + if (die == NULL) + { + // We have already resolved this type... + return true; + } + // Once we start resolving this type, remove it from the forward declaration + // map in case anyone child members or other types require this type to get resolved. + // The type will get resolved when all of the calls to SymbolFileDWARF::ResolveClangOpaqueTypeDefinition + // are done. + m_forward_decl_clang_type_to_die.erase (clang_type_no_qualifiers.GetOpaqueQualType()); + + + // Disable external storage for this type so we don't get anymore + // clang::ExternalASTSource queries for this type. + clang_type.SetHasExternalStorage (false); + + DWARFDebugInfo* debug_info = DebugInfo(); + + DWARFCompileUnit *dwarf_cu = debug_info->GetCompileUnitContainingDIE (die->GetOffset()).get(); + Type *type = m_die_to_type.lookup (die); + + const dw_tag_t tag = die->Tag(); + + Log *log (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO|DWARF_LOG_TYPE_COMPLETION)); + if (log) + { + GetObjectFile()->GetModule()->LogMessageVerboseBacktrace (log, + "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...", + MakeUserID(die->GetOffset()), + DW_TAG_value_to_name(tag), + type->GetName().AsCString()); + + } + assert (clang_type); + DWARFDebugInfoEntry::Attributes attributes; + + switch (tag) + { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + { + LayoutInfo layout_info; + + { + if (die->HasChildren()) + { + + LanguageType class_language = eLanguageTypeUnknown; + if (clang_type.IsObjCObjectOrInterfaceType()) + { + class_language = eLanguageTypeObjC; + // For objective C we don't start the definition when + // the class is created. + clang_type.StartTagDeclarationDefinition (); + } + + int tag_decl_kind = -1; + AccessType default_accessibility = eAccessNone; + if (tag == DW_TAG_structure_type) + { + tag_decl_kind = clang::TTK_Struct; + default_accessibility = eAccessPublic; + } + else if (tag == DW_TAG_union_type) + { + tag_decl_kind = clang::TTK_Union; + default_accessibility = eAccessPublic; + } + else if (tag == DW_TAG_class_type) + { + tag_decl_kind = clang::TTK_Class; + default_accessibility = eAccessPrivate; + } + + SymbolContext sc(GetCompUnitForDWARFCompUnit(dwarf_cu)); + std::vector<clang::CXXBaseSpecifier *> base_classes; + std::vector<int> member_accessibilities; + bool is_a_class = false; + // Parse members and base classes first + DWARFDIECollection member_function_dies; + + DelayedPropertyList delayed_properties; + ParseChildMembers (sc, + dwarf_cu, + die, + clang_type, + class_language, + base_classes, + member_accessibilities, + member_function_dies, + delayed_properties, + default_accessibility, + is_a_class, + layout_info); + + // Now parse any methods if there were any... + size_t num_functions = member_function_dies.Size(); + if (num_functions > 0) + { + for (size_t i=0; i<num_functions; ++i) + { + ResolveType(dwarf_cu, member_function_dies.GetDIEPtrAtIndex(i)); + } + } + + if (class_language == eLanguageTypeObjC) + { + std::string class_str (clang_type.GetTypeName()); + if (!class_str.empty()) + { + + DIEArray method_die_offsets; + if (m_using_apple_tables) + { + if (m_apple_objc_ap.get()) + m_apple_objc_ap->FindByName(class_str.c_str(), method_die_offsets); + } + else + { + if (!m_indexed) + Index (); + + ConstString class_name (class_str.c_str()); + m_objc_class_selectors_index.Find (class_name, method_die_offsets); + } + + if (!method_die_offsets.empty()) + { + DWARFDebugInfo* debug_info = DebugInfo(); + + DWARFCompileUnit* method_cu = NULL; + const size_t num_matches = method_die_offsets.size(); + for (size_t i=0; i<num_matches; ++i) + { + const dw_offset_t die_offset = method_die_offsets[i]; + DWARFDebugInfoEntry *method_die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &method_cu); + + if (method_die) + ResolveType (method_cu, method_die); + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_objc accelerator table had bad die 0x%8.8x for '%s')\n", + die_offset, class_str.c_str()); + } + } + } + } + + for (DelayedPropertyList::iterator pi = delayed_properties.begin(), pe = delayed_properties.end(); + pi != pe; + ++pi) + pi->Finalize(); + } + } + + // If we have a DW_TAG_structure_type instead of a DW_TAG_class_type we + // need to tell the clang type it is actually a class. + if (class_language != eLanguageTypeObjC) + { + if (is_a_class && tag_decl_kind != clang::TTK_Class) + clang_type.SetTagTypeKind (clang::TTK_Class); + } + + // Since DW_TAG_structure_type gets used for both classes + // and structures, we may need to set any DW_TAG_member + // fields to have a "private" access if none was specified. + // When we parsed the child members we tracked that actual + // accessibility value for each DW_TAG_member in the + // "member_accessibilities" array. If the value for the + // member is zero, then it was set to the "default_accessibility" + // which for structs was "public". Below we correct this + // by setting any fields to "private" that weren't correctly + // set. + if (is_a_class && !member_accessibilities.empty()) + { + // This is a class and all members that didn't have + // their access specified are private. + clang_type.SetDefaultAccessForRecordFields (eAccessPrivate, + &member_accessibilities.front(), + member_accessibilities.size()); + } + + if (!base_classes.empty()) + { + clang_type.SetBaseClassesForClassType (&base_classes.front(), + base_classes.size()); + + // Clang will copy each CXXBaseSpecifier in "base_classes" + // so we have to free them all. + ClangASTType::DeleteBaseClassSpecifiers (&base_classes.front(), + base_classes.size()); + } + } + } + + clang_type.BuildIndirectFields (); + clang_type.CompleteTagDeclarationDefinition (); + + if (!layout_info.field_offsets.empty() || + !layout_info.base_offsets.empty() || + !layout_info.vbase_offsets.empty() ) + { + if (type) + layout_info.bit_size = type->GetByteSize() * 8; + if (layout_info.bit_size == 0) + layout_info.bit_size = die->GetAttributeValueAsUnsigned(this, dwarf_cu, DW_AT_byte_size, 0) * 8; + + clang::CXXRecordDecl *record_decl = clang_type.GetAsCXXRecordDecl(); + if (record_decl) + { + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::ResolveClangOpaqueTypeDefinition (clang_type = %p) caching layout info for record_decl = %p, bit_size = %" PRIu64 ", alignment = %" PRIu64 ", field_offsets[%u], base_offsets[%u], vbase_offsets[%u])", + clang_type.GetOpaqueQualType(), + record_decl, + layout_info.bit_size, + layout_info.alignment, + (uint32_t)layout_info.field_offsets.size(), + (uint32_t)layout_info.base_offsets.size(), + (uint32_t)layout_info.vbase_offsets.size()); + + uint32_t idx; + { + llvm::DenseMap <const clang::FieldDecl *, uint64_t>::const_iterator pos, end = layout_info.field_offsets.end(); + for (idx = 0, pos = layout_info.field_offsets.begin(); pos != end; ++pos, ++idx) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::ResolveClangOpaqueTypeDefinition (clang_type = %p) field[%u] = { bit_offset=%u, name='%s' }", + clang_type.GetOpaqueQualType(), + idx, + (uint32_t)pos->second, + pos->first->getNameAsString().c_str()); + } + } + + { + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits>::const_iterator base_pos, base_end = layout_info.base_offsets.end(); + for (idx = 0, base_pos = layout_info.base_offsets.begin(); base_pos != base_end; ++base_pos, ++idx) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::ResolveClangOpaqueTypeDefinition (clang_type = %p) base[%u] = { byte_offset=%u, name='%s' }", + clang_type.GetOpaqueQualType(), + idx, + (uint32_t)base_pos->second.getQuantity(), + base_pos->first->getNameAsString().c_str()); + } + } + { + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits>::const_iterator vbase_pos, vbase_end = layout_info.vbase_offsets.end(); + for (idx = 0, vbase_pos = layout_info.vbase_offsets.begin(); vbase_pos != vbase_end; ++vbase_pos, ++idx) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::ResolveClangOpaqueTypeDefinition (clang_type = %p) vbase[%u] = { byte_offset=%u, name='%s' }", + clang_type.GetOpaqueQualType(), + idx, + (uint32_t)vbase_pos->second.getQuantity(), + vbase_pos->first->getNameAsString().c_str()); + } + } + } + m_record_decl_to_layout_map.insert(std::make_pair(record_decl, layout_info)); + } + } + } + + return clang_type; + + case DW_TAG_enumeration_type: + clang_type.StartTagDeclarationDefinition (); + if (die->HasChildren()) + { + SymbolContext sc(GetCompUnitForDWARFCompUnit(dwarf_cu)); + bool is_signed = false; + clang_type.IsIntegerType(is_signed); + ParseChildEnumerators(sc, clang_type, is_signed, type->GetByteSize(), dwarf_cu, die); + } + clang_type.CompleteTagDeclarationDefinition (); + return clang_type; + + default: + assert(false && "not a forward clang type decl!"); + break; + } + return false; +} + +Type* +SymbolFileDWARF::ResolveType (DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry* type_die, bool assert_not_being_parsed) +{ + if (type_die != NULL) + { + Type *type = m_die_to_type.lookup (type_die); + + if (type == NULL) + type = GetTypeForDIE (dwarf_cu, type_die).get(); + + if (assert_not_being_parsed) + { + if (type != DIE_IS_BEING_PARSED) + return type; + + GetObjectFile()->GetModule()->ReportError ("Parsing a die that is being parsed die: 0x%8.8x: %s %s", + type_die->GetOffset(), + DW_TAG_value_to_name(type_die->Tag()), + type_die->GetName(this, dwarf_cu)); + + } + else + return type; + } + return NULL; +} + +CompileUnit* +SymbolFileDWARF::GetCompUnitForDWARFCompUnit (DWARFCompileUnit* dwarf_cu, uint32_t cu_idx) +{ + // Check if the symbol vendor already knows about this compile unit? + if (dwarf_cu->GetUserData() == NULL) + { + // The symbol vendor doesn't know about this compile unit, we + // need to parse and add it to the symbol vendor object. + return ParseCompileUnit(dwarf_cu, cu_idx).get(); + } + return (CompileUnit*)dwarf_cu->GetUserData(); +} + +bool +SymbolFileDWARF::GetFunction (DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry* func_die, SymbolContext& sc) +{ + sc.Clear(false); + // Check if the symbol vendor already knows about this compile unit? + sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, UINT32_MAX); + + sc.function = sc.comp_unit->FindFunctionByUID (MakeUserID(func_die->GetOffset())).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, dwarf_cu, func_die); + + if (sc.function) + { + sc.module_sp = sc.function->CalculateSymbolContextModule(); + return true; + } + + return false; +} + +uint32_t +SymbolFileDWARF::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "SymbolFileDWARF::ResolveSymbolContext (so_addr = { section = %p, offset = 0x%" PRIx64 " }, resolve_scope = 0x%8.8x)", + so_addr.GetSection().get(), + so_addr.GetOffset(), + resolve_scope); + uint32_t resolved = 0; + if (resolve_scope & ( eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextBlock | + eSymbolContextLineEntry)) + { + lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); + + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + const dw_offset_t cu_offset = debug_info->GetCompileUnitAranges().FindAddress(file_vm_addr); + if (cu_offset != DW_INVALID_OFFSET) + { + uint32_t cu_idx = DW_INVALID_INDEX; + DWARFCompileUnit* dwarf_cu = debug_info->GetCompileUnit(cu_offset, &cu_idx).get(); + if (dwarf_cu) + { + sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); + if (sc.comp_unit) + { + resolved |= eSymbolContextCompUnit; + + bool force_check_line_table = false; + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) + { + DWARFDebugInfoEntry *function_die = NULL; + DWARFDebugInfoEntry *block_die = NULL; + if (resolve_scope & eSymbolContextBlock) + { + dwarf_cu->LookupAddress(file_vm_addr, &function_die, &block_die); + } + else + { + dwarf_cu->LookupAddress(file_vm_addr, &function_die, NULL); + } + + if (function_die != NULL) + { + sc.function = sc.comp_unit->FindFunctionByUID (MakeUserID(function_die->GetOffset())).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, dwarf_cu, function_die); + } + else + { + // We might have had a compile unit that had discontiguous + // address ranges where the gaps are symbols that don't have + // any debug info. Discontiguous compile unit address ranges + // should only happen when there aren't other functions from + // other compile units in these gaps. This helps keep the size + // of the aranges down. + force_check_line_table = true; + } + + if (sc.function != NULL) + { + resolved |= eSymbolContextFunction; + + if (resolve_scope & eSymbolContextBlock) + { + Block& block = sc.function->GetBlock (true); + + if (block_die != NULL) + sc.block = block.FindBlockByID (MakeUserID(block_die->GetOffset())); + else + sc.block = block.FindBlockByID (MakeUserID(function_die->GetOffset())); + if (sc.block) + resolved |= eSymbolContextBlock; + } + } + } + + if ((resolve_scope & eSymbolContextLineEntry) || force_check_line_table) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + if (line_table != NULL) + { + // And address that makes it into this function should be in terms + // of this debug file if there is no debug map, or it will be an + // address in the .o file which needs to be fixed up to be in terms + // of the debug map executable. Either way, calling FixupAddress() + // will work for us. + Address exe_so_addr (so_addr); + if (FixupAddress(exe_so_addr)) + { + if (line_table->FindLineEntryByAddress (exe_so_addr, sc.line_entry)) + { + resolved |= eSymbolContextLineEntry; + } + } + } + } + + if (force_check_line_table && !(resolved & eSymbolContextLineEntry)) + { + // We might have had a compile unit that had discontiguous + // address ranges where the gaps are symbols that don't have + // any debug info. Discontiguous compile unit address ranges + // should only happen when there aren't other functions from + // other compile units in these gaps. This helps keep the size + // of the aranges down. + sc.comp_unit = NULL; + resolved &= ~eSymbolContextCompUnit; + } + } + else + { + GetObjectFile()->GetModule()->ReportWarning ("0x%8.8x: compile unit %u failed to create a valid lldb_private::CompileUnit class.", + cu_offset, + cu_idx); + } + } + } + } + } + return resolved; +} + + + +uint32_t +SymbolFileDWARF::ResolveSymbolContext(const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + const uint32_t prev_size = sc_list.GetSize(); + if (resolve_scope & eSymbolContextCompUnit) + { + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + uint32_t cu_idx; + DWARFCompileUnit* dwarf_cu = NULL; + + for (cu_idx = 0; (dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx)) != NULL; ++cu_idx) + { + CompileUnit *dc_cu = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); + const bool full_match = file_spec.GetDirectory(); + bool file_spec_matches_cu_file_spec = dc_cu != NULL && FileSpec::Equal(file_spec, *dc_cu, full_match); + if (check_inlines || file_spec_matches_cu_file_spec) + { + SymbolContext sc (m_obj_file->GetModule()); + sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, cu_idx); + if (sc.comp_unit) + { + uint32_t file_idx = UINT32_MAX; + + // If we are looking for inline functions only and we don't + // find it in the support files, we are done. + if (check_inlines) + { + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex (1, file_spec, true); + if (file_idx == UINT32_MAX) + continue; + } + + if (line != 0) + { + LineTable *line_table = sc.comp_unit->GetLineTable(); + + if (line_table != NULL && line != 0) + { + // We will have already looked up the file index if + // we are searching for inline entries. + if (!check_inlines) + file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex (1, file_spec, true); + + if (file_idx != UINT32_MAX) + { + uint32_t found_line; + uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex (0, file_idx, line, false, &sc.line_entry); + found_line = sc.line_entry.line; + + while (line_idx != UINT32_MAX) + { + sc.function = NULL; + sc.block = NULL; + if (resolve_scope & (eSymbolContextFunction | eSymbolContextBlock)) + { + const lldb::addr_t file_vm_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); + if (file_vm_addr != LLDB_INVALID_ADDRESS) + { + DWARFDebugInfoEntry *function_die = NULL; + DWARFDebugInfoEntry *block_die = NULL; + dwarf_cu->LookupAddress(file_vm_addr, &function_die, resolve_scope & eSymbolContextBlock ? &block_die : NULL); + + if (function_die != NULL) + { + sc.function = sc.comp_unit->FindFunctionByUID (MakeUserID(function_die->GetOffset())).get(); + if (sc.function == NULL) + sc.function = ParseCompileUnitFunction(sc, dwarf_cu, function_die); + } + + if (sc.function != NULL) + { + Block& block = sc.function->GetBlock (true); + + if (block_die != NULL) + sc.block = block.FindBlockByID (MakeUserID(block_die->GetOffset())); + else + sc.block = block.FindBlockByID (MakeUserID(function_die->GetOffset())); + } + } + } + + sc_list.Append(sc); + line_idx = line_table->FindLineEntryIndexByFileIndex (line_idx + 1, file_idx, found_line, true, &sc.line_entry); + } + } + } + else if (file_spec_matches_cu_file_spec && !check_inlines) + { + // only append the context if we aren't looking for inline call sites + // by file and line and if the file spec matches that of the compile unit + sc_list.Append(sc); + } + } + else if (file_spec_matches_cu_file_spec && !check_inlines) + { + // only append the context if we aren't looking for inline call sites + // by file and line and if the file spec matches that of the compile unit + sc_list.Append(sc); + } + + if (!check_inlines) + break; + } + } + } + } + } + return sc_list.GetSize() - prev_size; +} + +void +SymbolFileDWARF::Index () +{ + if (m_indexed) + return; + m_indexed = true; + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::Index (%s)", + GetObjectFile()->GetFileSpec().GetFilename().AsCString()); + + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + uint32_t cu_idx = 0; + const uint32_t num_compile_units = GetNumCompileUnits(); + for (cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); + + bool clear_dies = dwarf_cu->ExtractDIEsIfNeeded (false) > 1; + + dwarf_cu->Index (cu_idx, + m_function_basename_index, + m_function_fullname_index, + m_function_method_index, + m_function_selector_index, + m_objc_class_selectors_index, + m_global_index, + m_type_index, + m_namespace_index); + + // Keep memory down by clearing DIEs if this generate function + // caused them to be parsed + if (clear_dies) + dwarf_cu->ClearDIEs (true); + } + + m_function_basename_index.Finalize(); + m_function_fullname_index.Finalize(); + m_function_method_index.Finalize(); + m_function_selector_index.Finalize(); + m_objc_class_selectors_index.Finalize(); + m_global_index.Finalize(); + m_type_index.Finalize(); + m_namespace_index.Finalize(); + +#if defined (ENABLE_DEBUG_PRINTF) + StreamFile s(stdout, false); + s.Printf ("DWARF index for '%s':", + GetObjectFile()->GetFileSpec().GetPath().c_str()); + s.Printf("\nFunction basenames:\n"); m_function_basename_index.Dump (&s); + s.Printf("\nFunction fullnames:\n"); m_function_fullname_index.Dump (&s); + s.Printf("\nFunction methods:\n"); m_function_method_index.Dump (&s); + s.Printf("\nFunction selectors:\n"); m_function_selector_index.Dump (&s); + s.Printf("\nObjective C class selectors:\n"); m_objc_class_selectors_index.Dump (&s); + s.Printf("\nGlobals and statics:\n"); m_global_index.Dump (&s); + s.Printf("\nTypes:\n"); m_type_index.Dump (&s); + s.Printf("\nNamepaces:\n"); m_namespace_index.Dump (&s); +#endif + } +} + +bool +SymbolFileDWARF::NamespaceDeclMatchesThisSymbolFile (const ClangNamespaceDecl *namespace_decl) +{ + if (namespace_decl == NULL) + { + // Invalid namespace decl which means we aren't matching only things + // in this symbol file, so return true to indicate it matches this + // symbol file. + return true; + } + + clang::ASTContext *namespace_ast = namespace_decl->GetASTContext(); + + if (namespace_ast == NULL) + return true; // No AST in the "namespace_decl", return true since it + // could then match any symbol file, including this one + + if (namespace_ast == GetClangASTContext().getASTContext()) + return true; // The ASTs match, return true + + // The namespace AST was valid, and it does not match... + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + GetObjectFile()->GetModule()->LogMessage(log, "Valid namespace does not match symbol file"); + + return false; +} + +bool +SymbolFileDWARF::DIEIsInNamespace (const ClangNamespaceDecl *namespace_decl, + DWARFCompileUnit* cu, + const DWARFDebugInfoEntry* die) +{ + // No namespace specified, so the answesr i + if (namespace_decl == NULL) + return true; + + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + const DWARFDebugInfoEntry *decl_ctx_die = NULL; + clang::DeclContext *die_clang_decl_ctx = GetClangDeclContextContainingDIE (cu, die, &decl_ctx_die); + if (decl_ctx_die) + { + clang::NamespaceDecl *clang_namespace_decl = namespace_decl->GetNamespaceDecl(); + + if (clang_namespace_decl) + { + if (decl_ctx_die->Tag() != DW_TAG_namespace) + { + if (log) + GetObjectFile()->GetModule()->LogMessage(log, "Found a match, but its parent is not a namespace"); + return false; + } + + if (clang_namespace_decl == die_clang_decl_ctx) + return true; + else + return false; + } + else + { + // We have a namespace_decl that was not NULL but it contained + // a NULL "clang::NamespaceDecl", so this means the global namespace + // So as long the the contained decl context DIE isn't a namespace + // we should be ok. + if (decl_ctx_die->Tag() != DW_TAG_namespace) + return true; + } + } + + if (log) + GetObjectFile()->GetModule()->LogMessage(log, "Found a match, but its parent doesn't exist"); + + return false; +} +uint32_t +SymbolFileDWARF::FindGlobalVariables (const ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, VariableList& variables) +{ + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", namespace_decl=%p, append=%u, max_matches=%u, variables)", + name.GetCString(), + namespace_decl, + append, + max_matches); + } + + if (!NamespaceDeclMatchesThisSymbolFile(namespace_decl)) + return 0; + + DWARFDebugInfo* info = DebugInfo(); + if (info == NULL) + return 0; + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + DIEArray die_offsets; + + if (m_using_apple_tables) + { + if (m_apple_names_ap.get()) + { + const char *name_cstr = name.GetCString(); + const char *base_name_start; + const char *base_name_end = NULL; + + if (!CPPLanguageRuntime::StripNamespacesFromVariableName(name_cstr, base_name_start, base_name_end)) + base_name_start = name_cstr; + + m_apple_names_ap->FindByName (base_name_start, die_offsets); + } + } + else + { + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + m_global_index.Find (name, die_offsets); + } + + const size_t num_die_matches = die_offsets.size(); + if (num_die_matches) + { + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule(); + assert (sc.module_sp); + + DWARFDebugInfo* debug_info = DebugInfo(); + DWARFCompileUnit* dwarf_cu = NULL; + const DWARFDebugInfoEntry* die = NULL; + bool done = false; + for (size_t i=0; i<num_die_matches && !done; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + + if (die) + { + switch (die->Tag()) + { + default: + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_try_block: + case DW_TAG_catch_block: + break; + + case DW_TAG_variable: + { + sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, UINT32_MAX); + + if (namespace_decl && !DIEIsInNamespace (namespace_decl, dwarf_cu, die)) + continue; + + ParseVariables(sc, dwarf_cu, LLDB_INVALID_ADDRESS, die, false, false, &variables); + + if (variables.GetSize() - original_size >= max_matches) + done = true; + } + break; + } + } + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_names accelerator table had bad die 0x%8.8x for '%s')\n", + die_offset, name.GetCString()); + } + } + } + } + + // Return the number of variable that were appended to the list + const uint32_t num_matches = variables.GetSize() - original_size; + if (log && num_matches > 0) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindGlobalVariables (name=\"%s\", namespace_decl=%p, append=%u, max_matches=%u, variables) => %u", + name.GetCString(), + namespace_decl, + append, + max_matches, + num_matches); + } + return num_matches; +} + +uint32_t +SymbolFileDWARF::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindGlobalVariables (regex=\"%s\", append=%u, max_matches=%u, variables)", + regex.GetText(), + append, + max_matches); + } + + DWARFDebugInfo* info = DebugInfo(); + if (info == NULL) + return 0; + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + DIEArray die_offsets; + + if (m_using_apple_tables) + { + if (m_apple_names_ap.get()) + { + DWARFMappedHash::DIEInfoArray hash_data_array; + if (m_apple_names_ap->AppendAllDIEsThatMatchingRegex (regex, hash_data_array)) + DWARFMappedHash::ExtractDIEArray (hash_data_array, die_offsets); + } + } + else + { + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + m_global_index.Find (regex, die_offsets); + } + + SymbolContext sc; + sc.module_sp = m_obj_file->GetModule(); + assert (sc.module_sp); + + DWARFCompileUnit* dwarf_cu = NULL; + const DWARFDebugInfoEntry* die = NULL; + const size_t num_matches = die_offsets.size(); + if (num_matches) + { + DWARFDebugInfo* debug_info = DebugInfo(); + for (size_t i=0; i<num_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + + if (die) + { + sc.comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, UINT32_MAX); + + ParseVariables(sc, dwarf_cu, LLDB_INVALID_ADDRESS, die, false, false, &variables); + + if (variables.GetSize() - original_size >= max_matches) + break; + } + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_names accelerator table had bad die 0x%8.8x for regex '%s')\n", + die_offset, regex.GetText()); + } + } + } + } + + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + + +bool +SymbolFileDWARF::ResolveFunction (dw_offset_t die_offset, + DWARFCompileUnit *&dwarf_cu, + SymbolContextList& sc_list) +{ + const DWARFDebugInfoEntry *die = DebugInfo()->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + return ResolveFunction (dwarf_cu, die, sc_list); +} + + +bool +SymbolFileDWARF::ResolveFunction (DWARFCompileUnit *cu, + const DWARFDebugInfoEntry *die, + SymbolContextList& sc_list) +{ + SymbolContext sc; + + if (die == NULL) + return false; + + // If we were passed a die that is not a function, just return false... + if (die->Tag() != DW_TAG_subprogram && die->Tag() != DW_TAG_inlined_subroutine) + return false; + + const DWARFDebugInfoEntry* inlined_die = NULL; + if (die->Tag() == DW_TAG_inlined_subroutine) + { + inlined_die = die; + + while ((die = die->GetParent()) != NULL) + { + if (die->Tag() == DW_TAG_subprogram) + break; + } + } + assert (die->Tag() == DW_TAG_subprogram); + if (GetFunction (cu, die, sc)) + { + Address addr; + // Parse all blocks if needed + if (inlined_die) + { + sc.block = sc.function->GetBlock (true).FindBlockByID (MakeUserID(inlined_die->GetOffset())); + assert (sc.block != NULL); + if (sc.block->GetStartAddress (addr) == false) + addr.Clear(); + } + else + { + sc.block = NULL; + addr = sc.function->GetAddressRange().GetBaseAddress(); + } + + if (addr.IsValid()) + { + sc_list.Append(sc); + return true; + } + } + + return false; +} + +void +SymbolFileDWARF::FindFunctions (const ConstString &name, + const NameToDIE &name_to_die, + SymbolContextList& sc_list) +{ + DIEArray die_offsets; + if (name_to_die.Find (name, die_offsets)) + { + ParseFunctions (die_offsets, sc_list); + } +} + + +void +SymbolFileDWARF::FindFunctions (const RegularExpression ®ex, + const NameToDIE &name_to_die, + SymbolContextList& sc_list) +{ + DIEArray die_offsets; + if (name_to_die.Find (regex, die_offsets)) + { + ParseFunctions (die_offsets, sc_list); + } +} + + +void +SymbolFileDWARF::FindFunctions (const RegularExpression ®ex, + const DWARFMappedHash::MemoryTable &memory_table, + SymbolContextList& sc_list) +{ + DIEArray die_offsets; + DWARFMappedHash::DIEInfoArray hash_data_array; + if (memory_table.AppendAllDIEsThatMatchingRegex (regex, hash_data_array)) + { + DWARFMappedHash::ExtractDIEArray (hash_data_array, die_offsets); + ParseFunctions (die_offsets, sc_list); + } +} + +void +SymbolFileDWARF::ParseFunctions (const DIEArray &die_offsets, + SymbolContextList& sc_list) +{ + const size_t num_matches = die_offsets.size(); + if (num_matches) + { + SymbolContext sc; + + DWARFCompileUnit* dwarf_cu = NULL; + for (size_t i=0; i<num_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + ResolveFunction (die_offset, dwarf_cu, sc_list); + } + } +} + +bool +SymbolFileDWARF::FunctionDieMatchesPartialName (const DWARFDebugInfoEntry* die, + const DWARFCompileUnit *dwarf_cu, + uint32_t name_type_mask, + const char *partial_name, + const char *base_name_start, + const char *base_name_end) +{ + // If we are looking only for methods, throw away all the ones that are or aren't in C++ classes: + if (name_type_mask == eFunctionNameTypeMethod || name_type_mask == eFunctionNameTypeBase) + { + clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIEOffset(die->GetOffset()); + if (!containing_decl_ctx) + return false; + + bool is_cxx_method = DeclKindIsCXXClass(containing_decl_ctx->getDeclKind()); + + if (name_type_mask == eFunctionNameTypeMethod) + { + if (is_cxx_method == false) + return false; + } + + if (name_type_mask == eFunctionNameTypeBase) + { + if (is_cxx_method == true) + return false; + } + } + + // Now we need to check whether the name we got back for this type matches the extra specifications + // that were in the name we're looking up: + if (base_name_start != partial_name || *base_name_end != '\0') + { + // First see if the stuff to the left matches the full name. To do that let's see if + // we can pull out the mips linkage name attribute: + + Mangled best_name; + DWARFDebugInfoEntry::Attributes attributes; + DWARFFormValue form_value; + die->GetAttributes(this, dwarf_cu, NULL, attributes); + uint32_t idx = attributes.FindAttributeIndex(DW_AT_MIPS_linkage_name); + if (idx == UINT32_MAX) + idx = attributes.FindAttributeIndex(DW_AT_linkage_name); + if (idx != UINT32_MAX) + { + if (attributes.ExtractFormValueAtIndex(this, idx, form_value)) + { + const char *mangled_name = form_value.AsCString(&get_debug_str_data()); + if (mangled_name) + best_name.SetValue (ConstString(mangled_name), true); + } + } + + if (!best_name) + { + idx = attributes.FindAttributeIndex(DW_AT_name); + if (idx != UINT32_MAX && attributes.ExtractFormValueAtIndex(this, idx, form_value)) + { + const char *name = form_value.AsCString(&get_debug_str_data()); + best_name.SetValue (ConstString(name), false); + } + } + + if (best_name.GetDemangledName()) + { + const char *demangled = best_name.GetDemangledName().GetCString(); + if (demangled) + { + std::string name_no_parens(partial_name, base_name_end - partial_name); + const char *partial_in_demangled = strstr (demangled, name_no_parens.c_str()); + if (partial_in_demangled == NULL) + return false; + else + { + // Sort out the case where our name is something like "Process::Destroy" and the match is + // "SBProcess::Destroy" - that shouldn't be a match. We should really always match on + // namespace boundaries... + + if (partial_name[0] == ':' && partial_name[1] == ':') + { + // The partial name was already on a namespace boundary so all matches are good. + return true; + } + else if (partial_in_demangled == demangled) + { + // They both start the same, so this is an good match. + return true; + } + else + { + if (partial_in_demangled - demangled == 1) + { + // Only one character difference, can't be a namespace boundary... + return false; + } + else if (*(partial_in_demangled - 1) == ':' && *(partial_in_demangled - 2) == ':') + { + // We are on a namespace boundary, so this is also good. + return true; + } + else + return false; + } + } + } + } + } + + return true; +} + +uint32_t +SymbolFileDWARF::FindFunctions (const ConstString &name, + const lldb_private::ClangNamespaceDecl *namespace_decl, + uint32_t name_type_mask, + bool include_inlines, + bool append, + SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::FindFunctions (name = '%s')", + name.AsCString()); + + // eFunctionNameTypeAuto should be pre-resolved by a call to Module::PrepareForFunctionNameLookup() + assert ((name_type_mask & eFunctionNameTypeAuto) == 0); + + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindFunctions (name=\"%s\", name_type_mask=0x%x, append=%u, sc_list)", + name.GetCString(), + name_type_mask, + append); + } + + // If we aren't appending the results to this list, then clear the list + if (!append) + sc_list.Clear(); + + if (!NamespaceDeclMatchesThisSymbolFile(namespace_decl)) + return 0; + + // If name is empty then we won't find anything. + if (name.IsEmpty()) + return 0; + + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + + const char *name_cstr = name.GetCString(); + + const uint32_t original_size = sc_list.GetSize(); + + DWARFDebugInfo* info = DebugInfo(); + if (info == NULL) + return 0; + + DWARFCompileUnit *dwarf_cu = NULL; + std::set<const DWARFDebugInfoEntry *> resolved_dies; + if (m_using_apple_tables) + { + if (m_apple_names_ap.get()) + { + + DIEArray die_offsets; + + uint32_t num_matches = 0; + + if (name_type_mask & eFunctionNameTypeFull) + { + // If they asked for the full name, match what they typed. At some point we may + // want to canonicalize this (strip double spaces, etc. For now, we just add all the + // dies that we find by exact match. + num_matches = m_apple_names_ap->FindByName (name_cstr, die_offsets); + for (uint32_t i = 0; i < num_matches; i++) + { + const dw_offset_t die_offset = die_offsets[i]; + const DWARFDebugInfoEntry *die = info->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + if (die) + { + if (namespace_decl && !DIEIsInNamespace (namespace_decl, dwarf_cu, die)) + continue; + + if (!include_inlines && die->Tag() == DW_TAG_inlined_subroutine) + continue; + + if (resolved_dies.find(die) == resolved_dies.end()) + { + if (ResolveFunction (dwarf_cu, die, sc_list)) + resolved_dies.insert(die); + } + } + else + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_names accelerator table had bad die 0x%8.8x for '%s')", + die_offset, name_cstr); + } + } + } + + if (name_type_mask & eFunctionNameTypeSelector) + { + if (namespace_decl && *namespace_decl) + return 0; // no selectors in namespaces + + num_matches = m_apple_names_ap->FindByName (name_cstr, die_offsets); + // Now make sure these are actually ObjC methods. In this case we can simply look up the name, + // and if it is an ObjC method name, we're good. + + for (uint32_t i = 0; i < num_matches; i++) + { + const dw_offset_t die_offset = die_offsets[i]; + const DWARFDebugInfoEntry* die = info->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + if (die) + { + const char *die_name = die->GetName(this, dwarf_cu); + if (ObjCLanguageRuntime::IsPossibleObjCMethodName(die_name)) + { + if (!include_inlines && die->Tag() == DW_TAG_inlined_subroutine) + continue; + + if (resolved_dies.find(die) == resolved_dies.end()) + { + if (ResolveFunction (dwarf_cu, die, sc_list)) + resolved_dies.insert(die); + } + } + } + else + { + GetObjectFile()->GetModule()->ReportError ("the DWARF debug information has been modified (.apple_names accelerator table had bad die 0x%8.8x for '%s')", + die_offset, name_cstr); + } + } + die_offsets.clear(); + } + + if (((name_type_mask & eFunctionNameTypeMethod) && !namespace_decl) || name_type_mask & eFunctionNameTypeBase) + { + // The apple_names table stores just the "base name" of C++ methods in the table. So we have to + // extract the base name, look that up, and if there is any other information in the name we were + // passed in we have to post-filter based on that. + + // FIXME: Arrange the logic above so that we don't calculate the base name twice: + num_matches = m_apple_names_ap->FindByName (name_cstr, die_offsets); + + for (uint32_t i = 0; i < num_matches; i++) + { + const dw_offset_t die_offset = die_offsets[i]; + const DWARFDebugInfoEntry* die = info->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + if (die) + { + if (!include_inlines && die->Tag() == DW_TAG_inlined_subroutine) + continue; + + if (namespace_decl && !DIEIsInNamespace (namespace_decl, dwarf_cu, die)) + continue; + + // If we get to here, the die is good, and we should add it: + if (resolved_dies.find(die) == resolved_dies.end()) + if (ResolveFunction (dwarf_cu, die, sc_list)) + { + bool keep_die = true; + if ((name_type_mask & (eFunctionNameTypeBase|eFunctionNameTypeMethod)) != (eFunctionNameTypeBase|eFunctionNameTypeMethod)) + { + // We are looking for either basenames or methods, so we need to + // trim out the ones we won't want by looking at the type + SymbolContext sc; + if (sc_list.GetLastContext(sc)) + { + if (sc.block) + { + // We have an inlined function + } + else if (sc.function) + { + Type *type = sc.function->GetType(); + + clang::DeclContext* decl_ctx = GetClangDeclContextContainingTypeUID (type->GetID()); + if (decl_ctx->isRecord()) + { + if (name_type_mask & eFunctionNameTypeBase) + { + sc_list.RemoveContextAtIndex(sc_list.GetSize()-1); + keep_die = false; + } + } + else + { + if (name_type_mask & eFunctionNameTypeMethod) + { + sc_list.RemoveContextAtIndex(sc_list.GetSize()-1); + keep_die = false; + } + } + } + } + } + if (keep_die) + resolved_dies.insert(die); + } + } + else + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_names accelerator table had bad die 0x%8.8x for '%s')", + die_offset, name_cstr); + } + } + die_offsets.clear(); + } + } + } + else + { + + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + if (name_type_mask & eFunctionNameTypeFull) + { + FindFunctions (name, m_function_fullname_index, sc_list); + + // Temporary workaround for global/anonymous namespace functions on linux +#if defined (__linux__) + // If we didn't find any functions in the global namespace try + // looking in the basename index but ignore any returned + // functions that have a namespace (ie. mangled names starting with + // '_ZN') but keep functions which have an anonymous namespace + if (sc_list.GetSize() == 0) + { + SymbolContextList temp_sc_list; + FindFunctions (name, m_function_basename_index, temp_sc_list); + if (!namespace_decl) + { + SymbolContext sc; + for (uint32_t i = 0; i < temp_sc_list.GetSize(); i++) + { + if (temp_sc_list.GetContextAtIndex(i, sc)) + { + ConstString mangled_name = sc.GetFunctionName(Mangled::ePreferMangled); + ConstString demangled_name = sc.GetFunctionName(Mangled::ePreferDemangled); + if (strncmp(mangled_name.GetCString(), "_ZN", 3) || + !strncmp(demangled_name.GetCString(), "(anonymous namespace)", 21)) + { + sc_list.Append(sc); + } + } + } + } + } +#endif + } + DIEArray die_offsets; + DWARFCompileUnit *dwarf_cu = NULL; + + if (name_type_mask & eFunctionNameTypeBase) + { + uint32_t num_base = m_function_basename_index.Find(name, die_offsets); + for (uint32_t i = 0; i < num_base; i++) + { + const DWARFDebugInfoEntry* die = info->GetDIEPtrWithCompileUnitHint (die_offsets[i], &dwarf_cu); + if (die) + { + if (!include_inlines && die->Tag() == DW_TAG_inlined_subroutine) + continue; + + if (namespace_decl && !DIEIsInNamespace (namespace_decl, dwarf_cu, die)) + continue; + + // If we get to here, the die is good, and we should add it: + if (resolved_dies.find(die) == resolved_dies.end()) + { + if (ResolveFunction (dwarf_cu, die, sc_list)) + resolved_dies.insert(die); + } + } + } + die_offsets.clear(); + } + + if (name_type_mask & eFunctionNameTypeMethod) + { + if (namespace_decl && *namespace_decl) + return 0; // no methods in namespaces + + uint32_t num_base = m_function_method_index.Find(name, die_offsets); + { + for (uint32_t i = 0; i < num_base; i++) + { + const DWARFDebugInfoEntry* die = info->GetDIEPtrWithCompileUnitHint (die_offsets[i], &dwarf_cu); + if (die) + { + if (!include_inlines && die->Tag() == DW_TAG_inlined_subroutine) + continue; + + // If we get to here, the die is good, and we should add it: + if (resolved_dies.find(die) == resolved_dies.end()) + { + if (ResolveFunction (dwarf_cu, die, sc_list)) + resolved_dies.insert(die); + } + } + } + } + die_offsets.clear(); + } + + if ((name_type_mask & eFunctionNameTypeSelector) && (!namespace_decl || !*namespace_decl)) + { + FindFunctions (name, m_function_selector_index, sc_list); + } + + } + + // Return the number of variable that were appended to the list + const uint32_t num_matches = sc_list.GetSize() - original_size; + + if (log && num_matches > 0) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindFunctions (name=\"%s\", name_type_mask=0x%x, append=%u, sc_list) => %u", + name.GetCString(), + name_type_mask, + append, + num_matches); + } + return num_matches; +} + +uint32_t +SymbolFileDWARF::FindFunctions(const RegularExpression& regex, bool include_inlines, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARF::FindFunctions (regex = '%s')", + regex.GetText()); + + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindFunctions (regex=\"%s\", append=%u, sc_list)", + regex.GetText(), + append); + } + + + // If we aren't appending the results to this list, then clear the list + if (!append) + sc_list.Clear(); + + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + uint32_t original_size = sc_list.GetSize(); + + if (m_using_apple_tables) + { + if (m_apple_names_ap.get()) + FindFunctions (regex, *m_apple_names_ap, sc_list); + } + else + { + // Index the DWARF if we haven't already + if (!m_indexed) + Index (); + + FindFunctions (regex, m_function_basename_index, sc_list); + + FindFunctions (regex, m_function_fullname_index, sc_list); + } + + // Return the number of variable that were appended to the list + return sc_list.GetSize() - original_size; +} + +uint32_t +SymbolFileDWARF::FindTypes (const SymbolContext& sc, + const ConstString &name, + const lldb_private::ClangNamespaceDecl *namespace_decl, + bool append, + uint32_t max_matches, + TypeList& types) +{ + DWARFDebugInfo* info = DebugInfo(); + if (info == NULL) + return 0; + + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + { + if (namespace_decl) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindTypes (sc, name=\"%s\", clang::NamespaceDecl(%p) \"%s\", append=%u, max_matches=%u, type_list)", + name.GetCString(), + namespace_decl->GetNamespaceDecl(), + namespace_decl->GetQualifiedName().c_str(), + append, + max_matches); + } + else + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindTypes (sc, name=\"%s\", clang::NamespaceDecl(NULL), append=%u, max_matches=%u, type_list)", + name.GetCString(), + append, + max_matches); + } + } + + // If we aren't appending the results to this list, then clear the list + if (!append) + types.Clear(); + + if (!NamespaceDeclMatchesThisSymbolFile(namespace_decl)) + return 0; + + DIEArray die_offsets; + + if (m_using_apple_tables) + { + if (m_apple_types_ap.get()) + { + const char *name_cstr = name.GetCString(); + m_apple_types_ap->FindByName (name_cstr, die_offsets); + } + } + else + { + if (!m_indexed) + Index (); + + m_type_index.Find (name, die_offsets); + } + + const size_t num_die_matches = die_offsets.size(); + + if (num_die_matches) + { + const uint32_t initial_types_size = types.GetSize(); + DWARFCompileUnit* dwarf_cu = NULL; + const DWARFDebugInfoEntry* die = NULL; + DWARFDebugInfo* debug_info = DebugInfo(); + for (size_t i=0; i<num_die_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + + if (die) + { + if (namespace_decl && !DIEIsInNamespace (namespace_decl, dwarf_cu, die)) + continue; + + Type *matching_type = ResolveType (dwarf_cu, die); + if (matching_type) + { + // We found a type pointer, now find the shared pointer form our type list + types.InsertUnique (matching_type->shared_from_this()); + if (types.GetSize() >= max_matches) + break; + } + } + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_types accelerator table had bad die 0x%8.8x for '%s')\n", + die_offset, name.GetCString()); + } + } + + } + const uint32_t num_matches = types.GetSize() - initial_types_size; + if (log && num_matches) + { + if (namespace_decl) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindTypes (sc, name=\"%s\", clang::NamespaceDecl(%p) \"%s\", append=%u, max_matches=%u, type_list) => %u", + name.GetCString(), + namespace_decl->GetNamespaceDecl(), + namespace_decl->GetQualifiedName().c_str(), + append, + max_matches, + num_matches); + } + else + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindTypes (sc, name=\"%s\", clang::NamespaceDecl(NULL), append=%u, max_matches=%u, type_list) => %u", + name.GetCString(), + append, + max_matches, + num_matches); + } + } + return num_matches; + } + return 0; +} + + +ClangNamespaceDecl +SymbolFileDWARF::FindNamespace (const SymbolContext& sc, + const ConstString &name, + const lldb_private::ClangNamespaceDecl *parent_namespace_decl) +{ + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); + + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindNamespace (sc, name=\"%s\")", + name.GetCString()); + } + + if (!NamespaceDeclMatchesThisSymbolFile(parent_namespace_decl)) + return ClangNamespaceDecl(); + + ClangNamespaceDecl namespace_decl; + DWARFDebugInfo* info = DebugInfo(); + if (info) + { + DIEArray die_offsets; + + // Index if we already haven't to make sure the compile units + // get indexed and make their global DIE index list + if (m_using_apple_tables) + { + if (m_apple_namespaces_ap.get()) + { + const char *name_cstr = name.GetCString(); + m_apple_namespaces_ap->FindByName (name_cstr, die_offsets); + } + } + else + { + if (!m_indexed) + Index (); + + m_namespace_index.Find (name, die_offsets); + } + + DWARFCompileUnit* dwarf_cu = NULL; + const DWARFDebugInfoEntry* die = NULL; + const size_t num_matches = die_offsets.size(); + if (num_matches) + { + DWARFDebugInfo* debug_info = DebugInfo(); + for (size_t i=0; i<num_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + + if (die) + { + if (parent_namespace_decl && !DIEIsInNamespace (parent_namespace_decl, dwarf_cu, die)) + continue; + + clang::NamespaceDecl *clang_namespace_decl = ResolveNamespaceDIE (dwarf_cu, die); + if (clang_namespace_decl) + { + namespace_decl.SetASTContext (GetClangASTContext().getASTContext()); + namespace_decl.SetNamespaceDecl (clang_namespace_decl); + break; + } + } + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_namespaces accelerator table had bad die 0x%8.8x for '%s')\n", + die_offset, name.GetCString()); + } + } + + } + } + } + if (log && namespace_decl.GetNamespaceDecl()) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindNamespace (sc, name=\"%s\") => clang::NamespaceDecl(%p) \"%s\"", + name.GetCString(), + namespace_decl.GetNamespaceDecl(), + namespace_decl.GetQualifiedName().c_str()); + } + + return namespace_decl; +} + +uint32_t +SymbolFileDWARF::FindTypes(std::vector<dw_offset_t> die_offsets, uint32_t max_matches, TypeList& types) +{ + // Remember how many sc_list are in the list before we search in case + // we are appending the results to a variable list. + uint32_t original_size = types.GetSize(); + + const uint32_t num_die_offsets = die_offsets.size(); + // Parse all of the types we found from the pubtypes matches + uint32_t i; + uint32_t num_matches = 0; + for (i = 0; i < num_die_offsets; ++i) + { + Type *matching_type = ResolveTypeUID (die_offsets[i]); + if (matching_type) + { + // We found a type pointer, now find the shared pointer form our type list + types.InsertUnique (matching_type->shared_from_this()); + ++num_matches; + if (num_matches >= max_matches) + break; + } + } + + // Return the number of variable that were appended to the list + return types.GetSize() - original_size; +} + + +size_t +SymbolFileDWARF::ParseChildParameters (const SymbolContext& sc, + clang::DeclContext *containing_decl_ctx, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + bool skip_artificial, + bool &is_static, + TypeList* type_list, + std::vector<ClangASTType>& function_param_types, + std::vector<clang::ParmVarDecl*>& function_param_decls, + unsigned &type_quals, + ClangASTContext::TemplateParameterInfos &template_param_infos) +{ + if (parent_die == NULL) + return 0; + + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + + size_t arg_idx = 0; + const DWARFDebugInfoEntry *die; + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + dw_tag_t tag = die->Tag(); + switch (tag) + { + case DW_TAG_formal_parameter: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, fixed_form_sizes, attributes); + if (num_attributes > 0) + { + const char *name = NULL; + Declaration decl; + dw_offset_t param_type_die_offset = DW_INVALID_OFFSET; + bool is_artificial = false; + // one of None, Auto, Register, Extern, Static, PrivateExtern + + clang::StorageClass storage = clang::SC_None; + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: param_type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_artificial: is_artificial = form_value.Boolean(); break; + case DW_AT_location: + // if (form_value.BlockData()) + // { + // const DataExtractor& debug_info_data = debug_info(); + // uint32_t block_length = form_value.Unsigned(); + // DataExtractor location(debug_info_data, form_value.BlockData() - debug_info_data.GetDataStart(), block_length); + // } + // else + // { + // } + // break; + case DW_AT_const_value: + case DW_AT_default_value: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_is_optional: + case DW_AT_segment: + case DW_AT_variable_parameter: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + bool skip = false; + if (skip_artificial) + { + if (is_artificial) + { + // In order to determine if a C++ member function is + // "const" we have to look at the const-ness of "this"... + // Ugly, but that + if (arg_idx == 0) + { + if (DeclKindIsCXXClass(containing_decl_ctx->getDeclKind())) + { + // Often times compilers omit the "this" name for the + // specification DIEs, so we can't rely upon the name + // being in the formal parameter DIE... + if (name == NULL || ::strcmp(name, "this")==0) + { + Type *this_type = ResolveTypeUID (param_type_die_offset); + if (this_type) + { + uint32_t encoding_mask = this_type->GetEncodingMask(); + if (encoding_mask & Type::eEncodingIsPointerUID) + { + is_static = false; + + if (encoding_mask & (1u << Type::eEncodingIsConstUID)) + type_quals |= clang::Qualifiers::Const; + if (encoding_mask & (1u << Type::eEncodingIsVolatileUID)) + type_quals |= clang::Qualifiers::Volatile; + } + } + } + } + } + skip = true; + } + else + { + + // HACK: Objective C formal parameters "self" and "_cmd" + // are not marked as artificial in the DWARF... + CompileUnit *comp_unit = GetCompUnitForDWARFCompUnit(dwarf_cu, UINT32_MAX); + if (comp_unit) + { + switch (comp_unit->GetLanguage()) + { + case eLanguageTypeObjC: + case eLanguageTypeObjC_plus_plus: + if (name && name[0] && (strcmp (name, "self") == 0 || strcmp (name, "_cmd") == 0)) + skip = true; + break; + default: + break; + } + } + } + } + + if (!skip) + { + Type *type = ResolveTypeUID(param_type_die_offset); + if (type) + { + function_param_types.push_back (type->GetClangForwardType()); + + clang::ParmVarDecl *param_var_decl = GetClangASTContext().CreateParameterDeclaration (name, + type->GetClangForwardType(), + storage); + assert(param_var_decl); + function_param_decls.push_back(param_var_decl); + + GetClangASTContext().SetMetadataAsUserID (param_var_decl, MakeUserID(die->GetOffset())); + } + } + } + arg_idx++; + } + break; + + case DW_TAG_template_type_parameter: + case DW_TAG_template_value_parameter: + ParseTemplateDIE (dwarf_cu, die,template_param_infos); + break; + + default: + break; + } + } + return arg_idx; +} + +size_t +SymbolFileDWARF::ParseChildEnumerators +( + const SymbolContext& sc, + lldb_private::ClangASTType &clang_type, + bool is_signed, + uint32_t enumerator_byte_size, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die +) +{ + if (parent_die == NULL) + return 0; + + size_t enumerators_added = 0; + const DWARFDebugInfoEntry *die; + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + const dw_tag_t tag = die->Tag(); + if (tag == DW_TAG_enumerator) + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_child_attributes = die->GetAttributes(this, dwarf_cu, fixed_form_sizes, attributes); + if (num_child_attributes > 0) + { + const char *name = NULL; + bool got_value = false; + int64_t enum_value = 0; + Declaration decl; + + uint32_t i; + for (i=0; i<num_child_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_const_value: + got_value = true; + if (is_signed) + enum_value = form_value.Signed(); + else + enum_value = form_value.Unsigned(); + break; + + case DW_AT_name: + name = form_value.AsCString(&get_debug_str_data()); + break; + + case DW_AT_description: + default: + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_sibling: + break; + } + } + } + + if (name && name[0] && got_value) + { + clang_type.AddEnumerationValueToEnumerationType (clang_type.GetEnumerationIntegerType(), + decl, + name, + enum_value, + enumerator_byte_size * 8); + ++enumerators_added; + } + } + } + } + return enumerators_added; +} + +void +SymbolFileDWARF::ParseChildArrayInfo +( + const SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + int64_t& first_index, + std::vector<uint64_t>& element_orders, + uint32_t& byte_stride, + uint32_t& bit_stride +) +{ + if (parent_die == NULL) + return; + + const DWARFDebugInfoEntry *die; + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) + { + const dw_tag_t tag = die->Tag(); + switch (tag) + { + case DW_TAG_subrange_type: + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_child_attributes = die->GetAttributes(this, dwarf_cu, fixed_form_sizes, attributes); + if (num_child_attributes > 0) + { + uint64_t num_elements = 0; + uint64_t lower_bound = 0; + uint64_t upper_bound = 0; + bool upper_bound_valid = false; + uint32_t i; + for (i=0; i<num_child_attributes; ++i) + { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_name: + break; + + case DW_AT_count: + num_elements = form_value.Unsigned(); + break; + + case DW_AT_bit_stride: + bit_stride = form_value.Unsigned(); + break; + + case DW_AT_byte_stride: + byte_stride = form_value.Unsigned(); + break; + + case DW_AT_lower_bound: + lower_bound = form_value.Unsigned(); + break; + + case DW_AT_upper_bound: + upper_bound_valid = true; + upper_bound = form_value.Unsigned(); + break; + + default: + case DW_AT_abstract_origin: + case DW_AT_accessibility: + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_sibling: + case DW_AT_threads_scaled: + case DW_AT_type: + case DW_AT_visibility: + break; + } + } + } + + if (num_elements == 0) + { + if (upper_bound_valid && upper_bound >= lower_bound) + num_elements = upper_bound - lower_bound + 1; + } + + element_orders.push_back (num_elements); + } + } + break; + } + } +} + +TypeSP +SymbolFileDWARF::GetTypeForDIE (DWARFCompileUnit *dwarf_cu, const DWARFDebugInfoEntry* die) +{ + TypeSP type_sp; + if (die != NULL) + { + assert(dwarf_cu != NULL); + Type *type_ptr = m_die_to_type.lookup (die); + if (type_ptr == NULL) + { + CompileUnit* lldb_cu = GetCompUnitForDWARFCompUnit(dwarf_cu); + assert (lldb_cu); + SymbolContext sc(lldb_cu); + type_sp = ParseType(sc, dwarf_cu, die, NULL); + } + else if (type_ptr != DIE_IS_BEING_PARSED) + { + // Grab the existing type from the master types lists + type_sp = type_ptr->shared_from_this(); + } + + } + return type_sp; +} + +clang::DeclContext * +SymbolFileDWARF::GetClangDeclContextContainingDIEOffset (dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = DebugInfo()->GetDIEPtr(die_offset, &cu_sp); + return GetClangDeclContextContainingDIE (cu_sp.get(), die, NULL); + } + return NULL; +} + +clang::DeclContext * +SymbolFileDWARF::GetClangDeclContextForDIEOffset (const SymbolContext &sc, dw_offset_t die_offset) +{ + if (die_offset != DW_INVALID_OFFSET) + { + DWARFDebugInfo* debug_info = DebugInfo(); + if (debug_info) + { + DWARFCompileUnitSP cu_sp; + const DWARFDebugInfoEntry* die = debug_info->GetDIEPtr(die_offset, &cu_sp); + if (die) + return GetClangDeclContextForDIE (sc, cu_sp.get(), die); + } + } + return NULL; +} + +clang::NamespaceDecl * +SymbolFileDWARF::ResolveNamespaceDIE (DWARFCompileUnit *dwarf_cu, const DWARFDebugInfoEntry *die) +{ + if (die && die->Tag() == DW_TAG_namespace) + { + // See if we already parsed this namespace DIE and associated it with a + // uniqued namespace declaration + clang::NamespaceDecl *namespace_decl = static_cast<clang::NamespaceDecl *>(m_die_to_decl_ctx[die]); + if (namespace_decl) + return namespace_decl; + else + { + const char *namespace_name = die->GetAttributeValueAsString(this, dwarf_cu, DW_AT_name, NULL); + clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE (dwarf_cu, die, NULL); + namespace_decl = GetClangASTContext().GetUniqueNamespaceDeclaration (namespace_name, containing_decl_ctx); + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); + if (log) + { + if (namespace_name) + { + GetObjectFile()->GetModule()->LogMessage (log, + "ASTContext => %p: 0x%8.8" PRIx64 ": DW_TAG_namespace with DW_AT_name(\"%s\") => clang::NamespaceDecl *%p (original = %p)", + GetClangASTContext().getASTContext(), + MakeUserID(die->GetOffset()), + namespace_name, + namespace_decl, + namespace_decl->getOriginalNamespace()); + } + else + { + GetObjectFile()->GetModule()->LogMessage (log, + "ASTContext => %p: 0x%8.8" PRIx64 ": DW_TAG_namespace (anonymous) => clang::NamespaceDecl *%p (original = %p)", + GetClangASTContext().getASTContext(), + MakeUserID(die->GetOffset()), + namespace_decl, + namespace_decl->getOriginalNamespace()); + } + } + + if (namespace_decl) + LinkDeclContextToDIE((clang::DeclContext*)namespace_decl, die); + return namespace_decl; + } + } + return NULL; +} + +clang::DeclContext * +SymbolFileDWARF::GetClangDeclContextForDIE (const SymbolContext &sc, DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die) +{ + clang::DeclContext *clang_decl_ctx = GetCachedClangDeclContextForDIE (die); + if (clang_decl_ctx) + return clang_decl_ctx; + // If this DIE has a specification, or an abstract origin, then trace to those. + + dw_offset_t die_offset = die->GetAttributeValueAsReference(this, cu, DW_AT_specification, DW_INVALID_OFFSET); + if (die_offset != DW_INVALID_OFFSET) + return GetClangDeclContextForDIEOffset (sc, die_offset); + + die_offset = die->GetAttributeValueAsReference(this, cu, DW_AT_abstract_origin, DW_INVALID_OFFSET); + if (die_offset != DW_INVALID_OFFSET) + return GetClangDeclContextForDIEOffset (sc, die_offset); + + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); + if (log) + GetObjectFile()->GetModule()->LogMessage(log, "SymbolFileDWARF::GetClangDeclContextForDIE (die = 0x%8.8x) %s '%s'", die->GetOffset(), DW_TAG_value_to_name(die->Tag()), die->GetName(this, cu)); + // This is the DIE we want. Parse it, then query our map. + bool assert_not_being_parsed = true; + ResolveTypeUID (cu, die, assert_not_being_parsed); + + clang_decl_ctx = GetCachedClangDeclContextForDIE (die); + + return clang_decl_ctx; +} + +clang::DeclContext * +SymbolFileDWARF::GetClangDeclContextContainingDIE (DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die, const DWARFDebugInfoEntry **decl_ctx_die_copy) +{ + if (m_clang_tu_decl == NULL) + m_clang_tu_decl = GetClangASTContext().getASTContext()->getTranslationUnitDecl(); + + const DWARFDebugInfoEntry *decl_ctx_die = GetDeclContextDIEContainingDIE (cu, die); + + if (decl_ctx_die_copy) + *decl_ctx_die_copy = decl_ctx_die; + + if (decl_ctx_die) + { + + DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find (decl_ctx_die); + if (pos != m_die_to_decl_ctx.end()) + return pos->second; + + switch (decl_ctx_die->Tag()) + { + case DW_TAG_compile_unit: + return m_clang_tu_decl; + + case DW_TAG_namespace: + return ResolveNamespaceDIE (cu, decl_ctx_die); + break; + + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + { + Type* type = ResolveType (cu, decl_ctx_die); + if (type) + { + clang::DeclContext *decl_ctx = type->GetClangForwardType().GetDeclContextForType (); + if (decl_ctx) + { + LinkDeclContextToDIE (decl_ctx, decl_ctx_die); + if (decl_ctx) + return decl_ctx; + } + } + } + break; + + default: + break; + } + } + return m_clang_tu_decl; +} + + +const DWARFDebugInfoEntry * +SymbolFileDWARF::GetDeclContextDIEContainingDIE (DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die) +{ + if (cu && die) + { + const DWARFDebugInfoEntry * const decl_die = die; + + while (die != NULL) + { + // If this is the original DIE that we are searching for a declaration + // for, then don't look in the cache as we don't want our own decl + // context to be our decl context... + if (decl_die != die) + { + switch (die->Tag()) + { + case DW_TAG_compile_unit: + case DW_TAG_namespace: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + return die; + + default: + break; + } + } + + dw_offset_t die_offset = die->GetAttributeValueAsReference(this, cu, DW_AT_specification, DW_INVALID_OFFSET); + if (die_offset != DW_INVALID_OFFSET) + { + DWARFCompileUnit *spec_cu = cu; + const DWARFDebugInfoEntry *spec_die = DebugInfo()->GetDIEPtrWithCompileUnitHint (die_offset, &spec_cu); + const DWARFDebugInfoEntry *spec_die_decl_ctx_die = GetDeclContextDIEContainingDIE (spec_cu, spec_die); + if (spec_die_decl_ctx_die) + return spec_die_decl_ctx_die; + } + + die_offset = die->GetAttributeValueAsReference(this, cu, DW_AT_abstract_origin, DW_INVALID_OFFSET); + if (die_offset != DW_INVALID_OFFSET) + { + DWARFCompileUnit *abs_cu = cu; + const DWARFDebugInfoEntry *abs_die = DebugInfo()->GetDIEPtrWithCompileUnitHint (die_offset, &abs_cu); + const DWARFDebugInfoEntry *abs_die_decl_ctx_die = GetDeclContextDIEContainingDIE (abs_cu, abs_die); + if (abs_die_decl_ctx_die) + return abs_die_decl_ctx_die; + } + + die = die->GetParent(); + } + } + return NULL; +} + + +Symbol * +SymbolFileDWARF::GetObjCClassSymbol (const ConstString &objc_class_name) +{ + Symbol *objc_class_symbol = NULL; + if (m_obj_file) + { + Symtab *symtab = m_obj_file->GetSymtab (); + if (symtab) + { + objc_class_symbol = symtab->FindFirstSymbolWithNameAndType (objc_class_name, + eSymbolTypeObjCClass, + Symtab::eDebugNo, + Symtab::eVisibilityAny); + } + } + return objc_class_symbol; +} + +// Some compilers don't emit the DW_AT_APPLE_objc_complete_type attribute. If they don't +// then we can end up looking through all class types for a complete type and never find +// the full definition. We need to know if this attribute is supported, so we determine +// this here and cache th result. We also need to worry about the debug map DWARF file +// if we are doing darwin DWARF in .o file debugging. +bool +SymbolFileDWARF::Supports_DW_AT_APPLE_objc_complete_type (DWARFCompileUnit *cu) +{ + if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) + { + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo; + if (cu && cu->Supports_DW_AT_APPLE_objc_complete_type()) + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; + else + { + DWARFDebugInfo* debug_info = DebugInfo(); + const uint32_t num_compile_units = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) + { + DWARFCompileUnit* dwarf_cu = debug_info->GetCompileUnitAtIndex(cu_idx); + if (dwarf_cu != cu && dwarf_cu->Supports_DW_AT_APPLE_objc_complete_type()) + { + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; + break; + } + } + } + if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolNo && GetDebugMapSymfile ()) + return m_debug_map_symfile->Supports_DW_AT_APPLE_objc_complete_type (this); + } + return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes; +} + +// This function can be used when a DIE is found that is a forward declaration +// DIE and we want to try and find a type that has the complete definition. +TypeSP +SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE (const DWARFDebugInfoEntry *die, + const ConstString &type_name, + bool must_be_implementation) +{ + + TypeSP type_sp; + + if (!type_name || (must_be_implementation && !GetObjCClassSymbol (type_name))) + return type_sp; + + DIEArray die_offsets; + + if (m_using_apple_tables) + { + if (m_apple_types_ap.get()) + { + const char *name_cstr = type_name.GetCString(); + m_apple_types_ap->FindCompleteObjCClassByName (name_cstr, die_offsets, must_be_implementation); + } + } + else + { + if (!m_indexed) + Index (); + + m_type_index.Find (type_name, die_offsets); + } + + const size_t num_matches = die_offsets.size(); + + DWARFCompileUnit* type_cu = NULL; + const DWARFDebugInfoEntry* type_die = NULL; + if (num_matches) + { + DWARFDebugInfo* debug_info = DebugInfo(); + for (size_t i=0; i<num_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + type_die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &type_cu); + + if (type_die) + { + bool try_resolving_type = false; + + // Don't try and resolve the DIE we are looking for with the DIE itself! + if (type_die != die) + { + switch (type_die->Tag()) + { + case DW_TAG_class_type: + case DW_TAG_structure_type: + try_resolving_type = true; + break; + default: + break; + } + } + + if (try_resolving_type) + { + if (must_be_implementation && type_cu->Supports_DW_AT_APPLE_objc_complete_type()) + try_resolving_type = type_die->GetAttributeValueAsUnsigned (this, type_cu, DW_AT_APPLE_objc_complete_type, 0); + + if (try_resolving_type) + { + Type *resolved_type = ResolveType (type_cu, type_die, false); + if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) + { + DEBUG_PRINTF ("resolved 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ") from %s to 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ")\n", + MakeUserID(die->GetOffset()), + MakeUserID(dwarf_cu->GetOffset()), + m_obj_file->GetFileSpec().GetFilename().AsCString(), + MakeUserID(type_die->GetOffset()), + MakeUserID(type_cu->GetOffset())); + + if (die) + m_die_to_type[die] = resolved_type; + type_sp = resolved_type->shared_from_this(); + break; + } + } + } + } + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_types accelerator table had bad die 0x%8.8x for '%s')\n", + die_offset, type_name.GetCString()); + } + } + + } + } + return type_sp; +} + + +//---------------------------------------------------------------------- +// This function helps to ensure that the declaration contexts match for +// two different DIEs. Often times debug information will refer to a +// forward declaration of a type (the equivalent of "struct my_struct;". +// There will often be a declaration of that type elsewhere that has the +// full definition. When we go looking for the full type "my_struct", we +// will find one or more matches in the accelerator tables and we will +// then need to make sure the type was in the same declaration context +// as the original DIE. This function can efficiently compare two DIEs +// and will return true when the declaration context matches, and false +// when they don't. +//---------------------------------------------------------------------- +bool +SymbolFileDWARF::DIEDeclContextsMatch (DWARFCompileUnit* cu1, const DWARFDebugInfoEntry *die1, + DWARFCompileUnit* cu2, const DWARFDebugInfoEntry *die2) +{ + if (die1 == die2) + return true; + +#if defined (LLDB_CONFIGURATION_DEBUG) + // You can't and shouldn't call this function with a compile unit from + // two different SymbolFileDWARF instances. + assert (DebugInfo()->ContainsCompileUnit (cu1)); + assert (DebugInfo()->ContainsCompileUnit (cu2)); +#endif + + DWARFDIECollection decl_ctx_1; + DWARFDIECollection decl_ctx_2; + //The declaration DIE stack is a stack of the declaration context + // DIEs all the way back to the compile unit. If a type "T" is + // declared inside a class "B", and class "B" is declared inside + // a class "A" and class "A" is in a namespace "lldb", and the + // namespace is in a compile unit, there will be a stack of DIEs: + // + // [0] DW_TAG_class_type for "B" + // [1] DW_TAG_class_type for "A" + // [2] DW_TAG_namespace for "lldb" + // [3] DW_TAG_compile_unit for the source file. + // + // We grab both contexts and make sure that everything matches + // all the way back to the compiler unit. + + // First lets grab the decl contexts for both DIEs + die1->GetDeclContextDIEs (this, cu1, decl_ctx_1); + die2->GetDeclContextDIEs (this, cu2, decl_ctx_2); + // Make sure the context arrays have the same size, otherwise + // we are done + const size_t count1 = decl_ctx_1.Size(); + const size_t count2 = decl_ctx_2.Size(); + if (count1 != count2) + return false; + + // Make sure the DW_TAG values match all the way back up the the + // compile unit. If they don't, then we are done. + const DWARFDebugInfoEntry *decl_ctx_die1; + const DWARFDebugInfoEntry *decl_ctx_die2; + size_t i; + for (i=0; i<count1; i++) + { + decl_ctx_die1 = decl_ctx_1.GetDIEPtrAtIndex (i); + decl_ctx_die2 = decl_ctx_2.GetDIEPtrAtIndex (i); + if (decl_ctx_die1->Tag() != decl_ctx_die2->Tag()) + return false; + } +#if defined LLDB_CONFIGURATION_DEBUG + + // Make sure the top item in the decl context die array is always + // DW_TAG_compile_unit. If it isn't then something went wrong in + // the DWARFDebugInfoEntry::GetDeclContextDIEs() function... + assert (decl_ctx_1.GetDIEPtrAtIndex (count1 - 1)->Tag() == DW_TAG_compile_unit); + +#endif + // Always skip the compile unit when comparing by only iterating up to + // "count - 1". Here we compare the names as we go. + for (i=0; i<count1 - 1; i++) + { + decl_ctx_die1 = decl_ctx_1.GetDIEPtrAtIndex (i); + decl_ctx_die2 = decl_ctx_2.GetDIEPtrAtIndex (i); + const char *name1 = decl_ctx_die1->GetName(this, cu1); + const char *name2 = decl_ctx_die2->GetName(this, cu2); + // If the string was from a DW_FORM_strp, then the pointer will often + // be the same! + if (name1 == name2) + continue; + + // Name pointers are not equal, so only compare the strings + // if both are not NULL. + if (name1 && name2) + { + // If the strings don't compare, we are done... + if (strcmp(name1, name2) != 0) + return false; + } + else + { + // One name was NULL while the other wasn't + return false; + } + } + // We made it through all of the checks and the declaration contexts + // are equal. + return true; +} + +// This function can be used when a DIE is found that is a forward declaration +// DIE and we want to try and find a type that has the complete definition. +// "cu" and "die" must be from this SymbolFileDWARF +TypeSP +SymbolFileDWARF::FindDefinitionTypeForDIE (DWARFCompileUnit* cu, + const DWARFDebugInfoEntry *die, + const ConstString &type_name) +{ + TypeSP type_sp; + +#if defined (LLDB_CONFIGURATION_DEBUG) + // You can't and shouldn't call this function with a compile unit from + // another SymbolFileDWARF instance. + assert (DebugInfo()->ContainsCompileUnit (cu)); +#endif + + if (cu == NULL || die == NULL || !type_name) + return type_sp; + + std::string qualified_name; + + Log *log (LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION|DWARF_LOG_LOOKUPS)); + if (log) + { + die->GetQualifiedName(this, cu, qualified_name); + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindDefinitionTypeForDIE(die=0x%8.8x (%s), name='%s')", + die->GetOffset(), + qualified_name.c_str(), + type_name.GetCString()); + } + + DIEArray die_offsets; + + if (m_using_apple_tables) + { + if (m_apple_types_ap.get()) + { + const bool has_tag = m_apple_types_ap->GetHeader().header_data.ContainsAtom (DWARFMappedHash::eAtomTypeTag); + const bool has_qualified_name_hash = m_apple_types_ap->GetHeader().header_data.ContainsAtom (DWARFMappedHash::eAtomTypeQualNameHash); + if (has_tag && has_qualified_name_hash) + { + if (qualified_name.empty()) + die->GetQualifiedName(this, cu, qualified_name); + + const uint32_t qualified_name_hash = MappedHash::HashStringUsingDJB (qualified_name.c_str()); + if (log) + GetObjectFile()->GetModule()->LogMessage (log,"FindByNameAndTagAndQualifiedNameHash()"); + m_apple_types_ap->FindByNameAndTagAndQualifiedNameHash (type_name.GetCString(), die->Tag(), qualified_name_hash, die_offsets); + } + else if (has_tag > 1) + { + if (log) + GetObjectFile()->GetModule()->LogMessage (log,"FindByNameAndTag()"); + m_apple_types_ap->FindByNameAndTag (type_name.GetCString(), die->Tag(), die_offsets); + } + else + { + m_apple_types_ap->FindByName (type_name.GetCString(), die_offsets); + } + } + } + else + { + if (!m_indexed) + Index (); + + m_type_index.Find (type_name, die_offsets); + } + + const size_t num_matches = die_offsets.size(); + + const dw_tag_t die_tag = die->Tag(); + + DWARFCompileUnit* type_cu = NULL; + const DWARFDebugInfoEntry* type_die = NULL; + if (num_matches) + { + DWARFDebugInfo* debug_info = DebugInfo(); + for (size_t i=0; i<num_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + type_die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &type_cu); + + if (type_die) + { + bool try_resolving_type = false; + + // Don't try and resolve the DIE we are looking for with the DIE itself! + if (type_die != die) + { + const dw_tag_t type_die_tag = type_die->Tag(); + // Make sure the tags match + if (type_die_tag == die_tag) + { + // The tags match, lets try resolving this type + try_resolving_type = true; + } + else + { + // The tags don't match, but we need to watch our for a + // forward declaration for a struct and ("struct foo") + // ends up being a class ("class foo { ... };") or + // vice versa. + switch (type_die_tag) + { + case DW_TAG_class_type: + // We had a "class foo", see if we ended up with a "struct foo { ... };" + try_resolving_type = (die_tag == DW_TAG_structure_type); + break; + case DW_TAG_structure_type: + // We had a "struct foo", see if we ended up with a "class foo { ... };" + try_resolving_type = (die_tag == DW_TAG_class_type); + break; + default: + // Tags don't match, don't event try to resolve + // using this type whose name matches.... + break; + } + } + } + + if (try_resolving_type) + { + if (log) + { + std::string qualified_name; + type_die->GetQualifiedName(this, cu, qualified_name); + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindDefinitionTypeForDIE(die=0x%8.8x, name='%s') trying die=0x%8.8x (%s)", + die->GetOffset(), + type_name.GetCString(), + type_die->GetOffset(), + qualified_name.c_str()); + } + + // Make sure the decl contexts match all the way up + if (DIEDeclContextsMatch(cu, die, type_cu, type_die)) + { + Type *resolved_type = ResolveType (type_cu, type_die, false); + if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) + { + DEBUG_PRINTF ("resolved 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ") from %s to 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ")\n", + MakeUserID(die->GetOffset()), + MakeUserID(dwarf_cu->GetOffset()), + m_obj_file->GetFileSpec().GetFilename().AsCString(), + MakeUserID(type_die->GetOffset()), + MakeUserID(type_cu->GetOffset())); + + m_die_to_type[die] = resolved_type; + type_sp = resolved_type->shared_from_this(); + break; + } + } + } + else + { + if (log) + { + std::string qualified_name; + type_die->GetQualifiedName(this, cu, qualified_name); + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindDefinitionTypeForDIE(die=0x%8.8x, name='%s') ignoring die=0x%8.8x (%s)", + die->GetOffset(), + type_name.GetCString(), + type_die->GetOffset(), + qualified_name.c_str()); + } + } + } + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_types accelerator table had bad die 0x%8.8x for '%s')\n", + die_offset, type_name.GetCString()); + } + } + + } + } + return type_sp; +} + +TypeSP +SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext (const DWARFDeclContext &dwarf_decl_ctx) +{ + TypeSP type_sp; + + const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize(); + if (dwarf_decl_ctx_count > 0) + { + const ConstString type_name(dwarf_decl_ctx[0].name); + const dw_tag_t tag = dwarf_decl_ctx[0].tag; + + if (type_name) + { + Log *log (LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION|DWARF_LOG_LOOKUPS)); + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%s, qualified-name='%s')", + DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), + dwarf_decl_ctx.GetQualifiedName()); + } + + DIEArray die_offsets; + + if (m_using_apple_tables) + { + if (m_apple_types_ap.get()) + { + const bool has_tag = m_apple_types_ap->GetHeader().header_data.ContainsAtom (DWARFMappedHash::eAtomTypeTag); + const bool has_qualified_name_hash = m_apple_types_ap->GetHeader().header_data.ContainsAtom (DWARFMappedHash::eAtomTypeQualNameHash); + if (has_tag && has_qualified_name_hash) + { + const char *qualified_name = dwarf_decl_ctx.GetQualifiedName(); + const uint32_t qualified_name_hash = MappedHash::HashStringUsingDJB (qualified_name); + if (log) + GetObjectFile()->GetModule()->LogMessage (log,"FindByNameAndTagAndQualifiedNameHash()"); + m_apple_types_ap->FindByNameAndTagAndQualifiedNameHash (type_name.GetCString(), tag, qualified_name_hash, die_offsets); + } + else if (has_tag) + { + if (log) + GetObjectFile()->GetModule()->LogMessage (log,"FindByNameAndTag()"); + m_apple_types_ap->FindByNameAndTag (type_name.GetCString(), tag, die_offsets); + } + else + { + m_apple_types_ap->FindByName (type_name.GetCString(), die_offsets); + } + } + } + else + { + if (!m_indexed) + Index (); + + m_type_index.Find (type_name, die_offsets); + } + + const size_t num_matches = die_offsets.size(); + + + DWARFCompileUnit* type_cu = NULL; + const DWARFDebugInfoEntry* type_die = NULL; + if (num_matches) + { + DWARFDebugInfo* debug_info = DebugInfo(); + for (size_t i=0; i<num_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + type_die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &type_cu); + + if (type_die) + { + bool try_resolving_type = false; + + // Don't try and resolve the DIE we are looking for with the DIE itself! + const dw_tag_t type_tag = type_die->Tag(); + // Make sure the tags match + if (type_tag == tag) + { + // The tags match, lets try resolving this type + try_resolving_type = true; + } + else + { + // The tags don't match, but we need to watch our for a + // forward declaration for a struct and ("struct foo") + // ends up being a class ("class foo { ... };") or + // vice versa. + switch (type_tag) + { + case DW_TAG_class_type: + // We had a "class foo", see if we ended up with a "struct foo { ... };" + try_resolving_type = (tag == DW_TAG_structure_type); + break; + case DW_TAG_structure_type: + // We had a "struct foo", see if we ended up with a "class foo { ... };" + try_resolving_type = (tag == DW_TAG_class_type); + break; + default: + // Tags don't match, don't event try to resolve + // using this type whose name matches.... + break; + } + } + + if (try_resolving_type) + { + DWARFDeclContext type_dwarf_decl_ctx; + type_die->GetDWARFDeclContext (this, type_cu, type_dwarf_decl_ctx); + + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%s, qualified-name='%s') trying die=0x%8.8x (%s)", + DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), + dwarf_decl_ctx.GetQualifiedName(), + type_die->GetOffset(), + type_dwarf_decl_ctx.GetQualifiedName()); + } + + // Make sure the decl contexts match all the way up + if (dwarf_decl_ctx == type_dwarf_decl_ctx) + { + Type *resolved_type = ResolveType (type_cu, type_die, false); + if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) + { + type_sp = resolved_type->shared_from_this(); + break; + } + } + } + else + { + if (log) + { + std::string qualified_name; + type_die->GetQualifiedName(this, type_cu, qualified_name); + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%s, qualified-name='%s') ignoring die=0x%8.8x (%s)", + DW_TAG_value_to_name(dwarf_decl_ctx[0].tag), + dwarf_decl_ctx.GetQualifiedName(), + type_die->GetOffset(), + qualified_name.c_str()); + } + } + } + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_types accelerator table had bad die 0x%8.8x for '%s')\n", + die_offset, type_name.GetCString()); + } + } + + } + } + } + } + return type_sp; +} + +bool +SymbolFileDWARF::CopyUniqueClassMethodTypes (SymbolFileDWARF *src_symfile, + Type *class_type, + DWARFCompileUnit* src_cu, + const DWARFDebugInfoEntry *src_class_die, + DWARFCompileUnit* dst_cu, + const DWARFDebugInfoEntry *dst_class_die, + llvm::SmallVectorImpl <const DWARFDebugInfoEntry *> &failures) +{ + if (!class_type || !src_cu || !src_class_die || !dst_cu || !dst_class_die) + return false; + if (src_class_die->Tag() != dst_class_die->Tag()) + return false; + + // We need to complete the class type so we can get all of the method types + // parsed so we can then unique those types to their equivalent counterparts + // in "dst_cu" and "dst_class_die" + class_type->GetClangFullType(); + + const DWARFDebugInfoEntry *src_die; + const DWARFDebugInfoEntry *dst_die; + UniqueCStringMap<const DWARFDebugInfoEntry *> src_name_to_die; + UniqueCStringMap<const DWARFDebugInfoEntry *> dst_name_to_die; + UniqueCStringMap<const DWARFDebugInfoEntry *> src_name_to_die_artificial; + UniqueCStringMap<const DWARFDebugInfoEntry *> dst_name_to_die_artificial; + for (src_die = src_class_die->GetFirstChild(); src_die != NULL; src_die = src_die->GetSibling()) + { + if (src_die->Tag() == DW_TAG_subprogram) + { + // Make sure this is a declaration and not a concrete instance by looking + // for DW_AT_declaration set to 1. Sometimes concrete function instances + // are placed inside the class definitions and shouldn't be included in + // the list of things are are tracking here. + if (src_die->GetAttributeValueAsUnsigned(src_symfile, src_cu, DW_AT_declaration, 0) == 1) + { + const char *src_name = src_die->GetMangledName (src_symfile, src_cu); + if (src_name) + { + ConstString src_const_name(src_name); + if (src_die->GetAttributeValueAsUnsigned(src_symfile, src_cu, DW_AT_artificial, 0)) + src_name_to_die_artificial.Append(src_const_name.GetCString(), src_die); + else + src_name_to_die.Append(src_const_name.GetCString(), src_die); + } + } + } + } + for (dst_die = dst_class_die->GetFirstChild(); dst_die != NULL; dst_die = dst_die->GetSibling()) + { + if (dst_die->Tag() == DW_TAG_subprogram) + { + // Make sure this is a declaration and not a concrete instance by looking + // for DW_AT_declaration set to 1. Sometimes concrete function instances + // are placed inside the class definitions and shouldn't be included in + // the list of things are are tracking here. + if (dst_die->GetAttributeValueAsUnsigned(this, dst_cu, DW_AT_declaration, 0) == 1) + { + const char *dst_name = dst_die->GetMangledName (this, dst_cu); + if (dst_name) + { + ConstString dst_const_name(dst_name); + if (dst_die->GetAttributeValueAsUnsigned(this, dst_cu, DW_AT_artificial, 0)) + dst_name_to_die_artificial.Append(dst_const_name.GetCString(), dst_die); + else + dst_name_to_die.Append(dst_const_name.GetCString(), dst_die); + } + } + } + } + const uint32_t src_size = src_name_to_die.GetSize (); + const uint32_t dst_size = dst_name_to_die.GetSize (); + Log *log (LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO | DWARF_LOG_TYPE_COMPLETION)); + + // Is everything kosher so we can go through the members at top speed? + bool fast_path = true; + + if (src_size != dst_size) + { + if (src_size != 0 && dst_size != 0) + { + if (log) + log->Printf("warning: trying to unique class DIE 0x%8.8x to 0x%8.8x, but they didn't have the same size (src=%d, dst=%d)", + src_class_die->GetOffset(), + dst_class_die->GetOffset(), + src_size, + dst_size); + } + + fast_path = false; + } + + uint32_t idx; + + if (fast_path) + { + for (idx = 0; idx < src_size; ++idx) + { + src_die = src_name_to_die.GetValueAtIndexUnchecked (idx); + dst_die = dst_name_to_die.GetValueAtIndexUnchecked (idx); + + if (src_die->Tag() != dst_die->Tag()) + { + if (log) + log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, but 0x%8.8x (%s) tags didn't match 0x%8.8x (%s)", + src_class_die->GetOffset(), + dst_class_die->GetOffset(), + src_die->GetOffset(), + DW_TAG_value_to_name(src_die->Tag()), + dst_die->GetOffset(), + DW_TAG_value_to_name(src_die->Tag())); + fast_path = false; + } + + const char *src_name = src_die->GetMangledName (src_symfile, src_cu); + const char *dst_name = dst_die->GetMangledName (this, dst_cu); + + // Make sure the names match + if (src_name == dst_name || (strcmp (src_name, dst_name) == 0)) + continue; + + if (log) + log->Printf("warning: tried to unique class DIE 0x%8.8x to 0x%8.8x, but 0x%8.8x (%s) names didn't match 0x%8.8x (%s)", + src_class_die->GetOffset(), + dst_class_die->GetOffset(), + src_die->GetOffset(), + src_name, + dst_die->GetOffset(), + dst_name); + + fast_path = false; + } + } + + // Now do the work of linking the DeclContexts and Types. + if (fast_path) + { + // We can do this quickly. Just run across the tables index-for-index since + // we know each node has matching names and tags. + for (idx = 0; idx < src_size; ++idx) + { + src_die = src_name_to_die.GetValueAtIndexUnchecked (idx); + dst_die = dst_name_to_die.GetValueAtIndexUnchecked (idx); + + clang::DeclContext *src_decl_ctx = src_symfile->m_die_to_decl_ctx[src_die]; + if (src_decl_ctx) + { + if (log) + log->Printf ("uniquing decl context %p from 0x%8.8x for 0x%8.8x", src_decl_ctx, src_die->GetOffset(), dst_die->GetOffset()); + LinkDeclContextToDIE (src_decl_ctx, dst_die); + } + else + { + if (log) + log->Printf ("warning: tried to unique decl context from 0x%8.8x for 0x%8.8x, but none was found", src_die->GetOffset(), dst_die->GetOffset()); + } + + Type *src_child_type = m_die_to_type[src_die]; + if (src_child_type) + { + if (log) + log->Printf ("uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", src_child_type, src_child_type->GetID(), src_die->GetOffset(), dst_die->GetOffset()); + m_die_to_type[dst_die] = src_child_type; + } + else + { + if (log) + log->Printf ("warning: tried to unique lldb_private::Type from 0x%8.8x for 0x%8.8x, but none was found", src_die->GetOffset(), dst_die->GetOffset()); + } + } + } + else + { + // We must do this slowly. For each member of the destination, look + // up a member in the source with the same name, check its tag, and + // unique them if everything matches up. Report failures. + + if (!src_name_to_die.IsEmpty() && !dst_name_to_die.IsEmpty()) + { + src_name_to_die.Sort(); + + for (idx = 0; idx < dst_size; ++idx) + { + const char *dst_name = dst_name_to_die.GetCStringAtIndex(idx); + dst_die = dst_name_to_die.GetValueAtIndexUnchecked(idx); + src_die = src_name_to_die.Find(dst_name, NULL); + + if (src_die && (src_die->Tag() == dst_die->Tag())) + { + clang::DeclContext *src_decl_ctx = src_symfile->m_die_to_decl_ctx[src_die]; + if (src_decl_ctx) + { + if (log) + log->Printf ("uniquing decl context %p from 0x%8.8x for 0x%8.8x", src_decl_ctx, src_die->GetOffset(), dst_die->GetOffset()); + LinkDeclContextToDIE (src_decl_ctx, dst_die); + } + else + { + if (log) + log->Printf ("warning: tried to unique decl context from 0x%8.8x for 0x%8.8x, but none was found", src_die->GetOffset(), dst_die->GetOffset()); + } + + Type *src_child_type = m_die_to_type[src_die]; + if (src_child_type) + { + if (log) + log->Printf ("uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", src_child_type, src_child_type->GetID(), src_die->GetOffset(), dst_die->GetOffset()); + m_die_to_type[dst_die] = src_child_type; + } + else + { + if (log) + log->Printf ("warning: tried to unique lldb_private::Type from 0x%8.8x for 0x%8.8x, but none was found", src_die->GetOffset(), dst_die->GetOffset()); + } + } + else + { + if (log) + log->Printf ("warning: couldn't find a match for 0x%8.8x", dst_die->GetOffset()); + + failures.push_back(dst_die); + } + } + } + } + + const uint32_t src_size_artificial = src_name_to_die_artificial.GetSize (); + const uint32_t dst_size_artificial = dst_name_to_die_artificial.GetSize (); + + UniqueCStringMap<const DWARFDebugInfoEntry *> name_to_die_artificial_not_in_src; + + if (src_size_artificial && dst_size_artificial) + { + dst_name_to_die_artificial.Sort(); + + for (idx = 0; idx < src_size_artificial; ++idx) + { + const char *src_name_artificial = src_name_to_die_artificial.GetCStringAtIndex(idx); + src_die = src_name_to_die_artificial.GetValueAtIndexUnchecked (idx); + dst_die = dst_name_to_die_artificial.Find(src_name_artificial, NULL); + + if (dst_die) + { + // Both classes have the artificial types, link them + clang::DeclContext *src_decl_ctx = m_die_to_decl_ctx[src_die]; + if (src_decl_ctx) + { + if (log) + log->Printf ("uniquing decl context %p from 0x%8.8x for 0x%8.8x", src_decl_ctx, src_die->GetOffset(), dst_die->GetOffset()); + LinkDeclContextToDIE (src_decl_ctx, dst_die); + } + else + { + if (log) + log->Printf ("warning: tried to unique decl context from 0x%8.8x for 0x%8.8x, but none was found", src_die->GetOffset(), dst_die->GetOffset()); + } + + Type *src_child_type = m_die_to_type[src_die]; + if (src_child_type) + { + if (log) + log->Printf ("uniquing type %p (uid=0x%" PRIx64 ") from 0x%8.8x for 0x%8.8x", src_child_type, src_child_type->GetID(), src_die->GetOffset(), dst_die->GetOffset()); + m_die_to_type[dst_die] = src_child_type; + } + else + { + if (log) + log->Printf ("warning: tried to unique lldb_private::Type from 0x%8.8x for 0x%8.8x, but none was found", src_die->GetOffset(), dst_die->GetOffset()); + } + } + } + } + + if (dst_size_artificial) + { + for (idx = 0; idx < dst_size_artificial; ++idx) + { + const char *dst_name_artificial = dst_name_to_die_artificial.GetCStringAtIndex(idx); + dst_die = dst_name_to_die_artificial.GetValueAtIndexUnchecked (idx); + if (log) + log->Printf ("warning: need to create artificial method for 0x%8.8x for method '%s'", dst_die->GetOffset(), dst_name_artificial); + + failures.push_back(dst_die); + } + } + + return (failures.size() != 0); +} + +TypeSP +SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool *type_is_new_ptr) +{ + TypeSP type_sp; + + if (type_is_new_ptr) + *type_is_new_ptr = false; + +#if defined(LLDB_CONFIGURATION_DEBUG) or defined(LLDB_CONFIGURATION_RELEASE) + static DIEStack g_die_stack; + DIEStack::ScopedPopper scoped_die_logger(g_die_stack); +#endif + + AccessType accessibility = eAccessNone; + if (die != NULL) + { + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); + if (log) + { + const DWARFDebugInfoEntry *context_die; + clang::DeclContext *context = GetClangDeclContextContainingDIE (dwarf_cu, die, &context_die); + + GetObjectFile()->GetModule()->LogMessage (log, "SymbolFileDWARF::ParseType (die = 0x%8.8x, decl_ctx = %p (die 0x%8.8x)) %s name = '%s')", + die->GetOffset(), + context, + context_die->GetOffset(), + DW_TAG_value_to_name(die->Tag()), + die->GetName(this, dwarf_cu)); + +#if defined(LLDB_CONFIGURATION_DEBUG) or defined(LLDB_CONFIGURATION_RELEASE) + scoped_die_logger.Push (dwarf_cu, die); + g_die_stack.LogDIEs(log, this); +#endif + } +// +// Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); +// if (log && dwarf_cu) +// { +// StreamString s; +// die->DumpLocation (this, dwarf_cu, s); +// GetObjectFile()->GetModule()->LogMessage (log, "SymbolFileDwarf::%s %s", __FUNCTION__, s.GetData()); +// +// } + + Type *type_ptr = m_die_to_type.lookup (die); + TypeList* type_list = GetTypeList(); + if (type_ptr == NULL) + { + ClangASTContext &ast = GetClangASTContext(); + if (type_is_new_ptr) + *type_is_new_ptr = true; + + const dw_tag_t tag = die->Tag(); + + bool is_forward_declaration = false; + DWARFDebugInfoEntry::Attributes attributes; + const char *type_name_cstr = NULL; + ConstString type_name_const_str; + Type::ResolveState resolve_state = Type::eResolveStateUnresolved; + uint64_t byte_size = 0; + Declaration decl; + + Type::EncodingDataType encoding_data_type = Type::eEncodingIsUID; + ClangASTType clang_type; + + dw_attr_t attr; + + switch (tag) + { + case DW_TAG_base_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + case DW_TAG_typedef: + case DW_TAG_const_type: + case DW_TAG_restrict_type: + case DW_TAG_volatile_type: + case DW_TAG_unspecified_type: + { + // Set a bit that lets us know that we are currently parsing this + m_die_to_type[die] = DIE_IS_BEING_PARSED; + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, NULL, attributes); + uint32_t encoding = 0; + lldb::user_id_t encoding_uid = LLDB_INVALID_UID; + + if (num_attributes > 0) + { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + // Work around a bug in llvm-gcc where they give a name to a reference type which doesn't + // include the "&"... + if (tag == DW_TAG_reference_type) + { + if (strchr (type_name_cstr, '&') == NULL) + type_name_cstr = NULL; + } + if (type_name_cstr) + type_name_const_str.SetCString(type_name_cstr); + break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_encoding: encoding = form_value.Unsigned(); break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + default: + case DW_AT_sibling: + break; + } + } + } + } + + DEBUG_PRINTF ("0x%8.8" PRIx64 ": %s (\"%s\") type => 0x%8.8x\n", MakeUserID(die->GetOffset()), DW_TAG_value_to_name(tag), type_name_cstr, encoding_uid); + + switch (tag) + { + default: + break; + + case DW_TAG_unspecified_type: + if (strcmp(type_name_cstr, "nullptr_t") == 0 || + strcmp(type_name_cstr, "decltype(nullptr)") == 0 ) + { + resolve_state = Type::eResolveStateFull; + clang_type = ast.GetBasicType(eBasicTypeNullPtr); + break; + } + // Fall through to base type below in case we can handle the type there... + + case DW_TAG_base_type: + resolve_state = Type::eResolveStateFull; + clang_type = ast.GetBuiltinTypeForDWARFEncodingAndBitSize (type_name_cstr, + encoding, + byte_size * 8); + break; + + case DW_TAG_pointer_type: encoding_data_type = Type::eEncodingIsPointerUID; break; + case DW_TAG_reference_type: encoding_data_type = Type::eEncodingIsLValueReferenceUID; break; + case DW_TAG_rvalue_reference_type: encoding_data_type = Type::eEncodingIsRValueReferenceUID; break; + case DW_TAG_typedef: encoding_data_type = Type::eEncodingIsTypedefUID; break; + case DW_TAG_const_type: encoding_data_type = Type::eEncodingIsConstUID; break; + case DW_TAG_restrict_type: encoding_data_type = Type::eEncodingIsRestrictUID; break; + case DW_TAG_volatile_type: encoding_data_type = Type::eEncodingIsVolatileUID; break; + } + + if (!clang_type && (encoding_data_type == Type::eEncodingIsPointerUID || encoding_data_type == Type::eEncodingIsTypedefUID) && sc.comp_unit != NULL) + { + bool translation_unit_is_objc = (sc.comp_unit->GetLanguage() == eLanguageTypeObjC || sc.comp_unit->GetLanguage() == eLanguageTypeObjC_plus_plus); + + if (translation_unit_is_objc) + { + if (type_name_cstr != NULL) + { + static ConstString g_objc_type_name_id("id"); + static ConstString g_objc_type_name_Class("Class"); + static ConstString g_objc_type_name_selector("SEL"); + + if (type_name_const_str == g_objc_type_name_id) + { + if (log) + GetObjectFile()->GetModule()->LogMessage (log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' is Objective C 'id' built-in type.", + die->GetOffset(), + DW_TAG_value_to_name(die->Tag()), + die->GetName(this, dwarf_cu)); + clang_type = ast.GetBasicType(eBasicTypeObjCID); + encoding_data_type = Type::eEncodingIsUID; + encoding_uid = LLDB_INVALID_UID; + resolve_state = Type::eResolveStateFull; + + } + else if (type_name_const_str == g_objc_type_name_Class) + { + if (log) + GetObjectFile()->GetModule()->LogMessage (log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' is Objective C 'Class' built-in type.", + die->GetOffset(), + DW_TAG_value_to_name(die->Tag()), + die->GetName(this, dwarf_cu)); + clang_type = ast.GetBasicType(eBasicTypeObjCClass); + encoding_data_type = Type::eEncodingIsUID; + encoding_uid = LLDB_INVALID_UID; + resolve_state = Type::eResolveStateFull; + } + else if (type_name_const_str == g_objc_type_name_selector) + { + if (log) + GetObjectFile()->GetModule()->LogMessage (log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' is Objective C 'selector' built-in type.", + die->GetOffset(), + DW_TAG_value_to_name(die->Tag()), + die->GetName(this, dwarf_cu)); + clang_type = ast.GetBasicType(eBasicTypeObjCSel); + encoding_data_type = Type::eEncodingIsUID; + encoding_uid = LLDB_INVALID_UID; + resolve_state = Type::eResolveStateFull; + } + } + else if (encoding_data_type == Type::eEncodingIsPointerUID && encoding_uid != LLDB_INVALID_UID) + { + // Clang sometimes erroneously emits id as objc_object*. In that case we fix up the type to "id". + + DWARFDebugInfoEntry* encoding_die = dwarf_cu->GetDIEPtr(encoding_uid); + + if (encoding_die && encoding_die->Tag() == DW_TAG_structure_type) + { + if (const char *struct_name = encoding_die->GetAttributeValueAsString(this, dwarf_cu, DW_AT_name, NULL)) + { + if (!strcmp(struct_name, "objc_object")) + { + if (log) + GetObjectFile()->GetModule()->LogMessage (log, "SymbolFileDWARF::ParseType (die = 0x%8.8x) %s '%s' is 'objc_object*', which we overrode to 'id'.", + die->GetOffset(), + DW_TAG_value_to_name(die->Tag()), + die->GetName(this, dwarf_cu)); + clang_type = ast.GetBasicType(eBasicTypeObjCID); + encoding_data_type = Type::eEncodingIsUID; + encoding_uid = LLDB_INVALID_UID; + resolve_state = Type::eResolveStateFull; + } + } + } + } + } + } + + type_sp.reset( new Type (MakeUserID(die->GetOffset()), + this, + type_name_const_str, + byte_size, + NULL, + encoding_uid, + encoding_data_type, + &decl, + clang_type, + resolve_state)); + + m_die_to_type[die] = type_sp.get(); + +// Type* encoding_type = GetUniquedTypeForDIEOffset(encoding_uid, type_sp, NULL, 0, 0, false); +// if (encoding_type != NULL) +// { +// if (encoding_type != DIE_IS_BEING_PARSED) +// type_sp->SetEncodingType(encoding_type); +// else +// m_indirect_fixups.push_back(type_sp.get()); +// } + } + break; + + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + { + // Set a bit that lets us know that we are currently parsing this + m_die_to_type[die] = DIE_IS_BEING_PARSED; + bool byte_size_valid = false; + + LanguageType class_language = eLanguageTypeUnknown; + bool is_complete_objc_class = false; + //bool struct_is_class = false; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, NULL, attributes); + if (num_attributes > 0) + { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: + if (dwarf_cu->DW_AT_decl_file_attributes_are_invalid()) + { + // llvm-gcc outputs invalid DW_AT_decl_file attributes that always + // point to the compile unit file, so we clear this invalid value + // so that we can still unique types efficiently. + decl.SetFile(FileSpec ("<invalid>", false)); + } + else + decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); + break; + + case DW_AT_decl_line: + decl.SetLine(form_value.Unsigned()); + break; + + case DW_AT_decl_column: + decl.SetColumn(form_value.Unsigned()); + break; + + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_const_str.SetCString(type_name_cstr); + break; + + case DW_AT_byte_size: + byte_size = form_value.Unsigned(); + byte_size_valid = true; + break; + + case DW_AT_accessibility: + accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); + break; + + case DW_AT_declaration: + is_forward_declaration = form_value.Boolean(); + break; + + case DW_AT_APPLE_runtime_class: + class_language = (LanguageType)form_value.Signed(); + break; + + case DW_AT_APPLE_objc_complete_type: + is_complete_objc_class = form_value.Signed(); + break; + + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_start_scope: + case DW_AT_visibility: + default: + case DW_AT_sibling: + break; + } + } + } + } + + UniqueDWARFASTType unique_ast_entry; + + // Only try and unique the type if it has a name. + if (type_name_const_str && + GetUniqueDWARFASTTypeMap().Find (type_name_const_str, + this, + dwarf_cu, + die, + decl, + byte_size_valid ? byte_size : -1, + unique_ast_entry)) + { + // We have already parsed this type or from another + // compile unit. GCC loves to use the "one definition + // rule" which can result in multiple definitions + // of the same class over and over in each compile + // unit. + type_sp = unique_ast_entry.m_type_sp; + if (type_sp) + { + m_die_to_type[die] = type_sp.get(); + return type_sp; + } + } + + DEBUG_PRINTF ("0x%8.8" PRIx64 ": %s (\"%s\")\n", MakeUserID(die->GetOffset()), DW_TAG_value_to_name(tag), type_name_cstr); + + int tag_decl_kind = -1; + AccessType default_accessibility = eAccessNone; + if (tag == DW_TAG_structure_type) + { + tag_decl_kind = clang::TTK_Struct; + default_accessibility = eAccessPublic; + } + else if (tag == DW_TAG_union_type) + { + tag_decl_kind = clang::TTK_Union; + default_accessibility = eAccessPublic; + } + else if (tag == DW_TAG_class_type) + { + tag_decl_kind = clang::TTK_Class; + default_accessibility = eAccessPrivate; + } + + if (byte_size_valid && byte_size == 0 && type_name_cstr && + die->HasChildren() == false && + sc.comp_unit->GetLanguage() == eLanguageTypeObjC) + { + // Work around an issue with clang at the moment where + // forward declarations for objective C classes are emitted + // as: + // DW_TAG_structure_type [2] + // DW_AT_name( "ForwardObjcClass" ) + // DW_AT_byte_size( 0x00 ) + // DW_AT_decl_file( "..." ) + // DW_AT_decl_line( 1 ) + // + // Note that there is no DW_AT_declaration and there are + // no children, and the byte size is zero. + is_forward_declaration = true; + } + + if (class_language == eLanguageTypeObjC || + class_language == eLanguageTypeObjC_plus_plus) + { + if (!is_complete_objc_class && Supports_DW_AT_APPLE_objc_complete_type(dwarf_cu)) + { + // We have a valid eSymbolTypeObjCClass class symbol whose + // name matches the current objective C class that we + // are trying to find and this DIE isn't the complete + // definition (we checked is_complete_objc_class above and + // know it is false), so the real definition is in here somewhere + type_sp = FindCompleteObjCDefinitionTypeForDIE (die, type_name_const_str, true); + + if (!type_sp && GetDebugMapSymfile ()) + { + // We weren't able to find a full declaration in + // this DWARF, see if we have a declaration anywhere + // else... + type_sp = m_debug_map_symfile->FindCompleteObjCDefinitionTypeForDIE (die, type_name_const_str, true); + } + + if (type_sp) + { + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is an incomplete objc type, complete type is 0x%8.8" PRIx64, + this, + die->GetOffset(), + DW_TAG_value_to_name(tag), + type_name_cstr, + type_sp->GetID()); + } + + // We found a real definition for this type elsewhere + // so lets use it and cache the fact that we found + // a complete type for this die + m_die_to_type[die] = type_sp.get(); + return type_sp; + } + } + } + + + if (is_forward_declaration) + { + // We have a forward declaration to a type and we need + // to try and find a full declaration. We look in the + // current type index just in case we have a forward + // declaration followed by an actual declarations in the + // DWARF. If this fails, we need to look elsewhere... + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a forward declaration, trying to find complete type", + this, + die->GetOffset(), + DW_TAG_value_to_name(tag), + type_name_cstr); + } + + DWARFDeclContext die_decl_ctx; + die->GetDWARFDeclContext(this, dwarf_cu, die_decl_ctx); + + //type_sp = FindDefinitionTypeForDIE (dwarf_cu, die, type_name_const_str); + type_sp = FindDefinitionTypeForDWARFDeclContext (die_decl_ctx); + + if (!type_sp && GetDebugMapSymfile ()) + { + // We weren't able to find a full declaration in + // this DWARF, see if we have a declaration anywhere + // else... + type_sp = m_debug_map_symfile->FindDefinitionTypeForDWARFDeclContext (die_decl_ctx); + } + + if (type_sp) + { + if (log) + { + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF(%p) - 0x%8.8x: %s type \"%s\" is a forward declaration, complete type is 0x%8.8" PRIx64, + this, + die->GetOffset(), + DW_TAG_value_to_name(tag), + type_name_cstr, + type_sp->GetID()); + } + + // We found a real definition for this type elsewhere + // so lets use it and cache the fact that we found + // a complete type for this die + m_die_to_type[die] = type_sp.get(); + return type_sp; + } + } + assert (tag_decl_kind != -1); + bool clang_type_was_created = false; + clang_type.SetClangType(ast.getASTContext(), m_forward_decl_die_to_clang_type.lookup (die)); + if (!clang_type) + { + const DWARFDebugInfoEntry *decl_ctx_die; + + clang::DeclContext *decl_ctx = GetClangDeclContextContainingDIE (dwarf_cu, die, &decl_ctx_die); + if (accessibility == eAccessNone && decl_ctx) + { + // Check the decl context that contains this class/struct/union. + // If it is a class we must give it an accessability. + const clang::Decl::Kind containing_decl_kind = decl_ctx->getDeclKind(); + if (DeclKindIsCXXClass (containing_decl_kind)) + accessibility = default_accessibility; + } + + ClangASTMetadata metadata; + metadata.SetUserID(MakeUserID(die->GetOffset())); + metadata.SetIsDynamicCXXType(ClassOrStructIsVirtual (dwarf_cu, die)); + + if (type_name_cstr && strchr (type_name_cstr, '<')) + { + ClangASTContext::TemplateParameterInfos template_param_infos; + if (ParseTemplateParameterInfos (dwarf_cu, die, template_param_infos)) + { + clang::ClassTemplateDecl *class_template_decl = ParseClassTemplateDecl (decl_ctx, + accessibility, + type_name_cstr, + tag_decl_kind, + template_param_infos); + + clang::ClassTemplateSpecializationDecl *class_specialization_decl = ast.CreateClassTemplateSpecializationDecl (decl_ctx, + class_template_decl, + tag_decl_kind, + template_param_infos); + clang_type = ast.CreateClassTemplateSpecializationType (class_specialization_decl); + clang_type_was_created = true; + + GetClangASTContext().SetMetadata (class_template_decl, metadata); + GetClangASTContext().SetMetadata (class_specialization_decl, metadata); + } + } + + if (!clang_type_was_created) + { + clang_type_was_created = true; + clang_type = ast.CreateRecordType (decl_ctx, + accessibility, + type_name_cstr, + tag_decl_kind, + class_language, + &metadata); + } + } + + // Store a forward declaration to this class type in case any + // parameters in any class methods need it for the clang + // types for function prototypes. + LinkDeclContextToDIE(clang_type.GetDeclContextForType(), die); + type_sp.reset (new Type (MakeUserID(die->GetOffset()), + this, + type_name_const_str, + byte_size, + NULL, + LLDB_INVALID_UID, + Type::eEncodingIsUID, + &decl, + clang_type, + Type::eResolveStateForward)); + + type_sp->SetIsCompleteObjCClass(is_complete_objc_class); + + + // Add our type to the unique type map so we don't + // end up creating many copies of the same type over + // and over in the ASTContext for our module + unique_ast_entry.m_type_sp = type_sp; + unique_ast_entry.m_symfile = this; + unique_ast_entry.m_cu = dwarf_cu; + unique_ast_entry.m_die = die; + unique_ast_entry.m_declaration = decl; + unique_ast_entry.m_byte_size = byte_size; + GetUniqueDWARFASTTypeMap().Insert (type_name_const_str, + unique_ast_entry); + + if (is_forward_declaration && die->HasChildren()) + { + // Check to see if the DIE actually has a definition, some version of GCC will + // emit DIEs with DW_AT_declaration set to true, but yet still have subprogram, + // members, or inheritance, so we can't trust it + const DWARFDebugInfoEntry *child_die = die->GetFirstChild(); + while (child_die) + { + switch (child_die->Tag()) + { + case DW_TAG_inheritance: + case DW_TAG_subprogram: + case DW_TAG_member: + case DW_TAG_APPLE_property: + child_die = NULL; + is_forward_declaration = false; + break; + default: + child_die = child_die->GetSibling(); + break; + } + } + } + + if (!is_forward_declaration) + { + // Always start the definition for a class type so that + // if the class has child classes or types that require + // the class to be created for use as their decl contexts + // the class will be ready to accept these child definitions. + if (die->HasChildren() == false) + { + // No children for this struct/union/class, lets finish it + clang_type.StartTagDeclarationDefinition (); + clang_type.CompleteTagDeclarationDefinition (); + + if (tag == DW_TAG_structure_type) // this only applies in C + { + clang::RecordDecl *record_decl = clang_type.GetAsRecordDecl(); + + if (record_decl) + { + LayoutInfo layout_info; + + layout_info.alignment = 0; + layout_info.bit_size = 0; + + m_record_decl_to_layout_map.insert(std::make_pair(record_decl, layout_info)); + } + } + } + else if (clang_type_was_created) + { + // Start the definition if the class is not objective C since + // the underlying decls respond to isCompleteDefinition(). Objective + // C decls dont' respond to isCompleteDefinition() so we can't + // start the declaration definition right away. For C++ classs/union/structs + // we want to start the definition in case the class is needed as the + // declaration context for a contained class or type without the need + // to complete that type.. + + if (class_language != eLanguageTypeObjC && + class_language != eLanguageTypeObjC_plus_plus) + clang_type.StartTagDeclarationDefinition (); + + // Leave this as a forward declaration until we need + // to know the details of the type. lldb_private::Type + // will automatically call the SymbolFile virtual function + // "SymbolFileDWARF::ResolveClangOpaqueTypeDefinition(Type *)" + // When the definition needs to be defined. + m_forward_decl_die_to_clang_type[die] = clang_type.GetOpaqueQualType(); + m_forward_decl_clang_type_to_die[clang_type.RemoveFastQualifiers().GetOpaqueQualType()] = die; + clang_type.SetHasExternalStorage (true); + } + } + + } + break; + + case DW_TAG_enumeration_type: + { + // Set a bit that lets us know that we are currently parsing this + m_die_to_type[die] = DIE_IS_BEING_PARSED; + + lldb::user_id_t encoding_uid = DW_INVALID_OFFSET; + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, NULL, attributes); + if (num_attributes > 0) + { + uint32_t i; + + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_const_str.SetCString(type_name_cstr); + break; + case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; + case DW_AT_accessibility: break; //accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; + case DW_AT_declaration: break; //is_forward_declaration = form_value.Boolean(); break; + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_bit_stride: + case DW_AT_byte_stride: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_start_scope: + case DW_AT_visibility: + case DW_AT_specification: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + DEBUG_PRINTF ("0x%8.8" PRIx64 ": %s (\"%s\")\n", MakeUserID(die->GetOffset()), DW_TAG_value_to_name(tag), type_name_cstr); + + ClangASTType enumerator_clang_type; + clang_type.SetClangType (ast.getASTContext(), m_forward_decl_die_to_clang_type.lookup (die)); + if (!clang_type) + { + if (encoding_uid != DW_INVALID_OFFSET) + { + Type *enumerator_type = ResolveTypeUID(encoding_uid); + if (enumerator_type) + enumerator_clang_type = enumerator_type->GetClangFullType(); + } + + if (!enumerator_clang_type) + enumerator_clang_type = ast.GetBuiltinTypeForDWARFEncodingAndBitSize (NULL, + DW_ATE_signed, + byte_size * 8); + + clang_type = ast.CreateEnumerationType (type_name_cstr, + GetClangDeclContextContainingDIE (dwarf_cu, die, NULL), + decl, + enumerator_clang_type); + } + else + { + enumerator_clang_type = clang_type.GetEnumerationIntegerType (); + } + + LinkDeclContextToDIE(clang_type.GetDeclContextForType(), die); + + type_sp.reset( new Type (MakeUserID(die->GetOffset()), + this, + type_name_const_str, + byte_size, + NULL, + encoding_uid, + Type::eEncodingIsUID, + &decl, + clang_type, + Type::eResolveStateForward)); + + clang_type.StartTagDeclarationDefinition (); + if (die->HasChildren()) + { + SymbolContext cu_sc(GetCompUnitForDWARFCompUnit(dwarf_cu)); + bool is_signed = false; + enumerator_clang_type.IsIntegerType(is_signed); + ParseChildEnumerators(cu_sc, clang_type, is_signed, type_sp->GetByteSize(), dwarf_cu, die); + } + clang_type.CompleteTagDeclarationDefinition (); + } + } + break; + + case DW_TAG_inlined_subroutine: + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + { + // Set a bit that lets us know that we are currently parsing this + m_die_to_type[die] = DIE_IS_BEING_PARSED; + + //const char *mangled = NULL; + dw_offset_t type_die_offset = DW_INVALID_OFFSET; + bool is_variadic = false; + bool is_inline = false; + bool is_static = false; + bool is_virtual = false; + bool is_explicit = false; + bool is_artificial = false; + dw_offset_t specification_die_offset = DW_INVALID_OFFSET; + dw_offset_t abstract_origin_die_offset = DW_INVALID_OFFSET; + dw_offset_t object_pointer_die_offset = DW_INVALID_OFFSET; + + unsigned type_quals = 0; + clang::StorageClass storage = clang::SC_None;//, Extern, Static, PrivateExtern + + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, NULL, attributes); + if (num_attributes > 0) + { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_const_str.SetCString(type_name_cstr); + break; + + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: break; // mangled = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; + case DW_AT_declaration: break; // is_forward_declaration = form_value.Boolean(); break; + case DW_AT_inline: is_inline = form_value.Boolean(); break; + case DW_AT_virtuality: is_virtual = form_value.Boolean(); break; + case DW_AT_explicit: is_explicit = form_value.Boolean(); break; + case DW_AT_artificial: is_artificial = form_value.Boolean(); break; + + + case DW_AT_external: + if (form_value.Unsigned()) + { + if (storage == clang::SC_None) + storage = clang::SC_Extern; + else + storage = clang::SC_PrivateExtern; + } + break; + + case DW_AT_specification: + specification_die_offset = form_value.Reference(dwarf_cu); + break; + + case DW_AT_abstract_origin: + abstract_origin_die_offset = form_value.Reference(dwarf_cu); + break; + + case DW_AT_object_pointer: + object_pointer_die_offset = form_value.Reference(dwarf_cu); + break; + + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_address_class: + case DW_AT_calling_convention: + case DW_AT_data_location: + case DW_AT_elemental: + case DW_AT_entry_pc: + case DW_AT_frame_base: + case DW_AT_high_pc: + case DW_AT_low_pc: + case DW_AT_prototyped: + case DW_AT_pure: + case DW_AT_ranges: + case DW_AT_recursive: + case DW_AT_return_addr: + case DW_AT_segment: + case DW_AT_start_scope: + case DW_AT_static_link: + case DW_AT_trampoline: + case DW_AT_visibility: + case DW_AT_vtable_elem_location: + case DW_AT_description: + case DW_AT_sibling: + break; + } + } + } + } + + std::string object_pointer_name; + if (object_pointer_die_offset != DW_INVALID_OFFSET) + { + // Get the name from the object pointer die + StreamString s; + if (DWARFDebugInfoEntry::GetName (this, dwarf_cu, object_pointer_die_offset, s)) + { + object_pointer_name.assign(s.GetData()); + } + } + + DEBUG_PRINTF ("0x%8.8" PRIx64 ": %s (\"%s\")\n", MakeUserID(die->GetOffset()), DW_TAG_value_to_name(tag), type_name_cstr); + + ClangASTType return_clang_type; + Type *func_type = NULL; + + if (type_die_offset != DW_INVALID_OFFSET) + func_type = ResolveTypeUID(type_die_offset); + + if (func_type) + return_clang_type = func_type->GetClangForwardType(); + else + return_clang_type = ast.GetBasicType(eBasicTypeVoid); + + + std::vector<ClangASTType> function_param_types; + std::vector<clang::ParmVarDecl*> function_param_decls; + + // Parse the function children for the parameters + + const DWARFDebugInfoEntry *decl_ctx_die = NULL; + clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE (dwarf_cu, die, &decl_ctx_die); + const clang::Decl::Kind containing_decl_kind = containing_decl_ctx->getDeclKind(); + + const bool is_cxx_method = DeclKindIsCXXClass (containing_decl_kind); + // Start off static. This will be set to false in ParseChildParameters(...) + // if we find a "this" paramters as the first parameter + if (is_cxx_method) + is_static = true; + ClangASTContext::TemplateParameterInfos template_param_infos; + + if (die->HasChildren()) + { + bool skip_artificial = true; + ParseChildParameters (sc, + containing_decl_ctx, + dwarf_cu, + die, + skip_artificial, + is_static, + type_list, + function_param_types, + function_param_decls, + type_quals, + template_param_infos); + } + + // clang_type will get the function prototype clang type after this call + clang_type = ast.CreateFunctionType (return_clang_type, + function_param_types.data(), + function_param_types.size(), + is_variadic, + type_quals); + + bool ignore_containing_context = false; + + if (type_name_cstr) + { + bool type_handled = false; + if (tag == DW_TAG_subprogram) + { + ObjCLanguageRuntime::MethodName objc_method (type_name_cstr, true); + if (objc_method.IsValid(true)) + { + SymbolContext empty_sc; + ClangASTType class_opaque_type; + ConstString class_name(objc_method.GetClassName()); + if (class_name) + { + TypeList types; + TypeSP complete_objc_class_type_sp (FindCompleteObjCDefinitionTypeForDIE (NULL, class_name, false)); + + if (complete_objc_class_type_sp) + { + ClangASTType type_clang_forward_type = complete_objc_class_type_sp->GetClangForwardType(); + if (type_clang_forward_type.IsObjCObjectOrInterfaceType ()) + class_opaque_type = type_clang_forward_type; + } + } + + if (class_opaque_type) + { + // If accessibility isn't set to anything valid, assume public for + // now... + if (accessibility == eAccessNone) + accessibility = eAccessPublic; + + clang::ObjCMethodDecl *objc_method_decl = class_opaque_type.AddMethodToObjCObjectType (type_name_cstr, + clang_type, + accessibility, + is_artificial); + type_handled = objc_method_decl != NULL; + if (type_handled) + { + LinkDeclContextToDIE(ClangASTContext::GetAsDeclContext(objc_method_decl), die); + GetClangASTContext().SetMetadataAsUserID (objc_method_decl, MakeUserID(die->GetOffset())); + } + else + { + GetObjectFile()->GetModule()->ReportError ("{0x%8.8x}: invalid Objective-C method 0x%4.4x (%s), please file a bug and attach the file at the start of this error message", + die->GetOffset(), + tag, + DW_TAG_value_to_name(tag)); + } + } + } + else if (is_cxx_method) + { + // Look at the parent of this DIE and see if is is + // a class or struct and see if this is actually a + // C++ method + Type *class_type = ResolveType (dwarf_cu, decl_ctx_die); + if (class_type) + { + if (class_type->GetID() != MakeUserID(decl_ctx_die->GetOffset())) + { + // We uniqued the parent class of this function to another class + // so we now need to associate all dies under "decl_ctx_die" to + // DIEs in the DIE for "class_type"... + SymbolFileDWARF *class_symfile = NULL; + DWARFCompileUnitSP class_type_cu_sp; + const DWARFDebugInfoEntry *class_type_die = NULL; + + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile(); + if (debug_map_symfile) + { + class_symfile = debug_map_symfile->GetSymbolFileByOSOIndex(SymbolFileDWARFDebugMap::GetOSOIndexFromUserID(class_type->GetID())); + class_type_die = class_symfile->DebugInfo()->GetDIEPtr(class_type->GetID(), &class_type_cu_sp); + } + else + { + class_symfile = this; + class_type_die = DebugInfo()->GetDIEPtr(class_type->GetID(), &class_type_cu_sp); + } + if (class_type_die) + { + llvm::SmallVector<const DWARFDebugInfoEntry *, 0> failures; + + CopyUniqueClassMethodTypes (class_symfile, + class_type, + class_type_cu_sp.get(), + class_type_die, + dwarf_cu, + decl_ctx_die, + failures); + + // FIXME do something with these failures that's smarter than + // just dropping them on the ground. Unfortunately classes don't + // like having stuff added to them after their definitions are + // complete... + + type_ptr = m_die_to_type[die]; + if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) + { + type_sp = type_ptr->shared_from_this(); + break; + } + } + } + + if (specification_die_offset != DW_INVALID_OFFSET) + { + // We have a specification which we are going to base our function + // prototype off of, so we need this type to be completed so that the + // m_die_to_decl_ctx for the method in the specification has a valid + // clang decl context. + class_type->GetClangForwardType(); + // If we have a specification, then the function type should have been + // made with the specification and not with this die. + DWARFCompileUnitSP spec_cu_sp; + const DWARFDebugInfoEntry* spec_die = DebugInfo()->GetDIEPtr(specification_die_offset, &spec_cu_sp); + clang::DeclContext *spec_clang_decl_ctx = GetClangDeclContextForDIE (sc, dwarf_cu, spec_die); + if (spec_clang_decl_ctx) + { + LinkDeclContextToDIE(spec_clang_decl_ctx, die); + } + else + { + GetObjectFile()->GetModule()->ReportWarning ("0x%8.8" PRIx64 ": DW_AT_specification(0x%8.8x) has no decl\n", + MakeUserID(die->GetOffset()), + specification_die_offset); + } + type_handled = true; + } + else if (abstract_origin_die_offset != DW_INVALID_OFFSET) + { + // We have a specification which we are going to base our function + // prototype off of, so we need this type to be completed so that the + // m_die_to_decl_ctx for the method in the abstract origin has a valid + // clang decl context. + class_type->GetClangForwardType(); + + DWARFCompileUnitSP abs_cu_sp; + const DWARFDebugInfoEntry* abs_die = DebugInfo()->GetDIEPtr(abstract_origin_die_offset, &abs_cu_sp); + clang::DeclContext *abs_clang_decl_ctx = GetClangDeclContextForDIE (sc, dwarf_cu, abs_die); + if (abs_clang_decl_ctx) + { + LinkDeclContextToDIE (abs_clang_decl_ctx, die); + } + else + { + GetObjectFile()->GetModule()->ReportWarning ("0x%8.8" PRIx64 ": DW_AT_abstract_origin(0x%8.8x) has no decl\n", + MakeUserID(die->GetOffset()), + abstract_origin_die_offset); + } + type_handled = true; + } + else + { + ClangASTType class_opaque_type = class_type->GetClangForwardType(); + if (class_opaque_type.IsCXXClassType ()) + { + if (class_opaque_type.IsBeingDefined ()) + { + // Neither GCC 4.2 nor clang++ currently set a valid accessibility + // in the DWARF for C++ methods... Default to public for now... + if (accessibility == eAccessNone) + accessibility = eAccessPublic; + + if (!is_static && !die->HasChildren()) + { + // We have a C++ member function with no children (this pointer!) + // and clang will get mad if we try and make a function that isn't + // well formed in the DWARF, so we will just skip it... + type_handled = true; + } + else + { + clang::CXXMethodDecl *cxx_method_decl; + // REMOVE THE CRASH DESCRIPTION BELOW + Host::SetCrashDescriptionWithFormat ("SymbolFileDWARF::ParseType() is adding a method %s to class %s in DIE 0x%8.8" PRIx64 " from %s", + type_name_cstr, + class_type->GetName().GetCString(), + MakeUserID(die->GetOffset()), + m_obj_file->GetFileSpec().GetPath().c_str()); + + const bool is_attr_used = false; + + cxx_method_decl = class_opaque_type.AddMethodToCXXRecordType (type_name_cstr, + clang_type, + accessibility, + is_virtual, + is_static, + is_inline, + is_explicit, + is_attr_used, + is_artificial); + + type_handled = cxx_method_decl != NULL; + + if (type_handled) + { + LinkDeclContextToDIE(ClangASTContext::GetAsDeclContext(cxx_method_decl), die); + + Host::SetCrashDescription (NULL); + + + ClangASTMetadata metadata; + metadata.SetUserID(MakeUserID(die->GetOffset())); + + if (!object_pointer_name.empty()) + { + metadata.SetObjectPtrName(object_pointer_name.c_str()); + if (log) + log->Printf ("Setting object pointer name: %s on method object %p.\n", + object_pointer_name.c_str(), + cxx_method_decl); + } + GetClangASTContext().SetMetadata (cxx_method_decl, metadata); + } + else + { + ignore_containing_context = true; + } + } + } + else + { + // We were asked to parse the type for a method in a class, yet the + // class hasn't been asked to complete itself through the + // clang::ExternalASTSource protocol, so we need to just have the + // class complete itself and do things the right way, then our + // DIE should then have an entry in the m_die_to_type map. First + // we need to modify the m_die_to_type so it doesn't think we are + // trying to parse this DIE anymore... + m_die_to_type[die] = NULL; + + // Now we get the full type to force our class type to complete itself + // using the clang::ExternalASTSource protocol which will parse all + // base classes and all methods (including the method for this DIE). + class_type->GetClangFullType(); + + // The type for this DIE should have been filled in the function call above + type_ptr = m_die_to_type[die]; + if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) + { + type_sp = type_ptr->shared_from_this(); + break; + } + + // FIXME This is fixing some even uglier behavior but we really need to + // uniq the methods of each class as well as the class itself. + // <rdar://problem/11240464> + type_handled = true; + } + } + } + } + } + } + + if (!type_handled) + { + // We just have a function that isn't part of a class + clang::FunctionDecl *function_decl = ast.CreateFunctionDeclaration (ignore_containing_context ? GetClangASTContext().GetTranslationUnitDecl() : containing_decl_ctx, + type_name_cstr, + clang_type, + storage, + is_inline); + +// if (template_param_infos.GetSize() > 0) +// { +// clang::FunctionTemplateDecl *func_template_decl = ast.CreateFunctionTemplateDecl (containing_decl_ctx, +// function_decl, +// type_name_cstr, +// template_param_infos); +// +// ast.CreateFunctionTemplateSpecializationInfo (function_decl, +// func_template_decl, +// template_param_infos); +// } + // Add the decl to our DIE to decl context map + assert (function_decl); + LinkDeclContextToDIE(function_decl, die); + if (!function_param_decls.empty()) + ast.SetFunctionParameters (function_decl, + &function_param_decls.front(), + function_param_decls.size()); + + ClangASTMetadata metadata; + metadata.SetUserID(MakeUserID(die->GetOffset())); + + if (!object_pointer_name.empty()) + { + metadata.SetObjectPtrName(object_pointer_name.c_str()); + if (log) + log->Printf ("Setting object pointer name: %s on function object %p.", + object_pointer_name.c_str(), + function_decl); + } + GetClangASTContext().SetMetadata (function_decl, metadata); + } + } + type_sp.reset( new Type (MakeUserID(die->GetOffset()), + this, + type_name_const_str, + 0, + NULL, + LLDB_INVALID_UID, + Type::eEncodingIsUID, + &decl, + clang_type, + Type::eResolveStateFull)); + assert(type_sp.get()); + } + break; + + case DW_TAG_array_type: + { + // Set a bit that lets us know that we are currently parsing this + m_die_to_type[die] = DIE_IS_BEING_PARSED; + + lldb::user_id_t type_die_offset = DW_INVALID_OFFSET; + int64_t first_index = 0; + uint32_t byte_stride = 0; + uint32_t bit_stride = 0; + bool is_vector = false; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, NULL, attributes); + + if (num_attributes > 0) + { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: + type_name_cstr = form_value.AsCString(&get_debug_str_data()); + type_name_const_str.SetCString(type_name_cstr); + break; + + case DW_AT_type: type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_byte_size: break; // byte_size = form_value.Unsigned(); break; + case DW_AT_byte_stride: byte_stride = form_value.Unsigned(); break; + case DW_AT_bit_stride: bit_stride = form_value.Unsigned(); break; + case DW_AT_GNU_vector: is_vector = form_value.Boolean(); break; + case DW_AT_accessibility: break; // accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; + case DW_AT_declaration: break; // is_forward_declaration = form_value.Boolean(); break; + case DW_AT_allocated: + case DW_AT_associated: + case DW_AT_data_location: + case DW_AT_description: + case DW_AT_ordering: + case DW_AT_start_scope: + case DW_AT_visibility: + case DW_AT_specification: + case DW_AT_abstract_origin: + case DW_AT_sibling: + break; + } + } + } + + DEBUG_PRINTF ("0x%8.8" PRIx64 ": %s (\"%s\")\n", MakeUserID(die->GetOffset()), DW_TAG_value_to_name(tag), type_name_cstr); + + Type *element_type = ResolveTypeUID(type_die_offset); + + if (element_type) + { + std::vector<uint64_t> element_orders; + ParseChildArrayInfo(sc, dwarf_cu, die, first_index, element_orders, byte_stride, bit_stride); + if (byte_stride == 0 && bit_stride == 0) + byte_stride = element_type->GetByteSize(); + ClangASTType array_element_type = element_type->GetClangForwardType(); + uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride; + uint64_t num_elements = 0; + std::vector<uint64_t>::const_reverse_iterator pos; + std::vector<uint64_t>::const_reverse_iterator end = element_orders.rend(); + for (pos = element_orders.rbegin(); pos != end; ++pos) + { + num_elements = *pos; + clang_type = ast.CreateArrayType (array_element_type, + num_elements, + is_vector); + array_element_type = clang_type; + array_element_bit_stride = num_elements ? array_element_bit_stride * num_elements : array_element_bit_stride; + } + ConstString empty_name; + type_sp.reset( new Type (MakeUserID(die->GetOffset()), + this, + empty_name, + array_element_bit_stride / 8, + NULL, + type_die_offset, + Type::eEncodingIsUID, + &decl, + clang_type, + Type::eResolveStateFull)); + type_sp->SetEncodingType (element_type); + } + } + } + break; + + case DW_TAG_ptr_to_member_type: + { + dw_offset_t type_die_offset = DW_INVALID_OFFSET; + dw_offset_t containing_type_die_offset = DW_INVALID_OFFSET; + + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, NULL, attributes); + + if (num_attributes > 0) { + uint32_t i; + for (i=0; i<num_attributes; ++i) + { + attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_type: + type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_containing_type: + containing_type_die_offset = form_value.Reference(dwarf_cu); break; + } + } + } + + Type *pointee_type = ResolveTypeUID(type_die_offset); + Type *class_type = ResolveTypeUID(containing_type_die_offset); + + ClangASTType pointee_clang_type = pointee_type->GetClangForwardType(); + ClangASTType class_clang_type = class_type->GetClangLayoutType(); + + clang_type = pointee_clang_type.CreateMemberPointerType(class_clang_type); + + byte_size = clang_type.GetByteSize(); + + type_sp.reset( new Type (MakeUserID(die->GetOffset()), + this, + type_name_const_str, + byte_size, + NULL, + LLDB_INVALID_UID, + Type::eEncodingIsUID, + NULL, + clang_type, + Type::eResolveStateForward)); + } + + break; + } + default: + GetObjectFile()->GetModule()->ReportError ("{0x%8.8x}: unhandled type tag 0x%4.4x (%s), please file a bug and attach the file at the start of this error message", + die->GetOffset(), + tag, + DW_TAG_value_to_name(tag)); + break; + } + + if (type_sp.get()) + { + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(die); + dw_tag_t sc_parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + + SymbolContextScope * symbol_context_scope = NULL; + if (sc_parent_tag == DW_TAG_compile_unit) + { + symbol_context_scope = sc.comp_unit; + } + else if (sc.function != NULL) + { + symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(MakeUserID(sc_parent_die->GetOffset())); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; + } + + if (symbol_context_scope != NULL) + { + type_sp->SetSymbolContextScope(symbol_context_scope); + } + + // We are ready to put this type into the uniqued list up at the module level + type_list->Insert (type_sp); + + m_die_to_type[die] = type_sp.get(); + } + } + else if (type_ptr != DIE_IS_BEING_PARSED) + { + type_sp = type_ptr->shared_from_this(); + } + } + return type_sp; +} + +size_t +SymbolFileDWARF::ParseTypes +( + const SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + bool parse_siblings, + bool parse_children +) +{ + size_t types_added = 0; + while (die != NULL) + { + bool type_is_new = false; + if (ParseType(sc, dwarf_cu, die, &type_is_new).get()) + { + if (type_is_new) + ++types_added; + } + + if (parse_children && die->HasChildren()) + { + if (die->Tag() == DW_TAG_subprogram) + { + SymbolContext child_sc(sc); + child_sc.function = sc.comp_unit->FindFunctionByUID(MakeUserID(die->GetOffset())).get(); + types_added += ParseTypes(child_sc, dwarf_cu, die->GetFirstChild(), true, true); + } + else + types_added += ParseTypes(sc, dwarf_cu, die->GetFirstChild(), true, true); + } + + if (parse_siblings) + die = die->GetSibling(); + else + die = NULL; + } + return types_added; +} + + +size_t +SymbolFileDWARF::ParseFunctionBlocks (const SymbolContext &sc) +{ + assert(sc.comp_unit && sc.function); + size_t functions_added = 0; + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); + if (dwarf_cu) + { + dw_offset_t function_die_offset = sc.function->GetID(); + const DWARFDebugInfoEntry *function_die = dwarf_cu->GetDIEPtr(function_die_offset); + if (function_die) + { + ParseFunctionBlocks(sc, &sc.function->GetBlock (false), dwarf_cu, function_die, LLDB_INVALID_ADDRESS, 0); + } + } + + return functions_added; +} + + +size_t +SymbolFileDWARF::ParseTypes (const SymbolContext &sc) +{ + // At least a compile unit must be valid + assert(sc.comp_unit); + size_t types_added = 0; + DWARFCompileUnit* dwarf_cu = GetDWARFCompileUnit(sc.comp_unit); + if (dwarf_cu) + { + if (sc.function) + { + dw_offset_t function_die_offset = sc.function->GetID(); + const DWARFDebugInfoEntry *func_die = dwarf_cu->GetDIEPtr(function_die_offset); + if (func_die && func_die->HasChildren()) + { + types_added = ParseTypes(sc, dwarf_cu, func_die->GetFirstChild(), true, true); + } + } + else + { + const DWARFDebugInfoEntry *dwarf_cu_die = dwarf_cu->DIE(); + if (dwarf_cu_die && dwarf_cu_die->HasChildren()) + { + types_added = ParseTypes(sc, dwarf_cu, dwarf_cu_die->GetFirstChild(), true, true); + } + } + } + + return types_added; +} + +size_t +SymbolFileDWARF::ParseVariablesForContext (const SymbolContext& sc) +{ + if (sc.comp_unit != NULL) + { + DWARFDebugInfo* info = DebugInfo(); + if (info == NULL) + return 0; + + if (sc.function) + { + DWARFCompileUnit* dwarf_cu = info->GetCompileUnitContainingDIE(sc.function->GetID()).get(); + + if (dwarf_cu == NULL) + return 0; + + const DWARFDebugInfoEntry *function_die = dwarf_cu->GetDIEPtr(sc.function->GetID()); + + dw_addr_t func_lo_pc = function_die->GetAttributeValueAsUnsigned (this, dwarf_cu, DW_AT_low_pc, LLDB_INVALID_ADDRESS); + if (func_lo_pc != LLDB_INVALID_ADDRESS) + { + const size_t num_variables = ParseVariables(sc, dwarf_cu, func_lo_pc, function_die->GetFirstChild(), true, true); + + // Let all blocks know they have parse all their variables + sc.function->GetBlock (false).SetDidParseVariables (true, true); + return num_variables; + } + } + else if (sc.comp_unit) + { + DWARFCompileUnit* dwarf_cu = info->GetCompileUnit(sc.comp_unit->GetID()).get(); + + if (dwarf_cu == NULL) + return 0; + + uint32_t vars_added = 0; + VariableListSP variables (sc.comp_unit->GetVariableList(false)); + + if (variables.get() == NULL) + { + variables.reset(new VariableList()); + sc.comp_unit->SetVariableList(variables); + + DWARFCompileUnit* match_dwarf_cu = NULL; + const DWARFDebugInfoEntry* die = NULL; + DIEArray die_offsets; + if (m_using_apple_tables) + { + if (m_apple_names_ap.get()) + { + DWARFMappedHash::DIEInfoArray hash_data_array; + if (m_apple_names_ap->AppendAllDIEsInRange (dwarf_cu->GetOffset(), + dwarf_cu->GetNextCompileUnitOffset(), + hash_data_array)) + { + DWARFMappedHash::ExtractDIEArray (hash_data_array, die_offsets); + } + } + } + else + { + // Index if we already haven't to make sure the compile units + // get indexed and make their global DIE index list + if (!m_indexed) + Index (); + + m_global_index.FindAllEntriesForCompileUnit (dwarf_cu->GetOffset(), + dwarf_cu->GetNextCompileUnitOffset(), + die_offsets); + } + + const size_t num_matches = die_offsets.size(); + if (num_matches) + { + DWARFDebugInfo* debug_info = DebugInfo(); + for (size_t i=0; i<num_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &match_dwarf_cu); + if (die) + { + VariableSP var_sp (ParseVariableDIE(sc, dwarf_cu, die, LLDB_INVALID_ADDRESS)); + if (var_sp) + { + variables->AddVariableIfUnique (var_sp); + ++vars_added; + } + } + else + { + if (m_using_apple_tables) + { + GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_names accelerator table had bad die 0x%8.8x)\n", die_offset); + } + } + + } + } + } + return vars_added; + } + } + return 0; +} + + +VariableSP +SymbolFileDWARF::ParseVariableDIE +( + const SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + const lldb::addr_t func_low_pc +) +{ + + VariableSP var_sp (m_die_to_variable_sp[die]); + if (var_sp) + return var_sp; // Already been parsed! + + const dw_tag_t tag = die->Tag(); + + if ((tag == DW_TAG_variable) || + (tag == DW_TAG_constant) || + (tag == DW_TAG_formal_parameter && sc.function)) + { + DWARFDebugInfoEntry::Attributes attributes; + const size_t num_attributes = die->GetAttributes(this, dwarf_cu, NULL, attributes); + if (num_attributes > 0) + { + const char *name = NULL; + const char *mangled = NULL; + Declaration decl; + uint32_t i; + lldb::user_id_t type_uid = LLDB_INVALID_UID; + DWARFExpression location; + bool is_external = false; + bool is_artificial = false; + bool location_is_const_value_data = false; + bool has_explicit_location = false; + //AccessType accessibility = eAccessNone; + + for (i=0; i<num_attributes; ++i) + { + dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) + { + switch (attr) + { + case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; + case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; + case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; + case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_linkage_name: + case DW_AT_MIPS_linkage_name: mangled = form_value.AsCString(&get_debug_str_data()); break; + case DW_AT_type: type_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_external: is_external = form_value.Boolean(); break; + case DW_AT_const_value: + // If we have already found a DW_AT_location attribute, ignore this attribute. + if (!has_explicit_location) + { + location_is_const_value_data = true; + // The constant value will be either a block, a data value or a string. + const DataExtractor& debug_info_data = get_debug_info_data(); + if (DWARFFormValue::IsBlockForm(form_value.Form())) + { + // Retrieve the value as a block expression. + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + location.CopyOpcodeData(debug_info_data, block_offset, block_length); + } + else if (DWARFFormValue::IsDataForm(form_value.Form())) + { + // Retrieve the value as a data expression. + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + uint32_t data_offset = attributes.DIEOffsetAtIndex(i); + uint32_t data_length = fixed_form_sizes[form_value.Form()]; + location.CopyOpcodeData(debug_info_data, data_offset, data_length); + } + else + { + // Retrieve the value as a string expression. + if (form_value.Form() == DW_FORM_strp) + { + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + uint32_t data_offset = attributes.DIEOffsetAtIndex(i); + uint32_t data_length = fixed_form_sizes[form_value.Form()]; + location.CopyOpcodeData(debug_info_data, data_offset, data_length); + } + else + { + const char *str = form_value.AsCString(&debug_info_data); + uint32_t string_offset = str - (const char *)debug_info_data.GetDataStart(); + uint32_t string_length = strlen(str) + 1; + location.CopyOpcodeData(debug_info_data, string_offset, string_length); + } + } + } + break; + case DW_AT_location: + { + location_is_const_value_data = false; + has_explicit_location = true; + if (form_value.BlockData()) + { + const DataExtractor& debug_info_data = get_debug_info_data(); + + uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); + uint32_t block_length = form_value.Unsigned(); + location.CopyOpcodeData(get_debug_info_data(), block_offset, block_length); + } + else + { + const DataExtractor& debug_loc_data = get_debug_loc_data(); + const dw_offset_t debug_loc_offset = form_value.Unsigned(); + + size_t loc_list_length = DWARFLocationList::Size(debug_loc_data, debug_loc_offset); + if (loc_list_length > 0) + { + location.CopyOpcodeData(debug_loc_data, debug_loc_offset, loc_list_length); + assert (func_low_pc != LLDB_INVALID_ADDRESS); + location.SetLocationListSlide (func_low_pc - dwarf_cu->GetBaseAddress()); + } + } + } + break; + + case DW_AT_artificial: is_artificial = form_value.Boolean(); break; + case DW_AT_accessibility: break; //accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_endianity: + case DW_AT_segment: + case DW_AT_start_scope: + case DW_AT_visibility: + default: + case DW_AT_abstract_origin: + case DW_AT_sibling: + case DW_AT_specification: + break; + } + } + } + + ValueType scope = eValueTypeInvalid; + + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(die); + dw_tag_t parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + SymbolContextScope * symbol_context_scope = NULL; + + // DWARF doesn't specify if a DW_TAG_variable is a local, global + // or static variable, so we have to do a little digging by + // looking at the location of a varaible to see if it contains + // a DW_OP_addr opcode _somewhere_ in the definition. I say + // somewhere because clang likes to combine small global variables + // into the same symbol and have locations like: + // DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus + // So if we don't have a DW_TAG_formal_parameter, we can look at + // the location to see if it contains a DW_OP_addr opcode, and + // then we can correctly classify our variables. + if (tag == DW_TAG_formal_parameter) + scope = eValueTypeVariableArgument; + else + { + bool op_error = false; + // Check if the location has a DW_OP_addr with any address value... + lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS; + if (!location_is_const_value_data) + { + location_DW_OP_addr = location.GetLocation_DW_OP_addr (0, op_error); + if (op_error) + { + StreamString strm; + location.DumpLocationForAddress (&strm, eDescriptionLevelFull, 0, 0, NULL); + GetObjectFile()->GetModule()->ReportError ("0x%8.8x: %s has an invalid location: %s", die->GetOffset(), DW_TAG_value_to_name(die->Tag()), strm.GetString().c_str()); + } + } + + if (location_DW_OP_addr != LLDB_INVALID_ADDRESS) + { + if (is_external) + scope = eValueTypeVariableGlobal; + else + scope = eValueTypeVariableStatic; + + + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile (); + + if (debug_map_symfile) + { + // When leaving the DWARF in the .o files on darwin, + // when we have a global variable that wasn't initialized, + // the .o file might not have allocated a virtual + // address for the global variable. In this case it will + // have created a symbol for the global variable + // that is undefined/data and external and the value will + // be the byte size of the variable. When we do the + // address map in SymbolFileDWARFDebugMap we rely on + // having an address, we need to do some magic here + // so we can get the correct address for our global + // variable. The address for all of these entries + // will be zero, and there will be an undefined symbol + // in this object file, and the executable will have + // a matching symbol with a good address. So here we + // dig up the correct address and replace it in the + // location for the variable, and set the variable's + // symbol context scope to be that of the main executable + // so the file address will resolve correctly. + bool linked_oso_file_addr = false; + if (is_external && location_DW_OP_addr == 0) + { + // we have a possible uninitialized extern global + ConstString const_name(mangled ? mangled : name); + ObjectFile *debug_map_objfile = debug_map_symfile->GetObjectFile(); + if (debug_map_objfile) + { + Symtab *debug_map_symtab = debug_map_objfile->GetSymtab(); + if (debug_map_symtab) + { + Symbol *exe_symbol = debug_map_symtab->FindFirstSymbolWithNameAndType (const_name, + eSymbolTypeData, + Symtab::eDebugYes, + Symtab::eVisibilityExtern); + if (exe_symbol) + { + if (exe_symbol->ValueIsAddress()) + { + const addr_t exe_file_addr = exe_symbol->GetAddress().GetFileAddress(); + if (exe_file_addr != LLDB_INVALID_ADDRESS) + { + if (location.Update_DW_OP_addr (exe_file_addr)) + { + linked_oso_file_addr = true; + symbol_context_scope = exe_symbol; + } + } + } + } + } + } + } + + if (!linked_oso_file_addr) + { + // The DW_OP_addr is not zero, but it contains a .o file address which + // needs to be linked up correctly. + const lldb::addr_t exe_file_addr = debug_map_symfile->LinkOSOFileAddress(this, location_DW_OP_addr); + if (exe_file_addr != LLDB_INVALID_ADDRESS) + { + // Update the file address for this variable + location.Update_DW_OP_addr (exe_file_addr); + } + else + { + // Variable didn't make it into the final executable + return var_sp; + } + } + } + } + else + { + scope = eValueTypeVariableLocal; + } + } + + if (symbol_context_scope == NULL) + { + switch (parent_tag) + { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + if (sc.function) + { + symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(MakeUserID(sc_parent_die->GetOffset())); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; + } + break; + + default: + symbol_context_scope = sc.comp_unit; + break; + } + } + + if (symbol_context_scope) + { + var_sp.reset (new Variable (MakeUserID(die->GetOffset()), + name, + mangled, + SymbolFileTypeSP (new SymbolFileType(*this, type_uid)), + scope, + symbol_context_scope, + &decl, + location, + is_external, + is_artificial)); + + var_sp->SetLocationIsConstantValueData (location_is_const_value_data); + } + else + { + // Not ready to parse this variable yet. It might be a global + // or static variable that is in a function scope and the function + // in the symbol context wasn't filled in yet + return var_sp; + } + } + // Cache var_sp even if NULL (the variable was just a specification or + // was missing vital information to be able to be displayed in the debugger + // (missing location due to optimization, etc)) so we don't re-parse + // this DIE over and over later... + m_die_to_variable_sp[die] = var_sp; + } + return var_sp; +} + + +const DWARFDebugInfoEntry * +SymbolFileDWARF::FindBlockContainingSpecification (dw_offset_t func_die_offset, + dw_offset_t spec_block_die_offset, + DWARFCompileUnit **result_die_cu_handle) +{ + // Give the concrete function die specified by "func_die_offset", find the + // concrete block whose DW_AT_specification or DW_AT_abstract_origin points + // to "spec_block_die_offset" + DWARFDebugInfo* info = DebugInfo(); + + const DWARFDebugInfoEntry *die = info->GetDIEPtrWithCompileUnitHint(func_die_offset, result_die_cu_handle); + if (die) + { + assert (*result_die_cu_handle); + return FindBlockContainingSpecification (*result_die_cu_handle, die, spec_block_die_offset, result_die_cu_handle); + } + return NULL; +} + + +const DWARFDebugInfoEntry * +SymbolFileDWARF::FindBlockContainingSpecification(DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + dw_offset_t spec_block_die_offset, + DWARFCompileUnit **result_die_cu_handle) +{ + if (die) + { + switch (die->Tag()) + { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + { + if (die->GetAttributeValueAsReference (this, dwarf_cu, DW_AT_specification, DW_INVALID_OFFSET) == spec_block_die_offset) + { + *result_die_cu_handle = dwarf_cu; + return die; + } + + if (die->GetAttributeValueAsReference (this, dwarf_cu, DW_AT_abstract_origin, DW_INVALID_OFFSET) == spec_block_die_offset) + { + *result_die_cu_handle = dwarf_cu; + return die; + } + } + break; + } + + // Give the concrete function die specified by "func_die_offset", find the + // concrete block whose DW_AT_specification or DW_AT_abstract_origin points + // to "spec_block_die_offset" + for (const DWARFDebugInfoEntry *child_die = die->GetFirstChild(); child_die != NULL; child_die = child_die->GetSibling()) + { + const DWARFDebugInfoEntry *result_die = FindBlockContainingSpecification (dwarf_cu, + child_die, + spec_block_die_offset, + result_die_cu_handle); + if (result_die) + return result_die; + } + } + + *result_die_cu_handle = NULL; + return NULL; +} + +size_t +SymbolFileDWARF::ParseVariables +( + const SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const lldb::addr_t func_low_pc, + const DWARFDebugInfoEntry *orig_die, + bool parse_siblings, + bool parse_children, + VariableList* cc_variable_list +) +{ + if (orig_die == NULL) + return 0; + + VariableListSP variable_list_sp; + + size_t vars_added = 0; + const DWARFDebugInfoEntry *die = orig_die; + while (die != NULL) + { + dw_tag_t tag = die->Tag(); + + // Check to see if we have already parsed this variable or constant? + if (m_die_to_variable_sp[die]) + { + if (cc_variable_list) + cc_variable_list->AddVariableIfUnique (m_die_to_variable_sp[die]); + } + else + { + // We haven't already parsed it, lets do that now. + if ((tag == DW_TAG_variable) || + (tag == DW_TAG_constant) || + (tag == DW_TAG_formal_parameter && sc.function)) + { + if (variable_list_sp.get() == NULL) + { + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(orig_die); + dw_tag_t parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + switch (parent_tag) + { + case DW_TAG_compile_unit: + if (sc.comp_unit != NULL) + { + variable_list_sp = sc.comp_unit->GetVariableList(false); + if (variable_list_sp.get() == NULL) + { + variable_list_sp.reset(new VariableList()); + sc.comp_unit->SetVariableList(variable_list_sp); + } + } + else + { + GetObjectFile()->GetModule()->ReportError ("parent 0x%8.8" PRIx64 " %s with no valid compile unit in symbol context for 0x%8.8" PRIx64 " %s.\n", + MakeUserID(sc_parent_die->GetOffset()), + DW_TAG_value_to_name (parent_tag), + MakeUserID(orig_die->GetOffset()), + DW_TAG_value_to_name (orig_die->Tag())); + } + break; + + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + if (sc.function != NULL) + { + // Check to see if we already have parsed the variables for the given scope + + Block *block = sc.function->GetBlock(true).FindBlockByID(MakeUserID(sc_parent_die->GetOffset())); + if (block == NULL) + { + // This must be a specification or abstract origin with + // a concrete block couterpart in the current function. We need + // to find the concrete block so we can correctly add the + // variable to it + DWARFCompileUnit *concrete_block_die_cu = dwarf_cu; + const DWARFDebugInfoEntry *concrete_block_die = FindBlockContainingSpecification (sc.function->GetID(), + sc_parent_die->GetOffset(), + &concrete_block_die_cu); + if (concrete_block_die) + block = sc.function->GetBlock(true).FindBlockByID(MakeUserID(concrete_block_die->GetOffset())); + } + + if (block != NULL) + { + const bool can_create = false; + variable_list_sp = block->GetBlockVariableList (can_create); + if (variable_list_sp.get() == NULL) + { + variable_list_sp.reset(new VariableList()); + block->SetVariableList(variable_list_sp); + } + } + } + break; + + default: + GetObjectFile()->GetModule()->ReportError ("didn't find appropriate parent DIE for variable list for 0x%8.8" PRIx64 " %s.\n", + MakeUserID(orig_die->GetOffset()), + DW_TAG_value_to_name (orig_die->Tag())); + break; + } + } + + if (variable_list_sp) + { + VariableSP var_sp (ParseVariableDIE(sc, dwarf_cu, die, func_low_pc)); + if (var_sp) + { + variable_list_sp->AddVariableIfUnique (var_sp); + if (cc_variable_list) + cc_variable_list->AddVariableIfUnique (var_sp); + ++vars_added; + } + } + } + } + + bool skip_children = (sc.function == NULL && tag == DW_TAG_subprogram); + + if (!skip_children && parse_children && die->HasChildren()) + { + vars_added += ParseVariables(sc, dwarf_cu, func_low_pc, die->GetFirstChild(), true, true, cc_variable_list); + } + + if (parse_siblings) + die = die->GetSibling(); + else + die = NULL; + } + return vars_added; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +SymbolFileDWARF::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileDWARF::GetPluginVersion() +{ + return 1; +} + +void +SymbolFileDWARF::CompleteTagDecl (void *baton, clang::TagDecl *decl) +{ + SymbolFileDWARF *symbol_file_dwarf = (SymbolFileDWARF *)baton; + ClangASTType clang_type = symbol_file_dwarf->GetClangASTContext().GetTypeForDecl (decl); + if (clang_type) + symbol_file_dwarf->ResolveClangOpaqueTypeDefinition (clang_type); +} + +void +SymbolFileDWARF::CompleteObjCInterfaceDecl (void *baton, clang::ObjCInterfaceDecl *decl) +{ + SymbolFileDWARF *symbol_file_dwarf = (SymbolFileDWARF *)baton; + ClangASTType clang_type = symbol_file_dwarf->GetClangASTContext().GetTypeForDecl (decl); + if (clang_type) + symbol_file_dwarf->ResolveClangOpaqueTypeDefinition (clang_type); +} + +void +SymbolFileDWARF::DumpIndexes () +{ + StreamFile s(stdout, false); + + s.Printf ("DWARF index for (%s) '%s':", + GetObjectFile()->GetModule()->GetArchitecture().GetArchitectureName(), + GetObjectFile()->GetFileSpec().GetPath().c_str()); + s.Printf("\nFunction basenames:\n"); m_function_basename_index.Dump (&s); + s.Printf("\nFunction fullnames:\n"); m_function_fullname_index.Dump (&s); + s.Printf("\nFunction methods:\n"); m_function_method_index.Dump (&s); + s.Printf("\nFunction selectors:\n"); m_function_selector_index.Dump (&s); + s.Printf("\nObjective C class selectors:\n"); m_objc_class_selectors_index.Dump (&s); + s.Printf("\nGlobals and statics:\n"); m_global_index.Dump (&s); + s.Printf("\nTypes:\n"); m_type_index.Dump (&s); + s.Printf("\nNamepaces:\n"); m_namespace_index.Dump (&s); +} + +void +SymbolFileDWARF::SearchDeclContext (const clang::DeclContext *decl_context, + const char *name, + llvm::SmallVectorImpl <clang::NamedDecl *> *results) +{ + DeclContextToDIEMap::iterator iter = m_decl_ctx_to_die.find(decl_context); + + if (iter == m_decl_ctx_to_die.end()) + return; + + for (DIEPointerSet::iterator pos = iter->second.begin(), end = iter->second.end(); pos != end; ++pos) + { + const DWARFDebugInfoEntry *context_die = *pos; + + if (!results) + return; + + DWARFDebugInfo* info = DebugInfo(); + + DIEArray die_offsets; + + DWARFCompileUnit* dwarf_cu = NULL; + const DWARFDebugInfoEntry* die = NULL; + + if (m_using_apple_tables) + { + if (m_apple_types_ap.get()) + m_apple_types_ap->FindByName (name, die_offsets); + } + else + { + if (!m_indexed) + Index (); + + m_type_index.Find (ConstString(name), die_offsets); + } + + const size_t num_matches = die_offsets.size(); + + if (num_matches) + { + for (size_t i = 0; i < num_matches; ++i) + { + const dw_offset_t die_offset = die_offsets[i]; + die = info->GetDIEPtrWithCompileUnitHint (die_offset, &dwarf_cu); + + if (die->GetParent() != context_die) + continue; + + Type *matching_type = ResolveType (dwarf_cu, die); + + clang::QualType qual_type = matching_type->GetClangForwardType().GetQualType(); + + if (const clang::TagType *tag_type = llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr())) + { + clang::TagDecl *tag_decl = tag_type->getDecl(); + results->push_back(tag_decl); + } + else if (const clang::TypedefType *typedef_type = llvm::dyn_cast<clang::TypedefType>(qual_type.getTypePtr())) + { + clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl(); + results->push_back(typedef_decl); + } + } + } + } +} + +void +SymbolFileDWARF::FindExternalVisibleDeclsByName (void *baton, + const clang::DeclContext *decl_context, + clang::DeclarationName decl_name, + llvm::SmallVectorImpl <clang::NamedDecl *> *results) +{ + + switch (decl_context->getDeclKind()) + { + case clang::Decl::Namespace: + case clang::Decl::TranslationUnit: + { + SymbolFileDWARF *symbol_file_dwarf = (SymbolFileDWARF *)baton; + symbol_file_dwarf->SearchDeclContext (decl_context, decl_name.getAsString().c_str(), results); + } + break; + default: + break; + } +} + +bool +SymbolFileDWARF::LayoutRecordType (void *baton, + const clang::RecordDecl *record_decl, + uint64_t &size, + uint64_t &alignment, + llvm::DenseMap <const clang::FieldDecl *, uint64_t> &field_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &base_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &vbase_offsets) +{ + SymbolFileDWARF *symbol_file_dwarf = (SymbolFileDWARF *)baton; + return symbol_file_dwarf->LayoutRecordType (record_decl, size, alignment, field_offsets, base_offsets, vbase_offsets); +} + + +bool +SymbolFileDWARF::LayoutRecordType (const clang::RecordDecl *record_decl, + uint64_t &bit_size, + uint64_t &alignment, + llvm::DenseMap <const clang::FieldDecl *, uint64_t> &field_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &base_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &vbase_offsets) +{ + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO)); + RecordDeclToLayoutMap::iterator pos = m_record_decl_to_layout_map.find (record_decl); + bool success = false; + base_offsets.clear(); + vbase_offsets.clear(); + if (pos != m_record_decl_to_layout_map.end()) + { + bit_size = pos->second.bit_size; + alignment = pos->second.alignment; + field_offsets.swap(pos->second.field_offsets); + base_offsets.swap (pos->second.base_offsets); + vbase_offsets.swap (pos->second.vbase_offsets); + m_record_decl_to_layout_map.erase(pos); + success = true; + } + else + { + bit_size = 0; + alignment = 0; + field_offsets.clear(); + } + + if (log) + GetObjectFile()->GetModule()->LogMessage (log, + "SymbolFileDWARF::LayoutRecordType (record_decl = %p, bit_size = %" PRIu64 ", alignment = %" PRIu64 ", field_offsets[%u],base_offsets[%u], vbase_offsets[%u]) success = %i", + record_decl, + bit_size, + alignment, + (uint32_t)field_offsets.size(), + (uint32_t)base_offsets.size(), + (uint32_t)vbase_offsets.size(), + success); + return success; +} + + +SymbolFileDWARFDebugMap * +SymbolFileDWARF::GetDebugMapSymfile () +{ + if (m_debug_map_symfile == NULL && !m_debug_map_module_wp.expired()) + { + lldb::ModuleSP module_sp (m_debug_map_module_wp.lock()); + if (module_sp) + { + SymbolVendor *sym_vendor = module_sp->GetSymbolVendor(); + if (sym_vendor) + m_debug_map_symfile = (SymbolFileDWARFDebugMap *)sym_vendor->GetSymbolFile(); + } + } + return m_debug_map_symfile; +} + + diff --git a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h new file mode 100644 index 000000000000..b6c6d94782db --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -0,0 +1,622 @@ +//===-- SymbolFileDWARF.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_SymbolFileDWARF_h_ +#define SymbolFileDWARF_SymbolFileDWARF_h_ + +// C Includes +// C++ Includes +#include <list> +#include <map> +#include <set> +#include <vector> + +// Other libraries and framework includes +#include "clang/AST/CharUnits.h" +#include "clang/AST/ExternalASTSource.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" + +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/SymbolContext.h" + +// Project includes +#include "DWARFDefines.h" +#include "HashedNameToDIE.h" +#include "NameToDIE.h" +#include "UniqueDWARFASTType.h" + +//---------------------------------------------------------------------- +// Forward Declarations for this DWARF plugin +//---------------------------------------------------------------------- +class DebugMapModule; +class DWARFAbbreviationDeclaration; +class DWARFAbbreviationDeclarationSet; +class DWARFileUnit; +class DWARFDebugAbbrev; +class DWARFDebugAranges; +class DWARFDebugInfo; +class DWARFDebugInfoEntry; +class DWARFDebugLine; +class DWARFDebugPubnames; +class DWARFDebugRanges; +class DWARFDeclContext; +class DWARFDIECollection; +class DWARFFormValue; +class SymbolFileDWARFDebugMap; + +class SymbolFileDWARF : public lldb_private::SymbolFile, public lldb_private::UserID +{ +public: + friend class SymbolFileDWARFDebugMap; + friend class DebugMapModule; + friend class DWARFCompileUnit; + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile* + CreateInstance (lldb_private::ObjectFile* obj_file); + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileDWARF(lldb_private::ObjectFile* ofile); + virtual ~SymbolFileDWARF(); + + virtual uint32_t CalculateAbilities (); + virtual void InitializeObject(); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t GetNumCompileUnits(); + virtual lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index); + + virtual lldb::LanguageType ParseCompileUnitLanguage (const lldb_private::SymbolContext& sc); + virtual size_t ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList& support_files); + virtual size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + virtual size_t ParseTypes (const lldb_private::SymbolContext& sc); + virtual size_t ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* ResolveTypeUID(lldb::user_id_t type_uid); + virtual bool ResolveClangOpaqueTypeDefinition (lldb_private::ClangASTType& clang_type); + + virtual lldb_private::Type* ResolveType (DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry* type_die, bool assert_not_being_parsed = true); + virtual clang::DeclContext* GetClangDeclContextContainingTypeUID (lldb::user_id_t type_uid); + virtual clang::DeclContext* GetClangDeclContextForTypeUID (const lldb_private::SymbolContext &sc, lldb::user_id_t type_uid); + + virtual uint32_t ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + virtual uint32_t ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindGlobalVariables(const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindGlobalVariables(const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindFunctions(const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, uint32_t name_type_mask, bool include_inlines, bool append, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindFunctions(const lldb_private::RegularExpression& regex, bool include_inlines, bool append, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindTypes (const lldb_private::SymbolContext& sc, const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, lldb_private::TypeList& types); + virtual lldb_private::TypeList * + GetTypeList (); + virtual size_t GetTypes (lldb_private::SymbolContextScope *sc_scope, + uint32_t type_mask, + lldb_private::TypeList &type_list); + + virtual lldb_private::ClangASTContext & + GetClangASTContext (); + + virtual lldb_private::ClangNamespaceDecl + FindNamespace (const lldb_private::SymbolContext& sc, + const lldb_private::ConstString &name, + const lldb_private::ClangNamespaceDecl *parent_namespace_decl); + + + //------------------------------------------------------------------ + // ClangASTContext callbacks for external source lookups. + //------------------------------------------------------------------ + static void + CompleteTagDecl (void *baton, clang::TagDecl *); + + static void + CompleteObjCInterfaceDecl (void *baton, clang::ObjCInterfaceDecl *); + + static void + FindExternalVisibleDeclsByName (void *baton, + const clang::DeclContext *DC, + clang::DeclarationName Name, + llvm::SmallVectorImpl <clang::NamedDecl *> *results); + + static bool + LayoutRecordType (void *baton, + const clang::RecordDecl *record_decl, + uint64_t &size, + uint64_t &alignment, + llvm::DenseMap <const clang::FieldDecl *, uint64_t> &field_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &base_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &vbase_offsets); + + bool + LayoutRecordType (const clang::RecordDecl *record_decl, + uint64_t &size, + uint64_t &alignment, + llvm::DenseMap <const clang::FieldDecl *, uint64_t> &field_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &base_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &vbase_offsets); + + struct LayoutInfo + { + LayoutInfo () : + bit_size(0), + alignment(0), + field_offsets(), + base_offsets(), + vbase_offsets() + { + } + uint64_t bit_size; + uint64_t alignment; + llvm::DenseMap <const clang::FieldDecl *, uint64_t> field_offsets; + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> base_offsets; + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> vbase_offsets; + }; + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + // Approach 2 - count + accessor + // Index compile units would scan the initial compile units and register + // them with the module. This would only be done on demand if and only if + // the compile units were needed. + //virtual size_t GetCompUnitCount() = 0; + //virtual CompUnitSP GetCompUnitAtIndex(size_t cu_idx) = 0; + + const lldb_private::DataExtractor& get_debug_abbrev_data (); + const lldb_private::DataExtractor& get_debug_aranges_data (); + const lldb_private::DataExtractor& get_debug_frame_data (); + const lldb_private::DataExtractor& get_debug_info_data (); + const lldb_private::DataExtractor& get_debug_line_data (); + const lldb_private::DataExtractor& get_debug_loc_data (); + const lldb_private::DataExtractor& get_debug_ranges_data (); + const lldb_private::DataExtractor& get_debug_str_data (); + const lldb_private::DataExtractor& get_apple_names_data (); + const lldb_private::DataExtractor& get_apple_types_data (); + const lldb_private::DataExtractor& get_apple_namespaces_data (); + const lldb_private::DataExtractor& get_apple_objc_data (); + + + DWARFDebugAbbrev* DebugAbbrev(); + const DWARFDebugAbbrev* DebugAbbrev() const; + + DWARFDebugInfo* DebugInfo(); + const DWARFDebugInfo* DebugInfo() const; + + DWARFDebugRanges* DebugRanges(); + const DWARFDebugRanges* DebugRanges() const; + + const lldb_private::DataExtractor& + GetCachedSectionData (uint32_t got_flag, + lldb::SectionType sect_type, + lldb_private::DataExtractor &data); + + static bool + SupportedVersion(uint16_t version); + + clang::DeclContext * + GetCachedClangDeclContextForDIE (const DWARFDebugInfoEntry *die) + { + DIEToDeclContextMap::iterator pos = m_die_to_decl_ctx.find(die); + if (pos != m_die_to_decl_ctx.end()) + return pos->second; + else + return NULL; + } + + clang::DeclContext * + GetClangDeclContextForDIE (const lldb_private::SymbolContext &sc, DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die); + + clang::DeclContext * + GetClangDeclContextForDIEOffset (const lldb_private::SymbolContext &sc, dw_offset_t die_offset); + + clang::DeclContext * + GetClangDeclContextContainingDIE (DWARFCompileUnit *cu, + const DWARFDebugInfoEntry *die, + const DWARFDebugInfoEntry **decl_ctx_die); + + clang::DeclContext * + GetClangDeclContextContainingDIEOffset (dw_offset_t die_offset); + + const DWARFDebugInfoEntry * + GetDeclContextDIEContainingDIE (DWARFCompileUnit *cu, const DWARFDebugInfoEntry *die); + + void + SearchDeclContext (const clang::DeclContext *decl_context, + const char *name, + llvm::SmallVectorImpl <clang::NamedDecl *> *results); + + lldb_private::Flags& + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags& + GetFlags () const + { + return m_flags; + } + + bool + HasForwardDeclForClangType (const lldb_private::ClangASTType &clang_type); + +protected: + + enum + { + flagsGotDebugAbbrevData = (1 << 0), + flagsGotDebugArangesData = (1 << 1), + flagsGotDebugFrameData = (1 << 2), + flagsGotDebugInfoData = (1 << 3), + flagsGotDebugLineData = (1 << 4), + flagsGotDebugLocData = (1 << 5), + flagsGotDebugMacInfoData = (1 << 6), + flagsGotDebugPubNamesData = (1 << 7), + flagsGotDebugPubTypesData = (1 << 8), + flagsGotDebugRangesData = (1 << 9), + flagsGotDebugStrData = (1 << 10), + flagsGotAppleNamesData = (1 << 11), + flagsGotAppleTypesData = (1 << 12), + flagsGotAppleNamespacesData = (1 << 13), + flagsGotAppleObjCData = (1 << 14) + }; + + bool NamespaceDeclMatchesThisSymbolFile (const lldb_private::ClangNamespaceDecl *namespace_decl); + + bool DIEIsInNamespace (const lldb_private::ClangNamespaceDecl *namespace_decl, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry* die); + + DISALLOW_COPY_AND_ASSIGN (SymbolFileDWARF); + lldb::CompUnitSP ParseCompileUnit (DWARFCompileUnit* dwarf_cu, uint32_t cu_idx); + DWARFCompileUnit* GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit); + DWARFCompileUnit* GetNextUnparsedDWARFCompileUnit(DWARFCompileUnit* prev_cu); + lldb_private::CompileUnit* GetCompUnitForDWARFCompUnit(DWARFCompileUnit* dwarf_cu, uint32_t cu_idx = UINT32_MAX); + bool GetFunction (DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry* func_die, lldb_private::SymbolContext& sc); + lldb_private::Function * ParseCompileUnitFunction (const lldb_private::SymbolContext& sc, DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die); + size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc, + lldb_private::Block *parent_block, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + lldb::addr_t subprogram_low_pc, + uint32_t depth); + size_t ParseTypes (const lldb_private::SymbolContext& sc, DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool parse_siblings, bool parse_children); + lldb::TypeSP ParseType (const lldb_private::SymbolContext& sc, DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry *die, bool *type_is_new); + lldb_private::Type* ResolveTypeUID (DWARFCompileUnit* dwarf_cu, const DWARFDebugInfoEntry* die, bool assert_not_being_parsed); + + lldb::VariableSP ParseVariableDIE( + const lldb_private::SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + const lldb::addr_t func_low_pc); + + size_t ParseVariables( + const lldb_private::SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const lldb::addr_t func_low_pc, + const DWARFDebugInfoEntry *die, + bool parse_siblings, + bool parse_children, + lldb_private::VariableList* cc_variable_list = NULL); + + class DelayedAddObjCClassProperty; + typedef std::vector <DelayedAddObjCClassProperty> DelayedPropertyList; + + bool ClassOrStructIsVirtual ( + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die); + + size_t ParseChildMembers( + const lldb_private::SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + lldb_private::ClangASTType &class_clang_type, + const lldb::LanguageType class_language, + std::vector<clang::CXXBaseSpecifier *>& base_classes, + std::vector<int>& member_accessibilities, + DWARFDIECollection& member_function_dies, + DelayedPropertyList& delayed_properties, + lldb::AccessType &default_accessibility, + bool &is_a_class, + LayoutInfo &layout_info); + + size_t ParseChildParameters( + const lldb_private::SymbolContext& sc, + clang::DeclContext *containing_decl_ctx, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + bool skip_artificial, + bool &is_static, + lldb_private::TypeList* type_list, + std::vector<lldb_private::ClangASTType>& function_args, + std::vector<clang::ParmVarDecl*>& function_param_decls, + unsigned &type_quals, + lldb_private::ClangASTContext::TemplateParameterInfos &template_param_infos); + + size_t ParseChildEnumerators( + const lldb_private::SymbolContext& sc, + lldb_private::ClangASTType &clang_type, + bool is_signed, + uint32_t enumerator_byte_size, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *enum_die); + + void ParseChildArrayInfo( + const lldb_private::SymbolContext& sc, + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + int64_t& first_index, + std::vector<uint64_t>& element_orders, + uint32_t& byte_stride, + uint32_t& bit_stride); + + // Given a die_offset, figure out the symbol context representing that die. + bool ResolveFunction (dw_offset_t offset, + DWARFCompileUnit *&dwarf_cu, + lldb_private::SymbolContextList& sc_list); + + bool ResolveFunction (DWARFCompileUnit *cu, + const DWARFDebugInfoEntry *die, + lldb_private::SymbolContextList& sc_list); + + bool FunctionDieMatchesPartialName ( + const DWARFDebugInfoEntry* die, + const DWARFCompileUnit *dwarf_cu, + uint32_t name_type_mask, + const char *partial_name, + const char *base_name_start, + const char *base_name_end); + + void FindFunctions( + const lldb_private::ConstString &name, + const NameToDIE &name_to_die, + lldb_private::SymbolContextList& sc_list); + + void FindFunctions ( + const lldb_private::RegularExpression ®ex, + const NameToDIE &name_to_die, + lldb_private::SymbolContextList& sc_list); + + void FindFunctions ( + const lldb_private::RegularExpression ®ex, + const DWARFMappedHash::MemoryTable &memory_table, + lldb_private::SymbolContextList& sc_list); + + lldb::TypeSP FindDefinitionTypeForDIE ( + DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + const lldb_private::ConstString &type_name); + + lldb::TypeSP FindDefinitionTypeForDWARFDeclContext ( + const DWARFDeclContext &die_decl_ctx); + + lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE ( + const DWARFDebugInfoEntry *die, + const lldb_private::ConstString &type_name, + bool must_be_implementation); + + bool Supports_DW_AT_APPLE_objc_complete_type (DWARFCompileUnit *cu); + + lldb::TypeSP FindCompleteObjCDefinitionType (const lldb_private::ConstString &type_name, + bool header_definition_ok); + + lldb_private::Symbol * GetObjCClassSymbol (const lldb_private::ConstString &objc_class_name); + + void ParseFunctions (const DIEArray &die_offsets, + lldb_private::SymbolContextList& sc_list); + lldb::TypeSP GetTypeForDIE (DWARFCompileUnit *cu, + const DWARFDebugInfoEntry* die); + + uint32_t FindTypes(std::vector<dw_offset_t> die_offsets, uint32_t max_matches, lldb_private::TypeList& types); + + void Index(); + + void DumpIndexes(); + + void SetDebugMapModule (const lldb::ModuleSP &module_sp) + { + m_debug_map_module_wp = module_sp; + } + + SymbolFileDWARFDebugMap * + GetDebugMapSymfile (); + + const DWARFDebugInfoEntry * + FindBlockContainingSpecification (dw_offset_t func_die_offset, + dw_offset_t spec_block_die_offset, + DWARFCompileUnit **dwarf_cu_handle); + + const DWARFDebugInfoEntry * + FindBlockContainingSpecification (DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + dw_offset_t spec_block_die_offset, + DWARFCompileUnit **dwarf_cu_handle); + + clang::NamespaceDecl * + ResolveNamespaceDIE (DWARFCompileUnit *curr_cu, const DWARFDebugInfoEntry *die); + + UniqueDWARFASTTypeMap & + GetUniqueDWARFASTTypeMap (); + + void LinkDeclContextToDIE (clang::DeclContext *decl_ctx, + const DWARFDebugInfoEntry *die) + { + m_die_to_decl_ctx[die] = decl_ctx; + // There can be many DIEs for a single decl context + m_decl_ctx_to_die[decl_ctx].insert(die); + } + + bool + UserIDMatches (lldb::user_id_t uid) const + { + const lldb::user_id_t high_uid = uid & 0xffffffff00000000ull; + if (high_uid) + return high_uid == GetID(); + return true; + } + + lldb::user_id_t + MakeUserID (dw_offset_t die_offset) const + { + return GetID() | die_offset; + } + + static bool + DeclKindIsCXXClass (clang::Decl::Kind decl_kind) + { + switch (decl_kind) + { + case clang::Decl::CXXRecord: + case clang::Decl::ClassTemplateSpecialization: + return true; + default: + break; + } + return false; + } + + bool + ParseTemplateParameterInfos (DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *parent_die, + lldb_private::ClangASTContext::TemplateParameterInfos &template_param_infos); + + bool + ParseTemplateDIE (DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + lldb_private::ClangASTContext::TemplateParameterInfos &template_param_infos); + + clang::ClassTemplateDecl * + ParseClassTemplateDecl (clang::DeclContext *decl_ctx, + lldb::AccessType access_type, + const char *parent_name, + int tag_decl_kind, + const lldb_private::ClangASTContext::TemplateParameterInfos &template_param_infos); + + bool + DIEDeclContextsMatch (DWARFCompileUnit* cu1, const DWARFDebugInfoEntry *die1, + DWARFCompileUnit* cu2, const DWARFDebugInfoEntry *die2); + + bool + ClassContainsSelector (DWARFCompileUnit *dwarf_cu, + const DWARFDebugInfoEntry *class_die, + const lldb_private::ConstString &selector); + + bool + CopyUniqueClassMethodTypes (SymbolFileDWARF *class_symfile, + lldb_private::Type *class_type, + DWARFCompileUnit* src_cu, + const DWARFDebugInfoEntry *src_class_die, + DWARFCompileUnit* dst_cu, + const DWARFDebugInfoEntry *dst_class_die, + llvm::SmallVectorImpl <const DWARFDebugInfoEntry *> &failures); + + bool + FixupAddress (lldb_private::Address &addr); + + typedef std::set<lldb_private::Type *> TypeSet; + + void + GetTypes (DWARFCompileUnit* dwarf_cu, + const DWARFDebugInfoEntry *die, + dw_offset_t min_die_offset, + dw_offset_t max_die_offset, + uint32_t type_mask, + TypeSet &type_set); + + lldb::ModuleWP m_debug_map_module_wp; + SymbolFileDWARFDebugMap * m_debug_map_symfile; + clang::TranslationUnitDecl * m_clang_tu_decl; + lldb_private::Flags m_flags; + lldb_private::DataExtractor m_dwarf_data; + lldb_private::DataExtractor m_data_debug_abbrev; + lldb_private::DataExtractor m_data_debug_aranges; + lldb_private::DataExtractor m_data_debug_frame; + lldb_private::DataExtractor m_data_debug_info; + lldb_private::DataExtractor m_data_debug_line; + lldb_private::DataExtractor m_data_debug_loc; + lldb_private::DataExtractor m_data_debug_ranges; + lldb_private::DataExtractor m_data_debug_str; + lldb_private::DataExtractor m_data_apple_names; + lldb_private::DataExtractor m_data_apple_types; + lldb_private::DataExtractor m_data_apple_namespaces; + lldb_private::DataExtractor m_data_apple_objc; + + // The unique pointer items below are generated on demand if and when someone accesses + // them through a non const version of this class. + std::unique_ptr<DWARFDebugAbbrev> m_abbr; + std::unique_ptr<DWARFDebugInfo> m_info; + std::unique_ptr<DWARFDebugLine> m_line; + std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_names_ap; + std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_types_ap; + std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_namespaces_ap; + std::unique_ptr<DWARFMappedHash::MemoryTable> m_apple_objc_ap; + NameToDIE m_function_basename_index; // All concrete functions + NameToDIE m_function_fullname_index; // All concrete functions + NameToDIE m_function_method_index; // All inlined functions + NameToDIE m_function_selector_index; // All method names for functions of classes + NameToDIE m_objc_class_selectors_index; // Given a class name, find all selectors for the class + NameToDIE m_global_index; // Global and static variables + NameToDIE m_type_index; // All type DIE offsets + NameToDIE m_namespace_index; // All type DIE offsets + bool m_indexed:1, + m_is_external_ast_source:1, + m_using_apple_tables:1; + lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type; + + std::unique_ptr<DWARFDebugRanges> m_ranges; + UniqueDWARFASTTypeMap m_unique_ast_type_map; + typedef llvm::SmallPtrSet<const DWARFDebugInfoEntry *, 4> DIEPointerSet; + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, clang::DeclContext *> DIEToDeclContextMap; + typedef llvm::DenseMap<const clang::DeclContext *, DIEPointerSet> DeclContextToDIEMap; + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb_private::Type *> DIEToTypePtr; + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb::VariableSP> DIEToVariableSP; + typedef llvm::DenseMap<const DWARFDebugInfoEntry *, lldb::clang_type_t> DIEToClangType; + typedef llvm::DenseMap<lldb::clang_type_t, const DWARFDebugInfoEntry *> ClangTypeToDIE; + typedef llvm::DenseMap<const clang::RecordDecl *, LayoutInfo> RecordDeclToLayoutMap; + DIEToDeclContextMap m_die_to_decl_ctx; + DeclContextToDIEMap m_decl_ctx_to_die; + DIEToTypePtr m_die_to_type; + DIEToVariableSP m_die_to_variable_sp; + DIEToClangType m_forward_decl_die_to_clang_type; + ClangTypeToDIE m_forward_decl_clang_type_to_die; + RecordDeclToLayoutMap m_record_decl_to_layout_map; +}; + +#endif // SymbolFileDWARF_SymbolFileDWARF_h_ diff --git a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp new file mode 100644 index 000000000000..a0430e3d52b6 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -0,0 +1,1586 @@ +//===-- SymbolFileDWARFDebugMap.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileDWARFDebugMap.h" + +#include "DWARFDebugAranges.h" + +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Section.h" + +//#define DEBUG_OSO_DMAP // DO NOT CHECKIN WITH THIS NOT COMMENTED OUT +#if defined(DEBUG_OSO_DMAP) +#include "lldb/Core/StreamFile.h" +#endif +#include "lldb/Core/Timer.h" + +#include "lldb/Symbol/ClangExternalASTSourceCallbacks.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" + +#include "LogChannelDWARF.h" +#include "SymbolFileDWARF.h" + +using namespace lldb; +using namespace lldb_private; + +// Subclass lldb_private::Module so we can intercept the "Module::GetObjectFile()" +// (so we can fixup the object file sections) and also for "Module::GetSymbolVendor()" +// (so we can fixup the symbol file id. + + + + +const SymbolFileDWARFDebugMap::FileRangeMap & +SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap(SymbolFileDWARFDebugMap *exe_symfile) +{ + if (file_range_map_valid) + return file_range_map; + + file_range_map_valid = true; + + Module *oso_module = exe_symfile->GetModuleByCompUnitInfo (this); + if (!oso_module) + return file_range_map; + + ObjectFile *oso_objfile = oso_module->GetObjectFile(); + if (!oso_objfile) + return file_range_map; + + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); + if (log) + { + ConstString object_name (oso_module->GetObjectName()); + log->Printf("%p: SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap ('%s')", + this, + oso_module->GetSpecificationDescription().c_str()); + } + + + std::vector<SymbolFileDWARFDebugMap::CompileUnitInfo *> cu_infos; + if (exe_symfile->GetCompUnitInfosForModule(oso_module, cu_infos)) + { + for (auto comp_unit_info : cu_infos) + { + Symtab *exe_symtab = exe_symfile->GetObjectFile()->GetSymtab(); + ModuleSP oso_module_sp (oso_objfile->GetModule()); + Symtab *oso_symtab = oso_objfile->GetSymtab(); + + ///const uint32_t fun_resolve_flags = SymbolContext::Module | eSymbolContextCompUnit | eSymbolContextFunction; + //SectionList *oso_sections = oso_objfile->Sections(); + // Now we need to make sections that map from zero based object + // file addresses to where things eneded up in the main executable. + + assert (comp_unit_info->first_symbol_index != UINT32_MAX); + // End index is one past the last valid symbol index + const uint32_t oso_end_idx = comp_unit_info->last_symbol_index + 1; + for (uint32_t idx = comp_unit_info->first_symbol_index + 2; // Skip the N_SO and N_OSO + idx < oso_end_idx; + ++idx) + { + Symbol *exe_symbol = exe_symtab->SymbolAtIndex(idx); + if (exe_symbol) + { + if (exe_symbol->IsDebug() == false) + continue; + + switch (exe_symbol->GetType()) + { + default: + break; + + case eSymbolTypeCode: + { + // For each N_FUN, or function that we run into in the debug map + // we make a new section that we add to the sections found in the + // .o file. This new section has the file address set to what the + // addresses are in the .o file, and the load address is adjusted + // to match where it ended up in the final executable! We do this + // before we parse any dwarf info so that when it goes get parsed + // all section/offset addresses that get registered will resolve + // correctly to the new addresses in the main executable. + + // First we find the original symbol in the .o file's symbol table + Symbol *oso_fun_symbol = oso_symtab->FindFirstSymbolWithNameAndType (exe_symbol->GetMangled().GetName(Mangled::ePreferMangled), + eSymbolTypeCode, + Symtab::eDebugNo, + Symtab::eVisibilityAny); + if (oso_fun_symbol) + { + // Add the inverse OSO file address to debug map entry mapping + exe_symfile->AddOSOFileRange (this, + exe_symbol->GetAddress().GetFileAddress(), + oso_fun_symbol->GetAddress().GetFileAddress(), + std::min<addr_t>(exe_symbol->GetByteSize(), oso_fun_symbol->GetByteSize())); + + } + } + break; + + case eSymbolTypeData: + { + // For each N_GSYM we remap the address for the global by making + // a new section that we add to the sections found in the .o file. + // This new section has the file address set to what the + // addresses are in the .o file, and the load address is adjusted + // to match where it ended up in the final executable! We do this + // before we parse any dwarf info so that when it goes get parsed + // all section/offset addresses that get registered will resolve + // correctly to the new addresses in the main executable. We + // initially set the section size to be 1 byte, but will need to + // fix up these addresses further after all globals have been + // parsed to span the gaps, or we can find the global variable + // sizes from the DWARF info as we are parsing. + + // Next we find the non-stab entry that corresponds to the N_GSYM in the .o file + Symbol *oso_gsym_symbol = oso_symtab->FindFirstSymbolWithNameAndType (exe_symbol->GetMangled().GetName(Mangled::ePreferMangled), + eSymbolTypeData, + Symtab::eDebugNo, + Symtab::eVisibilityAny); + + if (exe_symbol && oso_gsym_symbol && + exe_symbol->ValueIsAddress() && + oso_gsym_symbol->ValueIsAddress()) + { + // Add the inverse OSO file address to debug map entry mapping + exe_symfile->AddOSOFileRange (this, + exe_symbol->GetAddress().GetFileAddress(), + oso_gsym_symbol->GetAddress().GetFileAddress(), + std::min<addr_t>(exe_symbol->GetByteSize(), oso_gsym_symbol->GetByteSize())); + } + } + break; + } + } + } + + exe_symfile->FinalizeOSOFileRanges (this); + // We don't need the symbols anymore for the .o files + oso_objfile->ClearSymtab(); + } + } + return file_range_map; +} + + +class DebugMapModule : public Module +{ +public: + DebugMapModule (const ModuleSP &exe_module_sp, + uint32_t cu_idx, + const FileSpec& file_spec, + const ArchSpec& arch, + const ConstString *object_name, + off_t object_offset, + const TimeValue *object_mod_time_ptr) : + Module (file_spec, arch, object_name, object_offset, object_mod_time_ptr), + m_exe_module_wp (exe_module_sp), + m_cu_idx (cu_idx) + { + } + + virtual + ~DebugMapModule () + { + } + + + virtual SymbolVendor* + GetSymbolVendor(bool can_create = true, lldb_private::Stream *feedback_strm = NULL) + { + // Scope for locker + if (m_symfile_ap.get() || can_create == false) + return m_symfile_ap.get(); + + ModuleSP exe_module_sp (m_exe_module_wp.lock()); + if (exe_module_sp) + { + // Now get the object file outside of a locking scope + ObjectFile *oso_objfile = GetObjectFile (); + if (oso_objfile) + { + Mutex::Locker locker (m_mutex); + SymbolVendor* symbol_vendor = Module::GetSymbolVendor(can_create, feedback_strm); + if (symbol_vendor) + { + // Set a a pointer to this class to set our OSO DWARF file know + // that the DWARF is being used along with a debug map and that + // it will have the remapped sections that we do below. + SymbolFileDWARF *oso_symfile = SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF(symbol_vendor->GetSymbolFile()); + + if (!oso_symfile) + return NULL; + + ObjectFile *exe_objfile = exe_module_sp->GetObjectFile(); + SymbolVendor *exe_sym_vendor = exe_module_sp->GetSymbolVendor(); + + if (exe_objfile && exe_sym_vendor) + { + if (oso_symfile->GetNumCompileUnits() == 1) + { + oso_symfile->SetDebugMapModule(exe_module_sp); + // Set the ID of the symbol file DWARF to the index of the OSO + // shifted left by 32 bits to provide a unique prefix for any + // UserID's that get created in the symbol file. + oso_symfile->SetID (((uint64_t)m_cu_idx + 1ull) << 32ull); + } + else + { + oso_symfile->SetID (UINT64_MAX); + } + } + return symbol_vendor; + } + } + } + return NULL; + } + +protected: + ModuleWP m_exe_module_wp; + const uint32_t m_cu_idx; +}; + +void +SymbolFileDWARFDebugMap::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileDWARFDebugMap::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +SymbolFileDWARFDebugMap::GetPluginNameStatic() +{ + static ConstString g_name("dwarf-debugmap"); + return g_name; +} + +const char * +SymbolFileDWARFDebugMap::GetPluginDescriptionStatic() +{ + return "DWARF and DWARF3 debug symbol file reader (debug map)."; +} + +SymbolFile* +SymbolFileDWARFDebugMap::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileDWARFDebugMap (obj_file); +} + + +SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap (ObjectFile* ofile) : + SymbolFile(ofile), + m_flags(), + m_compile_unit_infos(), + m_func_indexes(), + m_glob_indexes(), + m_supports_DW_AT_APPLE_objc_complete_type (eLazyBoolCalculate) +{ +} + + +SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() +{ +} + +void +SymbolFileDWARFDebugMap::InitializeObject() +{ + // Install our external AST source callbacks so we can complete Clang types. + llvm::OwningPtr<clang::ExternalASTSource> ast_source_ap ( + new ClangExternalASTSourceCallbacks (SymbolFileDWARFDebugMap::CompleteTagDecl, + SymbolFileDWARFDebugMap::CompleteObjCInterfaceDecl, + NULL, + SymbolFileDWARFDebugMap::LayoutRecordType, + this)); + + GetClangASTContext().SetExternalSource (ast_source_ap); +} + +void +SymbolFileDWARFDebugMap::InitOSO() +{ + if (m_flags.test(kHaveInitializedOSOs)) + return; + + m_flags.set(kHaveInitializedOSOs); + + // If the object file has been stripped, there is no sense in looking further + // as all of the debug symbols for the debug map will not be available + if (m_obj_file->IsStripped()) + return; + + // Also make sure the file type is some sort of executable. Core files, debug + // info files (dSYM), object files (.o files), and stub libraries all can + switch (m_obj_file->GetType()) + { + case ObjectFile::eTypeInvalid: + case ObjectFile::eTypeCoreFile: + case ObjectFile::eTypeDebugInfo: + case ObjectFile::eTypeObjectFile: + case ObjectFile::eTypeStubLibrary: + case ObjectFile::eTypeUnknown: + return; + + case ObjectFile::eTypeExecutable: + case ObjectFile::eTypeDynamicLinker: + case ObjectFile::eTypeSharedLibrary: + break; + } + + // In order to get the abilities of this plug-in, we look at the list of + // N_OSO entries (object files) from the symbol table and make sure that + // these files exist and also contain valid DWARF. If we get any of that + // then we return the abilities of the first N_OSO's DWARF. + + Symtab* symtab = m_obj_file->GetSymtab(); + if (symtab) + { + Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); + + std::vector<uint32_t> oso_indexes; + // When a mach-o symbol is encoded, the n_type field is encoded in bits + // 23:16, and the n_desc field is encoded in bits 15:0. + // + // To find all N_OSO entries that are part of the DWARF + debug map + // we find only object file symbols with the flags value as follows: + // bits 23:16 == 0x66 (N_OSO) + // bits 15: 0 == 0x0001 (specifies this is a debug map object file) + const uint32_t k_oso_symbol_flags_value = 0x660001u; + + const uint32_t oso_index_count = symtab->AppendSymbolIndexesWithTypeAndFlagsValue(eSymbolTypeObjectFile, k_oso_symbol_flags_value, oso_indexes); + + if (oso_index_count > 0) + { + symtab->AppendSymbolIndexesWithType (eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny, m_func_indexes); + symtab->AppendSymbolIndexesWithType (eSymbolTypeData, Symtab::eDebugYes, Symtab::eVisibilityAny, m_glob_indexes); + + symtab->SortSymbolIndexesByValue(m_func_indexes, true); + symtab->SortSymbolIndexesByValue(m_glob_indexes, true); + + for (uint32_t sym_idx : m_func_indexes) + { + const Symbol *symbol = symtab->SymbolAtIndex(sym_idx); + lldb::addr_t file_addr = symbol->GetAddress().GetFileAddress(); + lldb::addr_t byte_size = symbol->GetByteSize(); + DebugMap::Entry debug_map_entry(file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS)); + m_debug_map.Append(debug_map_entry); + } + for (uint32_t sym_idx : m_glob_indexes) + { + const Symbol *symbol = symtab->SymbolAtIndex(sym_idx); + lldb::addr_t file_addr = symbol->GetAddress().GetFileAddress(); + lldb::addr_t byte_size = symbol->GetByteSize(); + DebugMap::Entry debug_map_entry(file_addr, byte_size, OSOEntry(sym_idx, LLDB_INVALID_ADDRESS)); + m_debug_map.Append(debug_map_entry); + } + m_debug_map.Sort(); + + m_compile_unit_infos.resize(oso_index_count); + + for (uint32_t i=0; i<oso_index_count; ++i) + { + const uint32_t so_idx = oso_indexes[i] - 1; + const uint32_t oso_idx = oso_indexes[i]; + const Symbol *so_symbol = symtab->SymbolAtIndex(so_idx); + const Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx); + if (so_symbol && + oso_symbol && + so_symbol->GetType() == eSymbolTypeSourceFile && + oso_symbol->GetType() == eSymbolTypeObjectFile) + { + m_compile_unit_infos[i].so_file.SetFile(so_symbol->GetName().AsCString(), false); + m_compile_unit_infos[i].oso_path = oso_symbol->GetName(); + TimeValue oso_mod_time; + oso_mod_time.OffsetWithSeconds(oso_symbol->GetAddress().GetOffset()); + m_compile_unit_infos[i].oso_mod_time = oso_mod_time; + uint32_t sibling_idx = so_symbol->GetSiblingIndex(); + // The sibling index can't be less that or equal to the current index "i" + if (sibling_idx <= i) + { + m_obj_file->GetModule()->ReportError ("N_SO in symbol with UID %u has invalid sibling in debug map, please file a bug and attach the binary listed in this error", so_symbol->GetID()); + } + else + { + const Symbol* last_symbol = symtab->SymbolAtIndex (sibling_idx - 1); + m_compile_unit_infos[i].first_symbol_index = so_idx; + m_compile_unit_infos[i].last_symbol_index = sibling_idx - 1; + m_compile_unit_infos[i].first_symbol_id = so_symbol->GetID(); + m_compile_unit_infos[i].last_symbol_id = last_symbol->GetID(); + + if (log) + log->Printf("Initialized OSO 0x%8.8x: file=%s", i, oso_symbol->GetName().GetCString()); + } + } + else + { + if (oso_symbol == NULL) + m_obj_file->GetModule()->ReportError ("N_OSO symbol[%u] can't be found, please file a bug and attach the binary listed in this error", oso_idx); + else if (so_symbol == NULL) + m_obj_file->GetModule()->ReportError ("N_SO not found for N_OSO symbol[%u], please file a bug and attach the binary listed in this error", oso_idx); + else if (so_symbol->GetType() != eSymbolTypeSourceFile) + m_obj_file->GetModule()->ReportError ("N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], please file a bug and attach the binary listed in this error", so_symbol->GetType(), oso_idx); + else if (oso_symbol->GetType() != eSymbolTypeSourceFile) + m_obj_file->GetModule()->ReportError ("N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], please file a bug and attach the binary listed in this error", oso_symbol->GetType(), oso_idx); + } + } + } + } +} + +Module * +SymbolFileDWARFDebugMap::GetModuleByOSOIndex (uint32_t oso_idx) +{ + const uint32_t cu_count = GetNumCompileUnits(); + if (oso_idx < cu_count) + return GetModuleByCompUnitInfo (&m_compile_unit_infos[oso_idx]); + return NULL; +} + +Module * +SymbolFileDWARFDebugMap::GetModuleByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + if (!comp_unit_info->oso_sp) + { + auto pos = m_oso_map.find (comp_unit_info->oso_path); + if (pos != m_oso_map.end()) + { + comp_unit_info->oso_sp = pos->second; + } + else + { + ObjectFile *obj_file = GetObjectFile(); + comp_unit_info->oso_sp.reset (new OSOInfo()); + m_oso_map[comp_unit_info->oso_path] = comp_unit_info->oso_sp; + const char *oso_path = comp_unit_info->oso_path.GetCString(); + FileSpec oso_file (oso_path, false); + ConstString oso_object; + if (oso_file.Exists()) + { + TimeValue oso_mod_time (oso_file.GetModificationTime()); + if (oso_mod_time != comp_unit_info->oso_mod_time) + { + obj_file->GetModule()->ReportError ("debug map object file '%s' has changed (actual time is 0x%" PRIx64 ", debug map time is 0x%" PRIx64 ") since this executable was linked, file will be ignored", + oso_file.GetPath().c_str(), + oso_mod_time.GetAsSecondsSinceJan1_1970(), + comp_unit_info->oso_mod_time.GetAsSecondsSinceJan1_1970()); + return NULL; + } + + } + else + { + const bool must_exist = true; + + if (!ObjectFile::SplitArchivePathWithObject (oso_path, + oso_file, + oso_object, + must_exist)) + { + return NULL; + } + } + // Always create a new module for .o files. Why? Because we + // use the debug map, to add new sections to each .o file and + // even though a .o file might not have changed, the sections + // that get added to the .o file can change. + comp_unit_info->oso_sp->module_sp.reset (new DebugMapModule (obj_file->GetModule(), + GetCompUnitInfoIndex(comp_unit_info), + oso_file, + m_obj_file->GetModule()->GetArchitecture(), + oso_object ? &oso_object : NULL, + 0, + oso_object ? &comp_unit_info->oso_mod_time : NULL)); + } + } + if (comp_unit_info->oso_sp) + return comp_unit_info->oso_sp->module_sp.get(); + return NULL; +} + + +bool +SymbolFileDWARFDebugMap::GetFileSpecForSO (uint32_t oso_idx, FileSpec &file_spec) +{ + if (oso_idx < m_compile_unit_infos.size()) + { + if (m_compile_unit_infos[oso_idx].so_file) + { + file_spec = m_compile_unit_infos[oso_idx].so_file; + return true; + } + } + return false; +} + + + +ObjectFile * +SymbolFileDWARFDebugMap::GetObjectFileByOSOIndex (uint32_t oso_idx) +{ + Module *oso_module = GetModuleByOSOIndex (oso_idx); + if (oso_module) + return oso_module->GetObjectFile(); + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFile (const SymbolContext& sc) +{ + CompileUnitInfo *comp_unit_info = GetCompUnitInfo (sc); + if (comp_unit_info) + return GetSymbolFileByCompUnitInfo (comp_unit_info); + return NULL; +} + +ObjectFile * +SymbolFileDWARFDebugMap::GetObjectFileByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + Module *oso_module = GetModuleByCompUnitInfo (comp_unit_info); + if (oso_module) + return oso_module->GetObjectFile(); + return NULL; +} + + +uint32_t +SymbolFileDWARFDebugMap::GetCompUnitInfoIndex (const CompileUnitInfo *comp_unit_info) +{ + if (!m_compile_unit_infos.empty()) + { + const CompileUnitInfo *first_comp_unit_info = &m_compile_unit_infos.front(); + const CompileUnitInfo *last_comp_unit_info = &m_compile_unit_infos.back(); + if (first_comp_unit_info <= comp_unit_info && comp_unit_info <= last_comp_unit_info) + return comp_unit_info - first_comp_unit_info; + } + return UINT32_MAX; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileByOSOIndex (uint32_t oso_idx) +{ + if (oso_idx < m_compile_unit_infos.size()) + return GetSymbolFileByCompUnitInfo (&m_compile_unit_infos[oso_idx]); + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileAsSymbolFileDWARF (SymbolFile *sym_file) +{ + if (sym_file && sym_file->GetPluginName() == SymbolFileDWARF::GetPluginNameStatic()) + return (SymbolFileDWARF *)sym_file; + return NULL; +} + +SymbolFileDWARF * +SymbolFileDWARFDebugMap::GetSymbolFileByCompUnitInfo (CompileUnitInfo *comp_unit_info) +{ + Module *oso_module = GetModuleByCompUnitInfo (comp_unit_info); + if (oso_module) + { + SymbolVendor *sym_vendor = oso_module->GetSymbolVendor(); + if (sym_vendor) + return GetSymbolFileAsSymbolFileDWARF (sym_vendor->GetSymbolFile()); + } + return NULL; +} + +uint32_t +SymbolFileDWARFDebugMap::CalculateAbilities () +{ + // In order to get the abilities of this plug-in, we look at the list of + // N_OSO entries (object files) from the symbol table and make sure that + // these files exist and also contain valid DWARF. If we get any of that + // then we return the abilities of the first N_OSO's DWARF. + + const uint32_t oso_index_count = GetNumCompileUnits(); + if (oso_index_count > 0) + { + InitOSO(); + if (!m_compile_unit_infos.empty()) + { + return SymbolFile::CompileUnits | + SymbolFile::Functions | + SymbolFile::Blocks | + SymbolFile::GlobalVariables | + SymbolFile::LocalVariables | + SymbolFile::VariableTypes | + SymbolFile::LineTables ; + } + } + return 0; +} + +uint32_t +SymbolFileDWARFDebugMap::GetNumCompileUnits() +{ + InitOSO (); + return m_compile_unit_infos.size(); +} + + +CompUnitSP +SymbolFileDWARFDebugMap::ParseCompileUnitAtIndex(uint32_t cu_idx) +{ + CompUnitSP comp_unit_sp; + const uint32_t cu_count = GetNumCompileUnits(); + + if (cu_idx < cu_count) + { + Module *oso_module = GetModuleByCompUnitInfo (&m_compile_unit_infos[cu_idx]); + if (oso_module) + { + FileSpec so_file_spec; + if (GetFileSpecForSO (cu_idx, so_file_spec)) + { + // User zero as the ID to match the compile unit at offset + // zero in each .o file since each .o file can only have + // one compile unit for now. + lldb::user_id_t cu_id = 0; + m_compile_unit_infos[cu_idx].compile_unit_sp.reset(new CompileUnit (m_obj_file->GetModule(), + NULL, + so_file_spec, + cu_id, + eLanguageTypeUnknown)); + + if (m_compile_unit_infos[cu_idx].compile_unit_sp) + { + // Let our symbol vendor know about this compile unit + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex (cu_idx, m_compile_unit_infos[cu_idx].compile_unit_sp); + } + } + } + comp_unit_sp = m_compile_unit_infos[cu_idx].compile_unit_sp; + } + + return comp_unit_sp; +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompUnitInfo (const SymbolContext& sc) +{ + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t i=0; i<cu_count; ++i) + { + if (sc.comp_unit == m_compile_unit_infos[i].compile_unit_sp.get()) + return &m_compile_unit_infos[i]; + } + return NULL; +} + + +size_t +SymbolFileDWARFDebugMap::GetCompUnitInfosForModule (const lldb_private::Module *module, std::vector<CompileUnitInfo *>& cu_infos) +{ + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t i=0; i<cu_count; ++i) + { + if (module == GetModuleByCompUnitInfo (&m_compile_unit_infos[i])) + cu_infos.push_back (&m_compile_unit_infos[i]); + } + return cu_infos.size(); +} + +lldb::LanguageType +SymbolFileDWARFDebugMap::ParseCompileUnitLanguage (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitLanguage (sc); + return eLanguageTypeUnknown; +} + +size_t +SymbolFileDWARFDebugMap::ParseCompileUnitFunctions (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitFunctions (sc); + return 0; +} + +bool +SymbolFileDWARFDebugMap::ParseCompileUnitLineTable (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitLineTable (sc); + return false; +} + +bool +SymbolFileDWARFDebugMap::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList &support_files) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseCompileUnitSupportFiles (sc, support_files); + return false; +} + + +size_t +SymbolFileDWARFDebugMap::ParseFunctionBlocks (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseFunctionBlocks (sc); + return 0; +} + + +size_t +SymbolFileDWARFDebugMap::ParseTypes (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseTypes (sc); + return 0; +} + + +size_t +SymbolFileDWARFDebugMap::ParseVariablesForContext (const SymbolContext& sc) +{ + SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->ParseVariablesForContext (sc); + return 0; +} + + + +Type* +SymbolFileDWARFDebugMap::ResolveTypeUID(lldb::user_id_t type_uid) +{ + const uint64_t oso_idx = GetOSOIndexFromUserID (type_uid); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + if (oso_dwarf) + return oso_dwarf->ResolveTypeUID (type_uid); + return NULL; +} + +bool +SymbolFileDWARFDebugMap::ResolveClangOpaqueTypeDefinition (ClangASTType& clang_type) +{ + // We have a struct/union/class/enum that needs to be fully resolved. + return false; +} + +uint32_t +SymbolFileDWARFDebugMap::ResolveSymbolContext (const Address& exe_so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + uint32_t resolved_flags = 0; + Symtab* symtab = m_obj_file->GetSymtab(); + if (symtab) + { + const addr_t exe_file_addr = exe_so_addr.GetFileAddress(); + + const DebugMap::Entry *debug_map_entry = m_debug_map.FindEntryThatContains (exe_file_addr); + if (debug_map_entry) + { + + sc.symbol = symtab->SymbolAtIndex(debug_map_entry->data.GetExeSymbolIndex()); + + if (sc.symbol != NULL) + { + resolved_flags |= eSymbolContextSymbol; + + uint32_t oso_idx = 0; + CompileUnitInfo* comp_unit_info = GetCompileUnitInfoForSymbolWithID (sc.symbol->GetID(), &oso_idx); + if (comp_unit_info) + { + comp_unit_info->GetFileRangeMap(this); + Module *oso_module = GetModuleByCompUnitInfo (comp_unit_info); + if (oso_module) + { + lldb::addr_t oso_file_addr = exe_file_addr - debug_map_entry->GetRangeBase() + debug_map_entry->data.GetOSOFileAddress(); + Address oso_so_addr; + if (oso_module->ResolveFileAddress(oso_file_addr, oso_so_addr)) + { + resolved_flags |= oso_module->GetSymbolVendor()->ResolveSymbolContext (oso_so_addr, resolve_scope, sc); + } + } + } + } + } + } + return resolved_flags; +} + + +uint32_t +SymbolFileDWARFDebugMap::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + const uint32_t initial = sc_list.GetSize(); + const uint32_t cu_count = GetNumCompileUnits(); + + for (uint32_t i=0; i<cu_count; ++i) + { + // If we are checking for inlines, then we need to look through all + // compile units no matter if "file_spec" matches. + bool resolve = check_inlines; + + if (!resolve) + { + FileSpec so_file_spec; + if (GetFileSpecForSO (i, so_file_spec)) + { + // Match the full path if the incoming file_spec has a directory (not just a basename) + const bool full_match = file_spec.GetDirectory(); + resolve = FileSpec::Equal (file_spec, so_file_spec, full_match); + } + } + if (resolve) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (i); + if (oso_dwarf) + oso_dwarf->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope, sc_list); + } + } + return sc_list.GetSize() - initial; +} + +uint32_t +SymbolFileDWARFDebugMap::PrivateFindGlobalVariables +( + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + const std::vector<uint32_t> &indexes, // Indexes into the symbol table that match "name" + uint32_t max_matches, + VariableList& variables +) +{ + const uint32_t original_size = variables.GetSize(); + const size_t match_count = indexes.size(); + for (size_t i=0; i<match_count; ++i) + { + uint32_t oso_idx; + CompileUnitInfo* comp_unit_info = GetCompileUnitInfoForSymbolWithIndex (indexes[i], &oso_idx); + if (comp_unit_info) + { + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + if (oso_dwarf) + { + if (oso_dwarf->FindGlobalVariables(name, namespace_decl, true, max_matches, variables)) + if (variables.GetSize() > max_matches) + break; + } + } + } + return variables.GetSize() - original_size; +} + +uint32_t +SymbolFileDWARFDebugMap::FindGlobalVariables (const ConstString &name, const ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, VariableList& variables) +{ + + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + uint32_t total_matches = 0; + SymbolFileDWARF *oso_dwarf; + for (uint32_t oso_idx = 0; ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx)) != NULL); ++oso_idx) + { + const uint32_t oso_matches = oso_dwarf->FindGlobalVariables (name, + namespace_decl, + true, + max_matches, + variables); + if (oso_matches > 0) + { + total_matches += oso_matches; + + // Are we getting all matches? + if (max_matches == UINT32_MAX) + continue; // Yep, continue getting everything + + // If we have found enough matches, lets get out + if (max_matches >= total_matches) + break; + + // Update the max matches for any subsequent calls to find globals + // in any other object files with DWARF + max_matches -= oso_matches; + } + } + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + + +uint32_t +SymbolFileDWARFDebugMap::FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + // If we aren't appending the results to this list, then clear the list + if (!append) + variables.Clear(); + + // Remember how many variables are in the list before we search in case + // we are appending the results to a variable list. + const uint32_t original_size = variables.GetSize(); + + uint32_t total_matches = 0; + SymbolFileDWARF *oso_dwarf; + for (uint32_t oso_idx = 0; ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx)) != NULL); ++oso_idx) + { + const uint32_t oso_matches = oso_dwarf->FindGlobalVariables (regex, + true, + max_matches, + variables); + if (oso_matches > 0) + { + total_matches += oso_matches; + + // Are we getting all matches? + if (max_matches == UINT32_MAX) + continue; // Yep, continue getting everything + + // If we have found enough matches, lets get out + if (max_matches >= total_matches) + break; + + // Update the max matches for any subsequent calls to find globals + // in any other object files with DWARF + max_matches -= oso_matches; + } + } + // Return the number of variable that were appended to the list + return variables.GetSize() - original_size; +} + + +int +SymbolFileDWARFDebugMap::SymbolContainsSymbolWithIndex (uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) +{ + const uint32_t symbol_idx = *symbol_idx_ptr; + + if (symbol_idx < comp_unit_info->first_symbol_index) + return -1; + + if (symbol_idx <= comp_unit_info->last_symbol_index) + return 0; + + return 1; +} + + +int +SymbolFileDWARFDebugMap::SymbolContainsSymbolWithID (user_id_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info) +{ + const user_id_t symbol_id = *symbol_idx_ptr; + + if (symbol_id < comp_unit_info->first_symbol_id) + return -1; + + if (symbol_id <= comp_unit_info->last_symbol_id) + return 0; + + return 1; +} + + +SymbolFileDWARFDebugMap::CompileUnitInfo* +SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithIndex (uint32_t symbol_idx, uint32_t *oso_idx_ptr) +{ + const uint32_t oso_index_count = m_compile_unit_infos.size(); + CompileUnitInfo *comp_unit_info = NULL; + if (oso_index_count) + { + comp_unit_info = (CompileUnitInfo*)bsearch(&symbol_idx, + &m_compile_unit_infos[0], + m_compile_unit_infos.size(), + sizeof(CompileUnitInfo), + (ComparisonFunction)SymbolContainsSymbolWithIndex); + } + + if (oso_idx_ptr) + { + if (comp_unit_info != NULL) + *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0]; + else + *oso_idx_ptr = UINT32_MAX; + } + return comp_unit_info; +} + +SymbolFileDWARFDebugMap::CompileUnitInfo* +SymbolFileDWARFDebugMap::GetCompileUnitInfoForSymbolWithID (user_id_t symbol_id, uint32_t *oso_idx_ptr) +{ + const uint32_t oso_index_count = m_compile_unit_infos.size(); + CompileUnitInfo *comp_unit_info = NULL; + if (oso_index_count) + { + comp_unit_info = (CompileUnitInfo*)::bsearch (&symbol_id, + &m_compile_unit_infos[0], + m_compile_unit_infos.size(), + sizeof(CompileUnitInfo), + (ComparisonFunction)SymbolContainsSymbolWithID); + } + + if (oso_idx_ptr) + { + if (comp_unit_info != NULL) + *oso_idx_ptr = comp_unit_info - &m_compile_unit_infos[0]; + else + *oso_idx_ptr = UINT32_MAX; + } + return comp_unit_info; +} + + +static void +RemoveFunctionsWithModuleNotEqualTo (const ModuleSP &module_sp, SymbolContextList &sc_list, uint32_t start_idx) +{ + // We found functions in .o files. Not all functions in the .o files + // will have made it into the final output file. The ones that did + // make it into the final output file will have a section whose module + // matches the module from the ObjectFile for this SymbolFile. When + // the modules don't match, then we have something that was in a + // .o file, but doesn't map to anything in the final executable. + uint32_t i=start_idx; + while (i < sc_list.GetSize()) + { + SymbolContext sc; + sc_list.GetContextAtIndex(i, sc); + if (sc.function) + { + const SectionSP section_sp (sc.function->GetAddressRange().GetBaseAddress().GetSection()); + if (section_sp->GetModule() != module_sp) + { + sc_list.RemoveContextAtIndex(i); + continue; + } + } + ++i; + } +} + +uint32_t +SymbolFileDWARFDebugMap::FindFunctions(const ConstString &name, const ClangNamespaceDecl *namespace_decl, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARFDebugMap::FindFunctions (name = %s)", + name.GetCString()); + + uint32_t initial_size = 0; + if (append) + initial_size = sc_list.GetSize(); + else + sc_list.Clear(); + + uint32_t oso_idx = 0; + SymbolFileDWARF *oso_dwarf; + while ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx++)) != NULL) + { + uint32_t sc_idx = sc_list.GetSize(); + if (oso_dwarf->FindFunctions(name, namespace_decl, name_type_mask, include_inlines, true, sc_list)) + { + RemoveFunctionsWithModuleNotEqualTo (m_obj_file->GetModule(), sc_list, sc_idx); + } + } + + return sc_list.GetSize() - initial_size; +} + + +uint32_t +SymbolFileDWARFDebugMap::FindFunctions (const RegularExpression& regex, bool include_inlines, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARFDebugMap::FindFunctions (regex = '%s')", + regex.GetText()); + + uint32_t initial_size = 0; + if (append) + initial_size = sc_list.GetSize(); + else + sc_list.Clear(); + + uint32_t oso_idx = 0; + SymbolFileDWARF *oso_dwarf; + while ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx++)) != NULL) + { + uint32_t sc_idx = sc_list.GetSize(); + + if (oso_dwarf->FindFunctions(regex, include_inlines, true, sc_list)) + { + RemoveFunctionsWithModuleNotEqualTo (m_obj_file->GetModule(), sc_list, sc_idx); + } + } + + return sc_list.GetSize() - initial_size; +} + +size_t +SymbolFileDWARFDebugMap::GetTypes (SymbolContextScope *sc_scope, + uint32_t type_mask, + TypeList &type_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileDWARFDebugMap::GetTypes (type_mask = 0x%8.8x)", + type_mask); + + + uint32_t initial_size = type_list.GetSize(); + SymbolFileDWARF *oso_dwarf = NULL; + if (sc_scope) + { + SymbolContext sc; + sc_scope->CalculateSymbolContext(&sc); + + CompileUnitInfo *cu_info = GetCompUnitInfo (sc); + if (cu_info) + { + oso_dwarf = GetSymbolFileByCompUnitInfo (cu_info); + if (oso_dwarf) + oso_dwarf->GetTypes (sc_scope, type_mask, type_list); + } + } + else + { + uint32_t oso_idx = 0; + while ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx++)) != NULL) + { + oso_dwarf->GetTypes (sc_scope, type_mask, type_list); + } + } + return type_list.GetSize() - initial_size; +} + + +TypeSP +SymbolFileDWARFDebugMap::FindDefinitionTypeForDWARFDeclContext (const DWARFDeclContext &die_decl_ctx) +{ + TypeSP type_sp; + SymbolFileDWARF *oso_dwarf; + for (uint32_t oso_idx = 0; ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx)) != NULL); ++oso_idx) + { + type_sp = oso_dwarf->FindDefinitionTypeForDWARFDeclContext (die_decl_ctx); + if (type_sp) + break; + } + return type_sp; +} + + + +bool +SymbolFileDWARFDebugMap::Supports_DW_AT_APPLE_objc_complete_type (SymbolFileDWARF *skip_dwarf_oso) +{ + if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) + { + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo; + SymbolFileDWARF *oso_dwarf; + for (uint32_t oso_idx = 0; ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx)) != NULL); ++oso_idx) + { + if (skip_dwarf_oso != oso_dwarf && oso_dwarf->Supports_DW_AT_APPLE_objc_complete_type(NULL)) + { + m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes; + break; + } + } + } + return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes; +} + +TypeSP +SymbolFileDWARFDebugMap::FindCompleteObjCDefinitionTypeForDIE (const DWARFDebugInfoEntry *die, + const ConstString &type_name, + bool must_be_implementation) +{ + TypeSP type_sp; + SymbolFileDWARF *oso_dwarf; + for (uint32_t oso_idx = 0; ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx)) != NULL); ++oso_idx) + { + type_sp = oso_dwarf->FindCompleteObjCDefinitionTypeForDIE (die, type_name, must_be_implementation); + if (type_sp) + break; + } + return type_sp; +} + +uint32_t +SymbolFileDWARFDebugMap::FindTypes +( + const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + uint32_t max_matches, + TypeList& types +) +{ + if (!append) + types.Clear(); + + const uint32_t initial_types_size = types.GetSize(); + SymbolFileDWARF *oso_dwarf; + + if (sc.comp_unit) + { + oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + return oso_dwarf->FindTypes (sc, name, namespace_decl, append, max_matches, types); + } + else + { + uint32_t oso_idx = 0; + while ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx++)) != NULL) + oso_dwarf->FindTypes (sc, name, namespace_decl, append, max_matches, types); + } + + return types.GetSize() - initial_types_size; +} + +// +//uint32_t +//SymbolFileDWARFDebugMap::FindTypes (const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, Type::Encoding encoding, lldb::user_id_t udt_uid, TypeList& types) +//{ +// SymbolFileDWARF *oso_dwarf = GetSymbolFile (sc); +// if (oso_dwarf) +// return oso_dwarf->FindTypes (sc, regex, append, max_matches, encoding, udt_uid, types); +// return 0; +//} + + +ClangNamespaceDecl +SymbolFileDWARFDebugMap::FindNamespace (const lldb_private::SymbolContext& sc, + const lldb_private::ConstString &name, + const ClangNamespaceDecl *parent_namespace_decl) +{ + ClangNamespaceDecl matching_namespace; + SymbolFileDWARF *oso_dwarf; + + if (sc.comp_unit) + { + oso_dwarf = GetSymbolFile (sc); + if (oso_dwarf) + matching_namespace = oso_dwarf->FindNamespace (sc, name, parent_namespace_decl); + } + else + { + for (uint32_t oso_idx = 0; + ((oso_dwarf = GetSymbolFileByOSOIndex (oso_idx)) != NULL); + ++oso_idx) + { + matching_namespace = oso_dwarf->FindNamespace (sc, name, parent_namespace_decl); + + if (matching_namespace) + break; + } + } + + return matching_namespace; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +SymbolFileDWARFDebugMap::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileDWARFDebugMap::GetPluginVersion() +{ + return 1; +} + +lldb::CompUnitSP +SymbolFileDWARFDebugMap::GetCompileUnit (SymbolFileDWARF *oso_dwarf) +{ + if (oso_dwarf) + { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx=0; cu_idx<cu_count; ++cu_idx) + { + SymbolFileDWARF *oso_symfile = GetSymbolFileByCompUnitInfo (&m_compile_unit_infos[cu_idx]); + if (oso_symfile == oso_dwarf) + { + if (!m_compile_unit_infos[cu_idx].compile_unit_sp) + m_compile_unit_infos[cu_idx].compile_unit_sp = ParseCompileUnitAtIndex (cu_idx); + + return m_compile_unit_infos[cu_idx].compile_unit_sp; + } + } + } + assert(!"this shouldn't happen"); + return lldb::CompUnitSP(); +} + +SymbolFileDWARFDebugMap::CompileUnitInfo * +SymbolFileDWARFDebugMap::GetCompileUnitInfo (SymbolFileDWARF *oso_dwarf) +{ + if (oso_dwarf) + { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx=0; cu_idx<cu_count; ++cu_idx) + { + SymbolFileDWARF *oso_symfile = GetSymbolFileByCompUnitInfo (&m_compile_unit_infos[cu_idx]); + if (oso_symfile == oso_dwarf) + { + return &m_compile_unit_infos[cu_idx]; + } + } + } + return NULL; +} + + +void +SymbolFileDWARFDebugMap::SetCompileUnit (SymbolFileDWARF *oso_dwarf, const CompUnitSP &cu_sp) +{ + if (oso_dwarf) + { + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx=0; cu_idx<cu_count; ++cu_idx) + { + SymbolFileDWARF *oso_symfile = GetSymbolFileByCompUnitInfo (&m_compile_unit_infos[cu_idx]); + if (oso_symfile == oso_dwarf) + { + if (m_compile_unit_infos[cu_idx].compile_unit_sp) + { + assert (m_compile_unit_infos[cu_idx].compile_unit_sp.get() == cu_sp.get()); + } + else + { + m_compile_unit_infos[cu_idx].compile_unit_sp = cu_sp; + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(cu_idx, cu_sp); + } + } + } + } +} + + +void +SymbolFileDWARFDebugMap::CompleteTagDecl (void *baton, clang::TagDecl *decl) +{ + SymbolFileDWARFDebugMap *symbol_file_dwarf = (SymbolFileDWARFDebugMap *)baton; + ClangASTType clang_type = symbol_file_dwarf->GetClangASTContext().GetTypeForDecl (decl); + if (clang_type) + { + SymbolFileDWARF *oso_dwarf; + + for (uint32_t oso_idx = 0; ((oso_dwarf = symbol_file_dwarf->GetSymbolFileByOSOIndex (oso_idx)) != NULL); ++oso_idx) + { + if (oso_dwarf->HasForwardDeclForClangType (clang_type)) + { + oso_dwarf->ResolveClangOpaqueTypeDefinition (clang_type); + return; + } + } + } +} + +void +SymbolFileDWARFDebugMap::CompleteObjCInterfaceDecl (void *baton, clang::ObjCInterfaceDecl *decl) +{ + SymbolFileDWARFDebugMap *symbol_file_dwarf = (SymbolFileDWARFDebugMap *)baton; + ClangASTType clang_type = symbol_file_dwarf->GetClangASTContext().GetTypeForDecl (decl); + if (clang_type) + { + SymbolFileDWARF *oso_dwarf; + + for (uint32_t oso_idx = 0; ((oso_dwarf = symbol_file_dwarf->GetSymbolFileByOSOIndex (oso_idx)) != NULL); ++oso_idx) + { + if (oso_dwarf->HasForwardDeclForClangType (clang_type)) + { + oso_dwarf->ResolveClangOpaqueTypeDefinition (clang_type); + return; + } + } + } +} + +bool +SymbolFileDWARFDebugMap::LayoutRecordType (void *baton, + const clang::RecordDecl *record_decl, + uint64_t &size, + uint64_t &alignment, + llvm::DenseMap <const clang::FieldDecl *, uint64_t> &field_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &base_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &vbase_offsets) +{ + SymbolFileDWARFDebugMap *symbol_file_dwarf = (SymbolFileDWARFDebugMap *)baton; + SymbolFileDWARF *oso_dwarf; + for (uint32_t oso_idx = 0; ((oso_dwarf = symbol_file_dwarf->GetSymbolFileByOSOIndex (oso_idx)) != NULL); ++oso_idx) + { + if (oso_dwarf->LayoutRecordType (record_decl, size, alignment, field_offsets, base_offsets, vbase_offsets)) + return true; + } + return false; +} + + + +clang::DeclContext* +SymbolFileDWARFDebugMap::GetClangDeclContextContainingTypeUID (lldb::user_id_t type_uid) +{ + const uint64_t oso_idx = GetOSOIndexFromUserID (type_uid); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + if (oso_dwarf) + return oso_dwarf->GetClangDeclContextContainingTypeUID (type_uid); + return NULL; +} + +clang::DeclContext* +SymbolFileDWARFDebugMap::GetClangDeclContextForTypeUID (const lldb_private::SymbolContext &sc, lldb::user_id_t type_uid) +{ + const uint64_t oso_idx = GetOSOIndexFromUserID (type_uid); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex (oso_idx); + if (oso_dwarf) + return oso_dwarf->GetClangDeclContextForTypeUID (sc, type_uid); + return NULL; +} + +bool +SymbolFileDWARFDebugMap::AddOSOFileRange (CompileUnitInfo *cu_info, + lldb::addr_t exe_file_addr, + lldb::addr_t oso_file_addr, + lldb::addr_t oso_byte_size) +{ + const uint32_t debug_map_idx = m_debug_map.FindEntryIndexThatContains(exe_file_addr); + if (debug_map_idx != UINT32_MAX) + { + DebugMap::Entry *debug_map_entry = m_debug_map.FindEntryThatContains(exe_file_addr); + debug_map_entry->data.SetOSOFileAddress(oso_file_addr); + cu_info->file_range_map.Append(FileRangeMap::Entry(oso_file_addr, oso_byte_size, exe_file_addr)); + return true; + } + return false; +} + +void +SymbolFileDWARFDebugMap::FinalizeOSOFileRanges (CompileUnitInfo *cu_info) +{ + cu_info->file_range_map.Sort(); +#if defined(DEBUG_OSO_DMAP) + const FileRangeMap &oso_file_range_map = cu_info->GetFileRangeMap(this); + const size_t n = oso_file_range_map.GetSize(); + printf ("SymbolFileDWARFDebugMap::FinalizeOSOFileRanges (cu_info = %p) %s\n", + cu_info, + cu_info->oso_sp->module_sp->GetFileSpec().GetPath().c_str()); + for (size_t i=0; i<n; ++i) + { + const FileRangeMap::Entry &entry = oso_file_range_map.GetEntryRef(i); + printf ("oso [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ") ==> exe [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")\n", + entry.GetRangeBase(), entry.GetRangeEnd(), + entry.data, entry.data + entry.GetByteSize()); + } +#endif +} + +lldb::addr_t +SymbolFileDWARFDebugMap::LinkOSOFileAddress (SymbolFileDWARF *oso_symfile, lldb::addr_t oso_file_addr) +{ + CompileUnitInfo *cu_info = GetCompileUnitInfo (oso_symfile); + if (cu_info) + { + const FileRangeMap::Entry *oso_range_entry = cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr); + if (oso_range_entry) + { + const DebugMap::Entry *debug_map_entry = m_debug_map.FindEntryThatContains(oso_range_entry->data); + if (debug_map_entry) + { + const lldb::addr_t offset = oso_file_addr - oso_range_entry->GetRangeBase(); + const lldb::addr_t exe_file_addr = debug_map_entry->GetRangeBase() + offset; + return exe_file_addr; + } + } + } + return LLDB_INVALID_ADDRESS; +} + +bool +SymbolFileDWARFDebugMap::LinkOSOAddress (Address &addr) +{ + // Make sure this address hasn't been fixed already + Module *exe_module = GetObjectFile()->GetModule().get(); + Module *addr_module = addr.GetModule().get(); + if (addr_module == exe_module) + return true; // Address is already in terms of the main executable module + + CompileUnitInfo *cu_info = GetCompileUnitInfo (GetSymbolFileAsSymbolFileDWARF(addr_module->GetSymbolVendor()->GetSymbolFile())); + if (cu_info) + { + const lldb::addr_t oso_file_addr = addr.GetFileAddress(); + const FileRangeMap::Entry *oso_range_entry = cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr); + if (oso_range_entry) + { + const DebugMap::Entry *debug_map_entry = m_debug_map.FindEntryThatContains(oso_range_entry->data); + if (debug_map_entry) + { + const lldb::addr_t offset = oso_file_addr - oso_range_entry->GetRangeBase(); + const lldb::addr_t exe_file_addr = debug_map_entry->GetRangeBase() + offset; + return exe_module->ResolveFileAddress(exe_file_addr, addr); + } + } + } + return true; +} + +LineTable * +SymbolFileDWARFDebugMap::LinkOSOLineTable (SymbolFileDWARF *oso_dwarf, LineTable *line_table) +{ + CompileUnitInfo *cu_info = GetCompileUnitInfo (oso_dwarf); + if (cu_info) + return line_table->LinkLineTable(cu_info->GetFileRangeMap(this)); + return NULL; +} + +size_t +SymbolFileDWARFDebugMap::AddOSOARanges (SymbolFileDWARF* dwarf2Data, DWARFDebugAranges* debug_aranges) +{ + size_t num_line_entries_added = 0; + if (debug_aranges && dwarf2Data) + { + CompileUnitInfo *compile_unit_info = GetCompileUnitInfo(dwarf2Data); + if (compile_unit_info) + { + const FileRangeMap &file_range_map = compile_unit_info->GetFileRangeMap(this); + for (size_t idx = 0; + idx < file_range_map.GetSize(); + idx++) + { + const FileRangeMap::Entry* entry = file_range_map.GetEntryAtIndex(idx); + if (entry) + { + printf ("[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")\n", entry->GetRangeBase(), entry->GetRangeEnd()); + debug_aranges->AppendRange(dwarf2Data->GetID(), entry->GetRangeBase(), entry->GetRangeEnd()); + num_line_entries_added++; + } + } + } + } + return num_line_entries_added; +} + diff --git a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h new file mode 100644 index 000000000000..06330b98dc19 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -0,0 +1,420 @@ +//===-- SymbolFileDWARFDebugMap.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef SymbolFileDWARF_SymbolFileDWARFDebugMap_h_ +#define SymbolFileDWARF_SymbolFileDWARFDebugMap_h_ + + +#include <vector> +#include <bitset> + +#include "clang/AST/CharUnits.h" + +#include "lldb/Core/RangeMap.h" +#include "lldb/Symbol/SymbolFile.h" + +#include "UniqueDWARFASTType.h" + +class SymbolFileDWARF; +class DWARFCompileUnit; +class DWARFDebugAranges; +class DWARFDebugInfoEntry; +class DWARFDeclContext; +class DebugMapModule; + +class SymbolFileDWARFDebugMap : public lldb_private::SymbolFile +{ +public: + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile * + CreateInstance (lldb_private::ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileDWARFDebugMap (lldb_private::ObjectFile* ofile); + virtual ~ SymbolFileDWARFDebugMap (); + + virtual uint32_t CalculateAbilities (); + + virtual void InitializeObject(); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t GetNumCompileUnits (); + virtual lldb::CompUnitSP ParseCompileUnitAtIndex (uint32_t index); + + virtual lldb::LanguageType ParseCompileUnitLanguage (const lldb_private::SymbolContext& sc); + virtual size_t ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + virtual bool ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList &support_files); + virtual size_t ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + virtual size_t ParseTypes (const lldb_private::SymbolContext& sc); + virtual size_t ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* ResolveTypeUID (lldb::user_id_t type_uid); + virtual clang::DeclContext* GetClangDeclContextContainingTypeUID (lldb::user_id_t type_uid); + virtual clang::DeclContext* GetClangDeclContextForTypeUID (const lldb_private::SymbolContext &sc, lldb::user_id_t type_uid); + virtual bool ResolveClangOpaqueTypeDefinition (lldb_private::ClangASTType& clang_type); + virtual uint32_t ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + virtual uint32_t ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindGlobalVariables (const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindGlobalVariables (const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + virtual uint32_t FindFunctions (const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, uint32_t name_type_mask, bool include_inlines, bool append, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindFunctions (const lldb_private::RegularExpression& regex, bool include_inlines, bool append, lldb_private::SymbolContextList& sc_list); + virtual uint32_t FindTypes (const lldb_private::SymbolContext& sc, const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, lldb_private::TypeList& types); + virtual lldb_private::ClangNamespaceDecl + FindNamespace (const lldb_private::SymbolContext& sc, + const lldb_private::ConstString &name, + const lldb_private::ClangNamespaceDecl *parent_namespace_decl); + virtual size_t GetTypes (lldb_private::SymbolContextScope *sc_scope, + uint32_t type_mask, + lldb_private::TypeList &type_list); + + + //------------------------------------------------------------------ + // ClangASTContext callbacks for external source lookups. + //------------------------------------------------------------------ + static void + CompleteTagDecl (void *baton, clang::TagDecl *); + + static void + CompleteObjCInterfaceDecl (void *baton, clang::ObjCInterfaceDecl *); + + static bool + LayoutRecordType (void *baton, + const clang::RecordDecl *record_decl, + uint64_t &size, + uint64_t &alignment, + llvm::DenseMap <const clang::FieldDecl *, uint64_t> &field_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &base_offsets, + llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &vbase_offsets); + + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + enum + { + kHaveInitializedOSOs = (1 << 0), + kNumFlags + }; + + friend class DWARFCompileUnit; + friend class SymbolFileDWARF; + friend class DebugMapModule; + struct OSOInfo + { + lldb::ModuleSP module_sp; + + OSOInfo() : + module_sp () + { + } + }; + + typedef std::shared_ptr<OSOInfo> OSOInfoSP; + + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, lldb::addr_t> FileRangeMap; + + //------------------------------------------------------------------ + // Class specific types + //------------------------------------------------------------------ + struct CompileUnitInfo + { + lldb_private::FileSpec so_file; + lldb_private::ConstString oso_path; + lldb_private::TimeValue oso_mod_time; + OSOInfoSP oso_sp; + lldb::CompUnitSP compile_unit_sp; + uint32_t first_symbol_index; + uint32_t last_symbol_index; + uint32_t first_symbol_id; + uint32_t last_symbol_id; + FileRangeMap file_range_map; + bool file_range_map_valid; + + + CompileUnitInfo() : + so_file (), + oso_path (), + oso_mod_time (), + oso_sp (), + compile_unit_sp (), + first_symbol_index (UINT32_MAX), + last_symbol_index (UINT32_MAX), + first_symbol_id (UINT32_MAX), + last_symbol_id (UINT32_MAX), + file_range_map (), + file_range_map_valid (false) + { + } + + const FileRangeMap & + GetFileRangeMap(SymbolFileDWARFDebugMap *exe_symfile); + }; + + //------------------------------------------------------------------ + // Protected Member Functions + //------------------------------------------------------------------ + void + InitOSO (); + + static uint32_t + GetOSOIndexFromUserID (lldb::user_id_t uid) + { + return (uint32_t)((uid >> 32ull) - 1ull); + } + + static SymbolFileDWARF * + GetSymbolFileAsSymbolFileDWARF (SymbolFile *sym_file); + + bool + GetFileSpecForSO (uint32_t oso_idx, lldb_private::FileSpec &file_spec); + + CompileUnitInfo * + GetCompUnitInfo (const lldb_private::SymbolContext& sc); + + size_t + GetCompUnitInfosForModule (const lldb_private::Module *oso_module, + std::vector<CompileUnitInfo *>& cu_infos); + + lldb_private::Module * + GetModuleByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + lldb_private::Module * + GetModuleByOSOIndex (uint32_t oso_idx); + + lldb_private::ObjectFile * + GetObjectFileByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + lldb_private::ObjectFile * + GetObjectFileByOSOIndex (uint32_t oso_idx); + + uint32_t + GetCompUnitInfoIndex (const CompileUnitInfo *comp_unit_info); + + SymbolFileDWARF * + GetSymbolFile (const lldb_private::SymbolContext& sc); + + SymbolFileDWARF * + GetSymbolFileByCompUnitInfo (CompileUnitInfo *comp_unit_info); + + SymbolFileDWARF * + GetSymbolFileByOSOIndex (uint32_t oso_idx); + + CompileUnitInfo * + GetCompileUnitInfoForSymbolWithIndex (uint32_t symbol_idx, uint32_t *oso_idx_ptr); + + CompileUnitInfo * + GetCompileUnitInfoForSymbolWithID (lldb::user_id_t symbol_id, uint32_t *oso_idx_ptr); + + static int + SymbolContainsSymbolWithIndex (uint32_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info); + + static int + SymbolContainsSymbolWithID (lldb::user_id_t *symbol_idx_ptr, const CompileUnitInfo *comp_unit_info); + + uint32_t + PrivateFindGlobalVariables (const lldb_private::ConstString &name, + const lldb_private::ClangNamespaceDecl *namespace_decl, + const std::vector<uint32_t> &name_symbol_indexes, + uint32_t max_matches, + lldb_private::VariableList& variables); + + + void + SetCompileUnit (SymbolFileDWARF *oso_dwarf, const lldb::CompUnitSP &cu_sp); + + lldb::CompUnitSP + GetCompileUnit (SymbolFileDWARF *oso_dwarf); + + CompileUnitInfo * + GetCompileUnitInfo (SymbolFileDWARF *oso_dwarf); + + lldb::TypeSP + FindDefinitionTypeForDWARFDeclContext (const DWARFDeclContext &die_decl_ctx); + + bool + Supports_DW_AT_APPLE_objc_complete_type (SymbolFileDWARF *skip_dwarf_oso); + + lldb::TypeSP + FindCompleteObjCDefinitionTypeForDIE (const DWARFDebugInfoEntry *die, + const lldb_private::ConstString &type_name, + bool must_be_implementation); + + + UniqueDWARFASTTypeMap & + GetUniqueDWARFASTTypeMap () + { + return m_unique_ast_type_map; + } + + + //------------------------------------------------------------------ + // OSOEntry + //------------------------------------------------------------------ + class OSOEntry + { + public: + + OSOEntry () : + m_exe_sym_idx (UINT32_MAX), + m_oso_file_addr (LLDB_INVALID_ADDRESS) + { + } + + OSOEntry (uint32_t exe_sym_idx, + lldb::addr_t oso_file_addr) : + m_exe_sym_idx (exe_sym_idx), + m_oso_file_addr (oso_file_addr) + { + } + + uint32_t + GetExeSymbolIndex () const + { + return m_exe_sym_idx; + } + + bool + operator < (const OSOEntry &rhs) const + { + return m_exe_sym_idx < rhs.m_exe_sym_idx; + } + + lldb::addr_t + GetOSOFileAddress () const + { + return m_oso_file_addr; + } + + void + SetOSOFileAddress (lldb::addr_t oso_file_addr) + { + m_oso_file_addr = oso_file_addr; + } + protected: + uint32_t m_exe_sym_idx; + lldb::addr_t m_oso_file_addr; + }; + + typedef lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, OSOEntry> DebugMap; + + //------------------------------------------------------------------ + // Member Variables + //------------------------------------------------------------------ + std::bitset<kNumFlags> m_flags; + std::vector<CompileUnitInfo> m_compile_unit_infos; + std::vector<uint32_t> m_func_indexes; // Sorted by address + std::vector<uint32_t> m_glob_indexes; + std::map<lldb_private::ConstString, OSOInfoSP> m_oso_map; + UniqueDWARFASTTypeMap m_unique_ast_type_map; + lldb_private::LazyBool m_supports_DW_AT_APPLE_objc_complete_type; + DebugMap m_debug_map; + + //------------------------------------------------------------------ + // When an object file from the debug map gets parsed in + // SymbolFileDWARF, it needs to tell the debug map about the object + // files addresses by calling this function once for each N_FUN, + // N_GSYM and N_STSYM and after all entries in the debug map have + // been matched up, FinalizeOSOFileRanges() should be called. + //------------------------------------------------------------------ + bool + AddOSOFileRange (CompileUnitInfo *cu_info, + lldb::addr_t exe_file_addr, + lldb::addr_t oso_file_addr, + lldb::addr_t oso_byte_size); + + //------------------------------------------------------------------ + // Called after calling AddOSOFileRange() for each object file debug + // map entry to finalize the info for the unlinked compile unit. + //------------------------------------------------------------------ + void + FinalizeOSOFileRanges (CompileUnitInfo *cu_info); + + //------------------------------------------------------------------ + /// Convert \a addr from a .o file address, to an executable address. + /// + /// @param[in] addr + /// A section offset address from a .o file + /// + /// @return + /// Returns true if \a addr was converted to be an executable + /// section/offset address, false otherwise. + //------------------------------------------------------------------ + bool + LinkOSOAddress (lldb_private::Address &addr); + + //------------------------------------------------------------------ + /// Convert a .o file "file address" to an executable "file address". + /// + /// @param[in] oso_symfile + /// The DWARF symbol file that contains \a oso_file_addr + /// + /// @param[in] oso_file_addr + /// A .o file "file address" to convert. + /// + /// @return + /// LLDB_INVALID_ADDRESS if \a oso_file_addr is not in the + /// linked executable, otherwise a valid "file address" from the + /// linked executable that contains the debug map. + //------------------------------------------------------------------ + lldb::addr_t + LinkOSOFileAddress (SymbolFileDWARF *oso_symfile, lldb::addr_t oso_file_addr); + + //------------------------------------------------------------------ + /// Given a line table full of lines with "file adresses" that are + /// for a .o file represented by \a oso_symfile, link a new line table + /// and return it. + /// + /// @param[in] oso_symfile + /// The DWARF symbol file that produced the \a line_table + /// + /// @param[in] addr + /// A section offset address from a .o file + /// + /// @return + /// Returns a valid line table full of linked addresses, or NULL + /// if none of the line table adresses exist in the main + /// executable. + //------------------------------------------------------------------ + lldb_private::LineTable * + LinkOSOLineTable (SymbolFileDWARF *oso_symfile, + lldb_private::LineTable *line_table); + + size_t + AddOSOARanges (SymbolFileDWARF* dwarf2Data, + DWARFDebugAranges* debug_aranges); +}; + +#endif // #ifndef SymbolFileDWARF_SymbolFileDWARFDebugMap_h_ diff --git a/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp b/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp new file mode 100644 index 000000000000..94044c0feb30 --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp @@ -0,0 +1,94 @@ +//===-- UniqueDWARFASTType.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UniqueDWARFASTType.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Symbol/Declaration.h" + +#include "DWARFDebugInfoEntry.h" + +bool +UniqueDWARFASTTypeList::Find +( + SymbolFileDWARF *symfile, + const DWARFCompileUnit *cu, + const DWARFDebugInfoEntry *die, + const lldb_private::Declaration &decl, + const int32_t byte_size, + UniqueDWARFASTType &entry +) const +{ + collection::const_iterator pos, end = m_collection.end(); + for (pos = m_collection.begin(); pos != end; ++pos) + { + // Make sure the tags match + if (pos->m_die->Tag() == die->Tag()) + { + // Validate byte sizes of both types only if both are valid. + if (pos->m_byte_size < 0 || byte_size < 0 || pos->m_byte_size == byte_size) + { + // Make sure the file and line match + if (pos->m_declaration == decl) + { + // The type has the same name, and was defined on the same + // file and line. Now verify all of the parent DIEs match. + const DWARFDebugInfoEntry *parent_arg_die = die->GetParent(); + const DWARFDebugInfoEntry *parend_pos_die = pos->m_die->GetParent(); + bool match = true; + bool done = false; + while (!done && match && parent_arg_die && parend_pos_die) + { + if (parent_arg_die->Tag() == parend_pos_die->Tag()) + { + const dw_tag_t tag = parent_arg_die->Tag(); + switch (tag) + { + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_namespace: + { + const char *parent_arg_die_name = parent_arg_die->GetName(symfile, cu); + if (parent_arg_die_name == NULL) // Anonymous (i.e. no-name) struct + { + match = false; + } + else + { + const char *parent_pos_die_name = parend_pos_die->GetName(pos->m_symfile, pos->m_cu); + if (parent_pos_die_name == NULL || strcmp (parent_arg_die_name, parent_pos_die_name)) + match = false; + } + } + break; + + case DW_TAG_compile_unit: + done = true; + break; + } + } + parent_arg_die = parent_arg_die->GetParent(); + parend_pos_die = parend_pos_die->GetParent(); + } + + if (match) + { + entry = *pos; + return true; + } + } + } + } + } + return false; +} diff --git a/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h b/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h new file mode 100644 index 000000000000..c85e175235ca --- /dev/null +++ b/source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h @@ -0,0 +1,175 @@ +//===-- UniqueDWARFASTType.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UniqueDWARFASTType_h_ +#define lldb_UniqueDWARFASTType_h_ + +// C Includes +// C++ Includes +#include <vector> + +// Other libraries and framework includes +#include "llvm/ADT/DenseMap.h" + +// Project includes +#include "lldb/Symbol/Declaration.h" + +class DWARFCompileUnit; +class DWARFDebugInfoEntry; +class SymbolFileDWARF; + +class UniqueDWARFASTType +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + UniqueDWARFASTType () : + m_type_sp (), + m_symfile (NULL), + m_cu (NULL), + m_die (NULL), + m_declaration (), + m_byte_size (-1) // Set to negative value to make sure we have a valid value + { + } + + UniqueDWARFASTType (lldb::TypeSP &type_sp, + SymbolFileDWARF *symfile, + DWARFCompileUnit *cu, + DWARFDebugInfoEntry *die, + const lldb_private::Declaration &decl, + int32_t byte_size) : + m_type_sp (type_sp), + m_symfile (symfile), + m_cu (cu), + m_die (die), + m_declaration (decl), + m_byte_size (byte_size) + { + } + + UniqueDWARFASTType (const UniqueDWARFASTType &rhs) : + m_type_sp (rhs.m_type_sp), + m_symfile (rhs.m_symfile), + m_cu (rhs.m_cu), + m_die (rhs.m_die), + m_declaration (rhs.m_declaration), + m_byte_size (rhs.m_byte_size) + { + } + + ~UniqueDWARFASTType() + { + } + + UniqueDWARFASTType & + operator= (const UniqueDWARFASTType &rhs) + { + if (this != &rhs) + { + m_type_sp = rhs.m_type_sp; + m_symfile = rhs.m_symfile; + m_cu = rhs.m_cu; + m_die = rhs.m_die; + m_declaration = rhs.m_declaration; + m_byte_size = rhs.m_byte_size; + } + return *this; + } + + lldb::TypeSP m_type_sp; + SymbolFileDWARF *m_symfile; + const DWARFCompileUnit *m_cu; + const DWARFDebugInfoEntry *m_die; + lldb_private::Declaration m_declaration; + int32_t m_byte_size; +}; + +class UniqueDWARFASTTypeList +{ +public: + UniqueDWARFASTTypeList () : + m_collection() + { + } + + ~UniqueDWARFASTTypeList () + { + } + + uint32_t + GetSize() + { + return (uint32_t)m_collection.size(); + } + + void + Append (const UniqueDWARFASTType &entry) + { + m_collection.push_back (entry); + } + + bool + Find (SymbolFileDWARF *symfile, + const DWARFCompileUnit *cu, + const DWARFDebugInfoEntry *die, + const lldb_private::Declaration &decl, + const int32_t byte_size, + UniqueDWARFASTType &entry) const; + +protected: + typedef std::vector<UniqueDWARFASTType> collection; + collection m_collection; +}; + +class UniqueDWARFASTTypeMap +{ +public: + UniqueDWARFASTTypeMap () : + m_collection () + { + } + + ~UniqueDWARFASTTypeMap () + { + } + + void + Insert (const lldb_private::ConstString &name, + const UniqueDWARFASTType &entry) + { + m_collection[name.GetCString()].Append (entry); + } + + bool + Find (const lldb_private::ConstString &name, + SymbolFileDWARF *symfile, + const DWARFCompileUnit *cu, + const DWARFDebugInfoEntry *die, + const lldb_private::Declaration &decl, + const int32_t byte_size, + UniqueDWARFASTType &entry) const + { + const char *unique_name_cstr = name.GetCString(); + collection::const_iterator pos = m_collection.find (unique_name_cstr); + if (pos != m_collection.end()) + { + return pos->second.Find (symfile, cu, die, decl, byte_size, entry); + } + return false; + } + +protected: + // A unique name string should be used + typedef llvm::DenseMap<const char *, UniqueDWARFASTTypeList> collection; + collection m_collection; +}; + +#endif // lldb_UniqueDWARFASTType_h_ diff --git a/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp b/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp new file mode 100644 index 000000000000..9beba517ec83 --- /dev/null +++ b/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp @@ -0,0 +1,407 @@ +//===-- SymbolFileSymtab.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolFileSymtab.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/TypeList.h" + +using namespace lldb; +using namespace lldb_private; + +void +SymbolFileSymtab::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolFileSymtab::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +SymbolFileSymtab::GetPluginNameStatic() +{ + static ConstString g_name("symtab"); + return g_name; +} + +const char * +SymbolFileSymtab::GetPluginDescriptionStatic() +{ + return "Reads debug symbols from an object file's symbol table."; +} + + +SymbolFile* +SymbolFileSymtab::CreateInstance (ObjectFile* obj_file) +{ + return new SymbolFileSymtab(obj_file); +} + +size_t +SymbolFileSymtab::GetTypes (SymbolContextScope *sc_scope, uint32_t type_mask, lldb_private::TypeList &type_list) +{ + return 0; +} + +SymbolFileSymtab::SymbolFileSymtab(ObjectFile* obj_file) : + SymbolFile(obj_file), + m_source_indexes(), + m_func_indexes(), + m_code_indexes(), + m_objc_class_name_to_index () +{ +} + +SymbolFileSymtab::~SymbolFileSymtab() +{ +} + +ClangASTContext & +SymbolFileSymtab::GetClangASTContext () +{ + ClangASTContext &ast = m_obj_file->GetModule()->GetClangASTContext(); + + return ast; +} + +uint32_t +SymbolFileSymtab::CalculateAbilities () +{ + uint32_t abilities = 0; + if (m_obj_file) + { + const Symtab *symtab = m_obj_file->GetSymtab(); + if (symtab) + { + //---------------------------------------------------------------------- + // The snippet of code below will get the indexes the module symbol + // table entries that are code, data, or function related (debug info), + // sort them by value (address) and dump the sorted symbols. + //---------------------------------------------------------------------- + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeSourceFile, m_source_indexes)) + { + abilities |= CompileUnits; + } + + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugYes, Symtab::eVisibilityAny, m_func_indexes)) + { + symtab->SortSymbolIndexesByValue(m_func_indexes, true); + abilities |= Functions; + } + + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeCode, Symtab::eDebugNo, Symtab::eVisibilityAny, m_code_indexes)) + { + symtab->SortSymbolIndexesByValue(m_code_indexes, true); + } + + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeData, m_data_indexes)) + { + symtab->SortSymbolIndexesByValue(m_data_indexes, true); + abilities |= GlobalVariables; + } + + lldb_private::Symtab::IndexCollection objc_class_indexes; + if (symtab->AppendSymbolIndexesWithType (eSymbolTypeObjCClass, objc_class_indexes)) + { + symtab->AppendSymbolNamesToMap (objc_class_indexes, + true, + true, + m_objc_class_name_to_index); + m_objc_class_name_to_index.Sort(); + } + } + } + return abilities; +} + +uint32_t +SymbolFileSymtab::GetNumCompileUnits() +{ + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (m_source_indexes.empty()) + return 0; + + // If we have any source file symbols we will logically orgnize the object symbols + // using these. + return m_source_indexes.size(); +} + +CompUnitSP +SymbolFileSymtab::ParseCompileUnitAtIndex(uint32_t idx) +{ + CompUnitSP cu_sp; + + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (idx < m_source_indexes.size()) + { + const Symbol *cu_symbol = m_obj_file->GetSymtab()->SymbolAtIndex(m_source_indexes[idx]); + if (cu_symbol) + cu_sp.reset(new CompileUnit (m_obj_file->GetModule(), NULL, cu_symbol->GetMangled().GetName().AsCString(), 0, eLanguageTypeUnknown)); + } + return cu_sp; +} + +lldb::LanguageType +SymbolFileSymtab::ParseCompileUnitLanguage (const SymbolContext& sc) +{ + return eLanguageTypeUnknown; +} + + +size_t +SymbolFileSymtab::ParseCompileUnitFunctions (const SymbolContext &sc) +{ + size_t num_added = 0; + // We must at least have a valid compile unit + assert (sc.comp_unit != NULL); + const Symtab *symtab = m_obj_file->GetSymtab(); + const Symbol *curr_symbol = NULL; + const Symbol *next_symbol = NULL; +// const char *prefix = m_obj_file->SymbolPrefix(); +// if (prefix == NULL) +// prefix == ""; +// +// const uint32_t prefix_len = strlen(prefix); + + // If we don't have any source file symbols we will just have one compile unit for + // the entire object file + if (m_source_indexes.empty()) + { + // The only time we will have a user ID of zero is when we don't have + // and source file symbols and we declare one compile unit for the + // entire object file + if (!m_func_indexes.empty()) + { + + } + + if (!m_code_indexes.empty()) + { +// StreamFile s(stdout); +// symtab->Dump(&s, m_code_indexes); + + uint32_t idx = 0; // Index into the indexes + const uint32_t num_indexes = m_code_indexes.size(); + for (idx = 0; idx < num_indexes; ++idx) + { + uint32_t symbol_idx = m_code_indexes[idx]; + curr_symbol = symtab->SymbolAtIndex(symbol_idx); + if (curr_symbol) + { + // Union of all ranges in the function DIE (if the function is discontiguous) + AddressRange func_range(curr_symbol->GetAddress(), 0); + if (func_range.GetBaseAddress().IsSectionOffset()) + { + uint32_t symbol_size = curr_symbol->GetByteSize(); + if (symbol_size != 0 && !curr_symbol->GetSizeIsSibling()) + func_range.SetByteSize(symbol_size); + else if (idx + 1 < num_indexes) + { + next_symbol = symtab->SymbolAtIndex(m_code_indexes[idx + 1]); + if (next_symbol) + { + func_range.SetByteSize(next_symbol->GetAddress().GetOffset() - curr_symbol->GetAddress().GetOffset()); + } + } + + FunctionSP func_sp(new Function(sc.comp_unit, + symbol_idx, // UserID is the DIE offset + LLDB_INVALID_UID, // We don't have any type info for this function + curr_symbol->GetMangled(), // Linker/mangled name + NULL, // no return type for a code symbol... + func_range)); // first address range + + if (func_sp.get() != NULL) + { + sc.comp_unit->AddFunction(func_sp); + ++num_added; + } + } + } + } + + } + } + else + { + // We assume we + } + return num_added; +} + +bool +SymbolFileSymtab::ParseCompileUnitLineTable (const SymbolContext &sc) +{ + return false; +} + +bool +SymbolFileSymtab::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList &support_files) +{ + return false; +} + +size_t +SymbolFileSymtab::ParseFunctionBlocks (const SymbolContext &sc) +{ + return 0; +} + + +size_t +SymbolFileSymtab::ParseTypes (const SymbolContext &sc) +{ + return 0; +} + + +size_t +SymbolFileSymtab::ParseVariablesForContext (const SymbolContext& sc) +{ + return 0; +} + +Type* +SymbolFileSymtab::ResolveTypeUID(lldb::user_id_t type_uid) +{ + return NULL; +} + +bool +SymbolFileSymtab::ResolveClangOpaqueTypeDefinition (lldb_private::ClangASTType& clang_opaque_type) +{ + return false; +} + +ClangNamespaceDecl +SymbolFileSymtab::FindNamespace (const SymbolContext& sc, const ConstString &name, const ClangNamespaceDecl *namespace_decl) +{ + return ClangNamespaceDecl(); +} + +uint32_t +SymbolFileSymtab::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +{ + if (m_obj_file->GetSymtab() == NULL) + return 0; + + uint32_t resolved_flags = 0; + if (resolve_scope & eSymbolContextSymbol) + { + sc.symbol = m_obj_file->GetSymtab()->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + return resolved_flags; +} + +uint32_t +SymbolFileSymtab::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindGlobalVariables(const ConstString &name, const ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, VariableList& variables) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindGlobalVariables(const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) +{ + return 0; +} + +uint32_t +SymbolFileSymtab::FindFunctions(const ConstString &name, const ClangNamespaceDecl *namespace_decl, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileSymtab::FindFunctions (name = '%s')", + name.GetCString()); + // If we ever support finding STABS or COFF debug info symbols, + // we will need to add support here. We are not trying to find symbols + // here, just "lldb_private::Function" objects that come from complete + // debug information. Any symbol queries should go through the symbol + // table itself in the module's object file. + return 0; +} + +uint32_t +SymbolFileSymtab::FindFunctions(const RegularExpression& regex, bool include_inlines, bool append, SymbolContextList& sc_list) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolFileSymtab::FindFunctions (regex = '%s')", + regex.GetText()); + // If we ever support finding STABS or COFF debug info symbols, + // we will need to add support here. We are not trying to find symbols + // here, just "lldb_private::Function" objects that come from complete + // debug information. Any symbol queries should go through the symbol + // table itself in the module's object file. + return 0; +} + +static int CountMethodArgs(const char *method_signature) +{ + int num_args = 0; + + for (const char *colon_pos = strchr(method_signature, ':'); + colon_pos != NULL; + colon_pos = strchr(colon_pos + 1, ':')) + { + num_args++; + } + + return num_args; +} + +uint32_t +SymbolFileSymtab::FindTypes (const lldb_private::SymbolContext& sc, + const lldb_private::ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + uint32_t max_matches, + lldb_private::TypeList& types) +{ + return 0; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +SymbolFileSymtab::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolFileSymtab::GetPluginVersion() +{ + return 1; +} diff --git a/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h b/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h new file mode 100644 index 000000000000..914efe6eb3c2 --- /dev/null +++ b/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h @@ -0,0 +1,142 @@ +//===-- SymbolFileSymtab.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolFileSymtab_h_ +#define liblldb_SymbolFileSymtab_h_ + +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/Symtab.h" +#include <vector> + +class SymbolFileSymtab : public lldb_private::SymbolFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolFile* + CreateInstance (lldb_private::ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFileSymtab(lldb_private::ObjectFile* obj_file); + + virtual + ~SymbolFileSymtab(); + + virtual uint32_t CalculateAbilities (); + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + virtual uint32_t + GetNumCompileUnits(); + + virtual lldb::CompUnitSP + ParseCompileUnitAtIndex(uint32_t index); + + virtual lldb::LanguageType + ParseCompileUnitLanguage (const lldb_private::SymbolContext& sc); + + virtual size_t + ParseCompileUnitFunctions (const lldb_private::SymbolContext& sc); + + virtual bool + ParseCompileUnitLineTable (const lldb_private::SymbolContext& sc); + + virtual bool + ParseCompileUnitSupportFiles (const lldb_private::SymbolContext& sc, lldb_private::FileSpecList &support_files); + + virtual size_t + ParseFunctionBlocks (const lldb_private::SymbolContext& sc); + + virtual size_t + ParseTypes (const lldb_private::SymbolContext& sc); + + virtual size_t + ParseVariablesForContext (const lldb_private::SymbolContext& sc); + + virtual lldb_private::Type* + ResolveTypeUID(lldb::user_id_t type_uid); + + virtual bool + ResolveClangOpaqueTypeDefinition (lldb_private::ClangASTType& clang_type); + + virtual uint32_t + ResolveSymbolContext (const lldb_private::Address& so_addr, uint32_t resolve_scope, lldb_private::SymbolContext& sc); + + virtual uint32_t + ResolveSymbolContext (const lldb_private::FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, lldb_private::SymbolContextList& sc_list); + + virtual uint32_t + FindGlobalVariables(const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + + virtual uint32_t + FindGlobalVariables(const lldb_private::RegularExpression& regex, bool append, uint32_t max_matches, lldb_private::VariableList& variables); + + virtual uint32_t + FindFunctions(const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, uint32_t name_type_mask, bool include_inlines, bool append, lldb_private::SymbolContextList& sc_list); + + virtual uint32_t + FindFunctions(const lldb_private::RegularExpression& regex, bool include_inlines, bool append, lldb_private::SymbolContextList& sc_list); + + virtual uint32_t + FindTypes (const lldb_private::SymbolContext& sc,const lldb_private::ConstString &name, const lldb_private::ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, lldb_private::TypeList& types); + + virtual size_t + GetTypes (lldb_private::SymbolContextScope *sc_scope, + uint32_t type_mask, + lldb_private::TypeList &type_list); + + virtual lldb_private::ClangNamespaceDecl + FindNamespace (const lldb_private::SymbolContext& sc, + const lldb_private::ConstString &name, + const lldb_private::ClangNamespaceDecl *parent_namespace_decl); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + typedef std::map<lldb_private::ConstString, lldb::TypeSP> TypeMap; + + lldb_private::Symtab::IndexCollection m_source_indexes; + lldb_private::Symtab::IndexCollection m_func_indexes; + lldb_private::Symtab::IndexCollection m_code_indexes; + lldb_private::Symtab::IndexCollection m_data_indexes; + lldb_private::Symtab::NameToIndexMap m_objc_class_name_to_index; + TypeMap m_objc_class_types; + + lldb_private::ClangASTContext & + GetClangASTContext (); + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolFileSymtab); +}; + + +#endif // liblldb_SymbolFileSymtab_h_ diff --git a/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp b/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp new file mode 100644 index 000000000000..6500aabdcea3 --- /dev/null +++ b/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp @@ -0,0 +1,199 @@ +//===-- SymbolVendorELF.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolVendorELF.h" + +//#include <libxml/parser.h> +// #include <libxml/tree.h> +#include <string.h> + +// #include <AvailabilityMacros.h> + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Symbol/ObjectFile.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SymbolVendorELF constructor +//---------------------------------------------------------------------- +SymbolVendorELF::SymbolVendorELF(const lldb::ModuleSP &module_sp) : + SymbolVendor (module_sp) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SymbolVendorELF::~SymbolVendorELF() +{ +} + +void +SymbolVendorELF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +SymbolVendorELF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +SymbolVendorELF::GetPluginNameStatic() +{ + static ConstString g_name("ELF"); + return g_name; +} + +const char * +SymbolVendorELF::GetPluginDescriptionStatic() +{ + return "Symbol vendor for ELF that looks for dSYM files that match executables."; +} + + + +//---------------------------------------------------------------------- +// CreateInstance +// +// Platforms can register a callback to use when creating symbol +// vendors to allow for complex debug information file setups, and to +// also allow for finding separate debug information files. +//---------------------------------------------------------------------- +SymbolVendor* +SymbolVendorELF::CreateInstance (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm) +{ + if (!module_sp) + return NULL; + + ObjectFile *obj_file = module_sp->GetObjectFile(); + if (!obj_file) + return NULL; + + static ConstString obj_file_elf("elf"); + ConstString obj_name = obj_file->GetPluginName(); + if (obj_name != obj_file_elf) + return NULL; + + lldb_private::UUID uuid; + if (!obj_file->GetUUID (&uuid)) + return NULL; + + // Get the .gnu_debuglink file (if specified). + FileSpecList file_spec_list = obj_file->GetDebugSymbolFilePaths(); + + // If the module specified a filespec, use it first. + FileSpec debug_symbol_fspec (module_sp->GetSymbolFileFileSpec()); + if (debug_symbol_fspec) + file_spec_list.Insert (0, debug_symbol_fspec); + + // If we have no debug symbol files, then nothing to do. + if (file_spec_list.IsEmpty()) + return NULL; + + Timer scoped_timer (__PRETTY_FUNCTION__, + "SymbolVendorELF::CreateInstance (module = %s)", + module_sp->GetFileSpec().GetPath().c_str()); + + for (size_t idx = 0; idx < file_spec_list.GetSize(); ++idx) + { + ModuleSpec module_spec; + const FileSpec fspec = file_spec_list.GetFileSpecAtIndex (idx); + + module_spec.GetFileSpec() = obj_file->GetFileSpec(); + module_spec.GetFileSpec().ResolvePath(); + module_spec.GetSymbolFileSpec() = fspec; + module_spec.GetUUID() = uuid; + FileSpec dsym_fspec = Symbols::LocateExecutableSymbolFile (module_spec); + if (dsym_fspec) + { + DataBufferSP dsym_file_data_sp; + lldb::offset_t dsym_file_data_offset = 0; + ObjectFileSP dsym_objfile_sp = ObjectFile::FindPlugin(module_sp, &dsym_fspec, 0, dsym_fspec.GetByteSize(), dsym_file_data_sp, dsym_file_data_offset); + if (dsym_objfile_sp) + { + // This objfile is for debugging purposes. Sadly, ObjectFileELF won't be able + // to figure this out consistently as the symbol file may not have stripped the + // code sections, etc. + dsym_objfile_sp->SetType (ObjectFile::eTypeDebugInfo); + + SymbolVendorELF* symbol_vendor = new SymbolVendorELF(module_sp); + if (symbol_vendor) + { + // Get the module unified section list and add our debug sections to that. + SectionList *module_section_list = module_sp->GetSectionList(); + SectionList *objfile_section_list = dsym_objfile_sp->GetSectionList(); + + static const SectionType g_sections[] = + { + eSectionTypeDWARFDebugAranges, + eSectionTypeDWARFDebugInfo, + eSectionTypeDWARFDebugAbbrev, + eSectionTypeDWARFDebugFrame, + eSectionTypeDWARFDebugLine, + eSectionTypeDWARFDebugStr, + eSectionTypeDWARFDebugLoc, + eSectionTypeDWARFDebugMacInfo, + eSectionTypeDWARFDebugPubNames, + eSectionTypeDWARFDebugPubTypes, + eSectionTypeDWARFDebugRanges, + eSectionTypeELFSymbolTable, + }; + for (size_t idx = 0; idx < sizeof(g_sections) / sizeof(g_sections[0]); ++idx) + { + SectionType section_type = g_sections[idx]; + SectionSP section_sp (objfile_section_list->FindSectionByType (section_type, true)); + if (section_sp) + { + SectionSP module_section_sp (module_section_list->FindSectionByType (section_type, true)); + if (module_section_sp) + module_section_list->ReplaceSection (module_section_sp->GetID(), section_sp); + else + module_section_list->AddSection (section_sp); + } + } + + symbol_vendor->AddSymbolFileRepresentation (dsym_objfile_sp); + return symbol_vendor; + } + } + } + } + return NULL; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +SymbolVendorELF::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +SymbolVendorELF::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h b/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h new file mode 100644 index 000000000000..acd62b6cc3ab --- /dev/null +++ b/source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h @@ -0,0 +1,58 @@ +//===-- SymbolVendorELF.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolVendorELF_h_ +#define liblldb_SymbolVendorELF_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/SymbolVendor.h" + +class SymbolVendorELF : public lldb_private::SymbolVendor +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::SymbolVendor* + CreateInstance (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolVendorELF (const lldb::ModuleSP &module_sp); + + virtual + ~SymbolVendorELF(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +private: + DISALLOW_COPY_AND_ASSIGN (SymbolVendorELF); +}; + +#endif // liblldb_SymbolVendorELF_h_ diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp new file mode 100644 index 000000000000..c93dea9b5d3c --- /dev/null +++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp @@ -0,0 +1,670 @@ +//===-- UnwindAssemblyInstEmulation.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UnwindAssemblyInstEmulation.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + + +//----------------------------------------------------------------------------------------------- +// UnwindAssemblyInstEmulation method definitions +//----------------------------------------------------------------------------------------------- + +bool +UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& range, + Thread& thread, + UnwindPlan& unwind_plan) +{ + if (range.GetByteSize() > 0 && + range.GetBaseAddress().IsValid() && + m_inst_emulator_ap.get()) + { + + // The the instruction emulation subclass setup the unwind plan for the + // first instruction. + m_inst_emulator_ap->CreateFunctionEntryUnwind (unwind_plan); + + // CreateFunctionEntryUnwind should have created the first row. If it + // doesn't, then we are done. + if (unwind_plan.GetRowCount() == 0) + return false; + + ExecutionContext exe_ctx; + thread.CalculateExecutionContext(exe_ctx); + DisassemblerSP disasm_sp (Disassembler::DisassembleRange (m_arch, + NULL, + NULL, + exe_ctx, + range)); + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + + if (disasm_sp) + { + + m_range_ptr = ⦥ + m_thread_ptr = &thread; + m_unwind_plan_ptr = &unwind_plan; + + const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); + const bool show_address = true; + const bool show_bytes = true; + m_inst_emulator_ap->GetRegisterInfo (unwind_plan.GetRegisterKind(), + unwind_plan.GetInitialCFARegister(), + m_cfa_reg_info); + + m_fp_is_cfa = false; + m_register_values.clear(); + m_pushed_regs.clear(); + + // Initialize the CFA with a known value. In the 32 bit case + // it will be 0x80000000, and in the 64 bit case 0x8000000000000000. + // We use the address byte size to be safe for any future addresss sizes + m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); + RegisterValue cfa_reg_value; + cfa_reg_value.SetUInt (m_initial_sp, m_cfa_reg_info.byte_size); + SetRegisterValue (m_cfa_reg_info, cfa_reg_value); + + const InstructionList &inst_list = disasm_sp->GetInstructionList (); + const size_t num_instructions = inst_list.GetSize(); + + if (num_instructions > 0) + { + Instruction *inst = inst_list.GetInstructionAtIndex (0).get(); + const addr_t base_addr = inst->GetAddress().GetFileAddress(); + + // Make a copy of the current instruction Row and save it in m_curr_row + // so we can add updates as we process the instructions. + UnwindPlan::RowSP last_row = unwind_plan.GetLastRow(); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + if (last_row.get()) + *newrow = *last_row.get(); + m_curr_row.reset(newrow); + + // Once we've seen the initial prologue instructions complete, save a + // copy of the CFI at that point into prologue_completed_row for possible + // use later. + int instructions_since_last_prologue_insn = 0; // # of insns since last CFI was update + + bool reinstate_prologue_next_instruction = false; // Next iteration, re-install the prologue row of CFI + + bool last_instruction_restored_return_addr_reg = false; // re-install the prologue row of CFI if the next instruction is a branch immediate + + bool return_address_register_has_been_saved = false; // if we've seen the ra register get saved yet + + UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI + + // cache the pc register number (in whatever register numbering this UnwindPlan uses) for + // quick reference during instruction parsing. + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + RegisterInfo pc_reg_info; + if (m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info)) + pc_reg_num = pc_reg_info.kinds[unwind_plan.GetRegisterKind()]; + else + pc_reg_num = LLDB_INVALID_REGNUM; + + // cache the return address register number (in whatever register numbering this UnwindPlan uses) for + // quick reference during instruction parsing. + uint32_t ra_reg_num = LLDB_INVALID_REGNUM; + RegisterInfo ra_reg_info; + if (m_inst_emulator_ap->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, ra_reg_info)) + ra_reg_num = ra_reg_info.kinds[unwind_plan.GetRegisterKind()]; + else + ra_reg_num = LLDB_INVALID_REGNUM; + + for (size_t idx=0; idx<num_instructions; ++idx) + { + m_curr_row_modified = false; + m_curr_insn_restored_a_register = false; + inst = inst_list.GetInstructionAtIndex (idx).get(); + if (inst) + { + if (log && log->GetVerbose ()) + { + StreamString strm; + inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL); + log->PutCString (strm.GetData()); + } + + m_inst_emulator_ap->SetInstruction (inst->GetOpcode(), + inst->GetAddress(), + exe_ctx.GetTargetPtr()); + + m_inst_emulator_ap->EvaluateInstruction (eEmulateInstructionOptionIgnoreConditions); + + // Were there any changes to the CFI while evaluating this instruction? + if (m_curr_row_modified) + { + reinstate_prologue_next_instruction = false; + m_curr_row->SetOffset (inst->GetAddress().GetFileAddress() + inst->GetOpcode().GetByteSize() - base_addr); + // Append the new row + unwind_plan.AppendRow (m_curr_row); + + // Allocate a new Row for m_curr_row, copy the current state into it + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *m_curr_row.get(); + m_curr_row.reset(newrow); + + // If m_curr_insn_restored_a_register == true, we're looking at an epilogue instruction. + // Set instructions_since_last_prologue_insn to a very high number so we don't append + // any of these epilogue instructions to our prologue_complete row. + if (m_curr_insn_restored_a_register == false && instructions_since_last_prologue_insn < 8) + instructions_since_last_prologue_insn = 0; + else + instructions_since_last_prologue_insn = 99; + + UnwindPlan::Row::RegisterLocation pc_regloc; + UnwindPlan::Row::RegisterLocation ra_regloc; + + // While parsing the instructions of this function, if we've ever + // seen the return address register (aka lr on arm) in a non-IsSame() state, + // it has been saved on the stack. If it's evern back to IsSame(), we've + // executed an epilogue. + if (ra_reg_num != LLDB_INVALID_REGNUM + && m_curr_row->GetRegisterInfo (ra_reg_num, ra_regloc) + && !ra_regloc.IsSame()) + { + return_address_register_has_been_saved = true; + } + + // If the caller's pc is "same", we've just executed an epilogue and we return to the caller + // after this instruction completes executing. + // If there are any instructions past this, there must have been flow control over this + // epilogue so we'll reinstate the original prologue setup instructions. + if (prologue_completed_row.get() + && pc_reg_num != LLDB_INVALID_REGNUM + && m_curr_row->GetRegisterInfo (pc_reg_num, pc_regloc) + && pc_regloc.IsSame()) + { + if (log && log->GetVerbose()) + log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- pc is <same>, restore prologue instructions."); + reinstate_prologue_next_instruction = true; + } + else if (prologue_completed_row.get() + && return_address_register_has_been_saved + && ra_reg_num != LLDB_INVALID_REGNUM + && m_curr_row->GetRegisterInfo (ra_reg_num, ra_regloc) + && ra_regloc.IsSame()) + { + if (log && log->GetVerbose()) + log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- lr is <same>, restore prologue instruction if the next instruction is a branch immediate."); + last_instruction_restored_return_addr_reg = true; + } + } + else + { + // If the previous instruction was a return-to-caller (epilogue), and we're still executing + // instructions in this function, there must be a code path that jumps over that epilogue. + // Also detect the case where we epilogue & branch imm to another function (tail-call opt) + // instead of a normal pop lr-into-pc exit. + // Reinstate the frame setup from the prologue. + if (reinstate_prologue_next_instruction + || (m_curr_insn_is_branch_immediate && last_instruction_restored_return_addr_reg)) + { + if (log && log->GetVerbose()) + log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- Reinstating prologue instruction set"); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *prologue_completed_row.get(); + m_curr_row.reset(newrow); + m_curr_row->SetOffset (inst->GetAddress().GetFileAddress() + inst->GetOpcode().GetByteSize() - base_addr); + unwind_plan.AppendRow(m_curr_row); + + newrow = new UnwindPlan::Row; + *newrow = *m_curr_row.get(); + m_curr_row.reset(newrow); + + reinstate_prologue_next_instruction = false; + last_instruction_restored_return_addr_reg = false; + m_curr_insn_is_branch_immediate = false; + } + + // clear both of these if either one wasn't set + if (last_instruction_restored_return_addr_reg) + { + last_instruction_restored_return_addr_reg = false; + } + if (m_curr_insn_is_branch_immediate) + { + m_curr_insn_is_branch_immediate = false; + } + + // Stop updating the prologue instructions if we've seen 8 non-prologue instructions + // in a row. + if (instructions_since_last_prologue_insn++ < 8) + { + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *m_curr_row.get(); + prologue_completed_row.reset(newrow); + if (log && log->GetVerbose()) + log->Printf("UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly -- saving a copy of the current row as the prologue row."); + } + } + } + } + } + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disasm_sp->GetInstructionList().Clear(); + } + + if (log && log->GetVerbose ()) + { + StreamString strm; + lldb::addr_t base_addr = range.GetBaseAddress().GetLoadAddress(thread.CalculateTarget().get()); + strm.Printf ("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", base_addr, base_addr + range.GetByteSize()); + unwind_plan.Dump(strm, &thread, base_addr); + log->PutCString (strm.GetData()); + } + return unwind_plan.GetRowCount() > 0; + } + return false; +} + +bool +UnwindAssemblyInstEmulation::GetFastUnwindPlan (AddressRange& func, + Thread& thread, + UnwindPlan &unwind_plan) +{ + return false; +} + +bool +UnwindAssemblyInstEmulation::FirstNonPrologueInsn (AddressRange& func, + const ExecutionContext &exe_ctx, + Address& first_non_prologue_insn) +{ + return false; +} + +UnwindAssembly * +UnwindAssemblyInstEmulation::CreateInstance (const ArchSpec &arch) +{ + std::unique_ptr<EmulateInstruction> inst_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypePrologueEpilogue, NULL)); + // Make sure that all prologue instructions are handled + if (inst_emulator_ap.get()) + return new UnwindAssemblyInstEmulation (arch, inst_emulator_ap.release()); + return NULL; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol in UnwindAssemblyParser_x86 +//------------------------------------------------------------------ +ConstString +UnwindAssemblyInstEmulation::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +UnwindAssemblyInstEmulation::GetPluginVersion() +{ + return 1; +} + +void +UnwindAssemblyInstEmulation::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +UnwindAssemblyInstEmulation::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +ConstString +UnwindAssemblyInstEmulation::GetPluginNameStatic() +{ + static ConstString g_name("inst-emulation"); + return g_name; +} + +const char * +UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() +{ + return "Instruction emulation based unwind information."; +} + + +uint64_t +UnwindAssemblyInstEmulation::MakeRegisterKindValuePair (const RegisterInfo ®_info) +{ + uint32_t reg_kind, reg_num; + if (EmulateInstruction::GetBestRegisterKindAndNumber (®_info, reg_kind, reg_num)) + return (uint64_t)reg_kind << 24 | reg_num; + return 0ull; +} + +void +UnwindAssemblyInstEmulation::SetRegisterValue (const RegisterInfo ®_info, const RegisterValue ®_value) +{ + m_register_values[MakeRegisterKindValuePair (reg_info)] = reg_value; +} + +bool +UnwindAssemblyInstEmulation::GetRegisterValue (const RegisterInfo ®_info, RegisterValue ®_value) +{ + const uint64_t reg_id = MakeRegisterKindValuePair (reg_info); + RegisterValueMap::const_iterator pos = m_register_values.find(reg_id); + if (pos != m_register_values.end()) + { + reg_value = pos->second; + return true; // We had a real value that comes from an opcode that wrote + // to it... + } + // We are making up a value that is recognizable... + reg_value.SetUInt(reg_id, reg_info.byte_size); + return false; +} + + +size_t +UnwindAssemblyInstEmulation::ReadMemory (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + void *dst, + size_t dst_len) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + + if (log && log->GetVerbose ()) + { + StreamString strm; + strm.Printf ("UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64 ", dst = %p, dst_len = %" PRIu64 ", context = ", + addr, + dst, + (uint64_t)dst_len); + context.Dump(strm, instruction); + log->PutCString (strm.GetData ()); + } + memset (dst, 0, dst_len); + return dst_len; +} + +size_t +UnwindAssemblyInstEmulation::WriteMemory (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t dst_len) +{ + if (baton && dst && dst_len) + return ((UnwindAssemblyInstEmulation *)baton)->WriteMemory (instruction, context, addr, dst, dst_len); + return 0; +} + +size_t +UnwindAssemblyInstEmulation::WriteMemory (EmulateInstruction *instruction, + const EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t dst_len) +{ + DataExtractor data (dst, + dst_len, + instruction->GetArchitecture ().GetByteOrder(), + instruction->GetArchitecture ().GetAddressByteSize()); + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + + if (log && log->GetVerbose ()) + { + StreamString strm; + + strm.PutCString ("UnwindAssemblyInstEmulation::WriteMemory ("); + data.Dump(&strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX, addr, 0, 0); + strm.PutCString (", context = "); + context.Dump(strm, instruction); + log->PutCString (strm.GetData()); + } + + const bool can_replace = true; + const bool cant_replace = false; + + switch (context.type) + { + default: + case EmulateInstruction::eContextInvalid: + case EmulateInstruction::eContextReadOpcode: + case EmulateInstruction::eContextImmediate: + case EmulateInstruction::eContextAdjustBaseRegister: + case EmulateInstruction::eContextRegisterPlusOffset: + case EmulateInstruction::eContextAdjustPC: + case EmulateInstruction::eContextRegisterStore: + case EmulateInstruction::eContextRegisterLoad: + case EmulateInstruction::eContextRelativeBranchImmediate: + case EmulateInstruction::eContextAbsoluteBranchRegister: + case EmulateInstruction::eContextSupervisorCall: + case EmulateInstruction::eContextTableBranchReadMemory: + case EmulateInstruction::eContextWriteRegisterRandomBits: + case EmulateInstruction::eContextWriteMemoryRandomBits: + case EmulateInstruction::eContextArithmetic: + case EmulateInstruction::eContextAdvancePC: + case EmulateInstruction::eContextReturnFromException: + case EmulateInstruction::eContextPopRegisterOffStack: + case EmulateInstruction::eContextAdjustStackPointer: + break; + + case EmulateInstruction::eContextPushRegisterOnStack: + { + uint32_t reg_num = LLDB_INVALID_REGNUM; + bool is_return_address_reg = false; + const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind(); + if (context.info_type == EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset) + { + reg_num = context.info.RegisterToRegisterPlusOffset.data_reg.kinds[unwind_reg_kind]; + if (context.info.RegisterToRegisterPlusOffset.data_reg.kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_RA) + is_return_address_reg = true; + } + else + { + assert (!"unhandled case, add code to handle this!"); + } + + if (reg_num != LLDB_INVALID_REGNUM) + { + if (m_pushed_regs.find (reg_num) == m_pushed_regs.end()) + { + m_pushed_regs[reg_num] = addr; + const int32_t offset = addr - m_initial_sp; + m_curr_row->SetRegisterLocationToAtCFAPlusOffset (reg_num, offset, cant_replace); + m_curr_row_modified = true; + if (is_return_address_reg) + { + // This push was pushing the return address register, + // so this is also how we will unwind the PC... + RegisterInfo pc_reg_info; + if (instruction->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc_reg_info)) + { + uint32_t pc_reg_num = pc_reg_info.kinds[unwind_reg_kind]; + if (pc_reg_num != LLDB_INVALID_REGNUM) + { + m_curr_row->SetRegisterLocationToAtCFAPlusOffset (pc_reg_num, offset, can_replace); + m_curr_row_modified = true; + } + } + } + } + } + } + break; + + } + + return dst_len; +} + +bool +UnwindAssemblyInstEmulation::ReadRegister (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) +{ + + if (baton && reg_info) + return ((UnwindAssemblyInstEmulation *)baton)->ReadRegister (instruction, reg_info, reg_value); + return false; +} +bool +UnwindAssemblyInstEmulation::ReadRegister (EmulateInstruction *instruction, + const RegisterInfo *reg_info, + RegisterValue ®_value) +{ + bool synthetic = GetRegisterValue (*reg_info, reg_value); + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + + if (log && log->GetVerbose ()) + { + + StreamString strm; + strm.Printf ("UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => synthetic_value = %i, value = ", reg_info->name, synthetic); + reg_value.Dump(&strm, reg_info, false, false, eFormatDefault); + log->PutCString(strm.GetData()); + } + return true; +} + +bool +UnwindAssemblyInstEmulation::WriteRegister (EmulateInstruction *instruction, + void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) +{ + if (baton && reg_info) + return ((UnwindAssemblyInstEmulation *)baton)->WriteRegister (instruction, context, reg_info, reg_value); + return false; +} +bool +UnwindAssemblyInstEmulation::WriteRegister (EmulateInstruction *instruction, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + + if (log && log->GetVerbose ()) + { + + StreamString strm; + strm.Printf ("UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ", reg_info->name); + reg_value.Dump(&strm, reg_info, false, false, eFormatDefault); + strm.PutCString (", context = "); + context.Dump(strm, instruction); + log->PutCString(strm.GetData()); + } + + const bool must_replace = true; + SetRegisterValue (*reg_info, reg_value); + + switch (context.type) + { + case EmulateInstruction::eContextInvalid: + case EmulateInstruction::eContextReadOpcode: + case EmulateInstruction::eContextImmediate: + case EmulateInstruction::eContextAdjustBaseRegister: + case EmulateInstruction::eContextRegisterPlusOffset: + case EmulateInstruction::eContextAdjustPC: + case EmulateInstruction::eContextRegisterStore: + case EmulateInstruction::eContextRegisterLoad: + case EmulateInstruction::eContextAbsoluteBranchRegister: + case EmulateInstruction::eContextSupervisorCall: + case EmulateInstruction::eContextTableBranchReadMemory: + case EmulateInstruction::eContextWriteRegisterRandomBits: + case EmulateInstruction::eContextWriteMemoryRandomBits: + case EmulateInstruction::eContextArithmetic: + case EmulateInstruction::eContextAdvancePC: + case EmulateInstruction::eContextReturnFromException: + case EmulateInstruction::eContextPushRegisterOnStack: +// { +// const uint32_t reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; +// if (reg_num != LLDB_INVALID_REGNUM) +// { +// const bool can_replace_only_if_unspecified = true; +// +// m_curr_row.SetRegisterLocationToUndefined (reg_num, +// can_replace_only_if_unspecified, +// can_replace_only_if_unspecified); +// m_curr_row_modified = true; +// } +// } + break; + + case EmulateInstruction::eContextRelativeBranchImmediate: + { + + { + m_curr_insn_is_branch_immediate = true; + } + } + break; + + case EmulateInstruction::eContextPopRegisterOffStack: + { + const uint32_t reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; + if (reg_num != LLDB_INVALID_REGNUM) + { + m_curr_row->SetRegisterLocationToSame (reg_num, must_replace); + m_curr_row_modified = true; + m_curr_insn_restored_a_register = true; + } + } + break; + + case EmulateInstruction::eContextSetFramePointer: + if (!m_fp_is_cfa) + { + m_fp_is_cfa = true; + m_cfa_reg_info = *reg_info; + const uint32_t cfa_reg_num = reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; + assert (cfa_reg_num != LLDB_INVALID_REGNUM); + m_curr_row->SetCFARegister(cfa_reg_num); + m_curr_row->SetCFAOffset(m_initial_sp - reg_value.GetAsUInt64()); + m_curr_row_modified = true; + } + break; + + case EmulateInstruction::eContextAdjustStackPointer: + // If we have created a frame using the frame pointer, don't follow + // subsequent adjustments to the stack pointer. + if (!m_fp_is_cfa) + { + m_curr_row->SetCFAOffset (m_initial_sp - reg_value.GetAsUInt64()); + m_curr_row_modified = true; + } + break; + } + return true; +} + + diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h new file mode 100644 index 000000000000..6a02f0a55104 --- /dev/null +++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h @@ -0,0 +1,185 @@ +//===-- UnwindAssemblyInstEmulation.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_UnwindAssemblyInstEmulation_h_ +#define liblldb_UnwindAssemblyInstEmulation_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/UnwindAssembly.h" + +class UnwindAssemblyInstEmulation : public lldb_private::UnwindAssembly +{ +public: + + virtual + ~UnwindAssemblyInstEmulation () + { + } + + virtual bool + GetNonCallSiteUnwindPlanFromAssembly (lldb_private::AddressRange& func, + lldb_private::Thread& thread, + lldb_private::UnwindPlan& unwind_plan); + + virtual bool + GetFastUnwindPlan (lldb_private::AddressRange& func, + lldb_private::Thread& thread, + lldb_private::UnwindPlan &unwind_plan); + + // thread may be NULL in which case we only use the Target (e.g. if this is called pre-process-launch). + virtual bool + FirstNonPrologueInsn (lldb_private::AddressRange& func, + const lldb_private::ExecutionContext &exe_ctx, + lldb_private::Address& first_non_prologue_insn); + + static lldb_private::UnwindAssembly * + CreateInstance (const lldb_private::ArchSpec &arch); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +private: + + static size_t + ReadMemory (lldb_private::EmulateInstruction *instruction, + void *baton, + const lldb_private::EmulateInstruction::Context &context, + lldb::addr_t addr, + void *dst, + size_t length); + + static size_t + WriteMemory (lldb_private::EmulateInstruction *instruction, + void *baton, + const lldb_private::EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + static bool + ReadRegister (lldb_private::EmulateInstruction *instruction, + void *baton, + const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value); + + static bool + WriteRegister (lldb_private::EmulateInstruction *instruction, + void *baton, + const lldb_private::EmulateInstruction::Context &context, + const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value); + + +// size_t +// ReadMemory (lldb_private::EmulateInstruction *instruction, +// const lldb_private::EmulateInstruction::Context &context, +// lldb::addr_t addr, +// void *dst, +// size_t length); + + size_t + WriteMemory (lldb_private::EmulateInstruction *instruction, + const lldb_private::EmulateInstruction::Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + bool + ReadRegister (lldb_private::EmulateInstruction *instruction, + const lldb_private::RegisterInfo *reg_info, + lldb_private::RegisterValue ®_value); + + bool + WriteRegister (lldb_private::EmulateInstruction *instruction, + const lldb_private::EmulateInstruction::Context &context, + const lldb_private::RegisterInfo *reg_info, + const lldb_private::RegisterValue ®_value); + + // Call CreateInstance to get an instance of this class + UnwindAssemblyInstEmulation (const lldb_private::ArchSpec &arch, + lldb_private::EmulateInstruction *inst_emulator) : + UnwindAssembly (arch), + m_inst_emulator_ap (inst_emulator), + m_range_ptr (NULL), + m_thread_ptr (NULL), + m_unwind_plan_ptr (NULL), + m_curr_row (), + m_cfa_reg_info (), + m_fp_is_cfa (false), + m_register_values (), + m_pushed_regs(), + m_curr_row_modified (false), + m_curr_insn_is_branch_immediate (false), + m_curr_insn_restored_a_register (false) + { + if (m_inst_emulator_ap.get()) + { + m_inst_emulator_ap->SetBaton (this); + m_inst_emulator_ap->SetCallbacks (ReadMemory, WriteMemory, ReadRegister, WriteRegister); + } + } + + static uint64_t + MakeRegisterKindValuePair (const lldb_private::RegisterInfo ®_info); + + void + SetRegisterValue (const lldb_private::RegisterInfo ®_info, + const lldb_private::RegisterValue ®_value); + + bool + GetRegisterValue (const lldb_private::RegisterInfo ®_info, + lldb_private::RegisterValue ®_value); + + std::unique_ptr<lldb_private::EmulateInstruction> m_inst_emulator_ap; + lldb_private::AddressRange* m_range_ptr; + lldb_private::Thread* m_thread_ptr; + lldb_private::UnwindPlan* m_unwind_plan_ptr; + lldb_private::UnwindPlan::RowSP m_curr_row; + typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap; + uint64_t m_initial_sp; + lldb_private::RegisterInfo m_cfa_reg_info; + bool m_fp_is_cfa; + typedef std::map<uint64_t, lldb_private::RegisterValue> RegisterValueMap; + RegisterValueMap m_register_values; + PushedRegisterToAddrMap m_pushed_regs; + + // While processing the instruction stream, we need to communicate some state change + // information up to the higher level loop that makes decisions about how to push + // the unwind instructions for the UnwindPlan we're constructing. + + // The instruction we're processing updated the UnwindPlan::Row contents + bool m_curr_row_modified; + // The instruction we're examining is a branch immediate instruction + bool m_curr_insn_is_branch_immediate; + // The instruction we're processing restored a caller's reg value (e.g. in an epilogue) + bool m_curr_insn_restored_a_register; +}; + +#endif // liblldb_UnwindAssemblyInstEmulation_h_ diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp new file mode 100644 index 000000000000..d491683f6875 --- /dev/null +++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp @@ -0,0 +1,973 @@ +//===-- UnwindAssembly-x86.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UnwindAssembly-x86.h" + +#include "llvm-c/Disassembler.h" +#include "llvm/Support/TargetSelect.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/UnwindAssembly.h" + +using namespace lldb; +using namespace lldb_private; + +enum CPU { + k_i386, + k_x86_64 +}; + +enum i386_register_numbers { + k_machine_eax = 0, + k_machine_ecx = 1, + k_machine_edx = 2, + k_machine_ebx = 3, + k_machine_esp = 4, + k_machine_ebp = 5, + k_machine_esi = 6, + k_machine_edi = 7, + k_machine_eip = 8 +}; + +enum x86_64_register_numbers { + k_machine_rax = 0, + k_machine_rcx = 1, + k_machine_rdx = 2, + k_machine_rbx = 3, + k_machine_rsp = 4, + k_machine_rbp = 5, + k_machine_rsi = 6, + k_machine_rdi = 7, + k_machine_r8 = 8, + k_machine_r9 = 9, + k_machine_r10 = 10, + k_machine_r11 = 11, + k_machine_r12 = 12, + k_machine_r13 = 13, + k_machine_r14 = 14, + k_machine_r15 = 15, + k_machine_rip = 16 +}; + +struct regmap_ent { + const char *name; + int machine_regno; + int lldb_regno; +}; + +static struct regmap_ent i386_register_map[] = { + {"eax", k_machine_eax, -1}, + {"ecx", k_machine_ecx, -1}, + {"edx", k_machine_edx, -1}, + {"ebx", k_machine_ebx, -1}, + {"esp", k_machine_esp, -1}, + {"ebp", k_machine_ebp, -1}, + {"esi", k_machine_esi, -1}, + {"edi", k_machine_edi, -1}, + {"eip", k_machine_eip, -1} +}; + +const int size_of_i386_register_map = sizeof (i386_register_map) / sizeof (struct regmap_ent); + +static int i386_register_map_initialized = 0; + +static struct regmap_ent x86_64_register_map[] = { + {"rax", k_machine_rax, -1}, + {"rcx", k_machine_rcx, -1}, + {"rdx", k_machine_rdx, -1}, + {"rbx", k_machine_rbx, -1}, + {"rsp", k_machine_rsp, -1}, + {"rbp", k_machine_rbp, -1}, + {"rsi", k_machine_rsi, -1}, + {"rdi", k_machine_rdi, -1}, + {"r8", k_machine_r8, -1}, + {"r9", k_machine_r9, -1}, + {"r10", k_machine_r10, -1}, + {"r11", k_machine_r11, -1}, + {"r12", k_machine_r12, -1}, + {"r13", k_machine_r13, -1}, + {"r14", k_machine_r14, -1}, + {"r15", k_machine_r15, -1}, + {"rip", k_machine_rip, -1} +}; + +const int size_of_x86_64_register_map = sizeof (x86_64_register_map) / sizeof (struct regmap_ent); + +static int x86_64_register_map_initialized = 0; + +//----------------------------------------------------------------------------------------------- +// AssemblyParse_x86 local-file class definition & implementation functions +//----------------------------------------------------------------------------------------------- + +class AssemblyParse_x86 { +public: + + AssemblyParse_x86 (const ExecutionContext &exe_ctx, int cpu, ArchSpec &arch, AddressRange func); + + ~AssemblyParse_x86 (); + + bool get_non_call_site_unwind_plan (UnwindPlan &unwind_plan); + + bool get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan); + + bool find_first_non_prologue_insn (Address &address); + +private: + enum { kMaxInstructionByteSize = 32 }; + + bool nonvolatile_reg_p (int machine_regno); + bool push_rbp_pattern_p (); + bool push_0_pattern_p (); + bool mov_rsp_rbp_pattern_p (); + bool sub_rsp_pattern_p (int& amount); + bool push_reg_p (int& regno); + bool mov_reg_to_local_stack_frame_p (int& regno, int& fp_offset); + bool ret_pattern_p (); + uint32_t extract_4 (uint8_t *b); + bool machine_regno_to_lldb_regno (int machine_regno, uint32_t& lldb_regno); + bool instruction_length (Address addr, int &length); + + const ExecutionContext m_exe_ctx; + + AddressRange m_func_bounds; + + Address m_cur_insn; + uint8_t m_cur_insn_bytes[kMaxInstructionByteSize]; + + int m_machine_ip_regnum; + int m_machine_sp_regnum; + int m_machine_fp_regnum; + + int m_lldb_ip_regnum; + int m_lldb_sp_regnum; + int m_lldb_fp_regnum; + + int m_wordsize; + int m_cpu; + ArchSpec m_arch; + ::LLVMDisasmContextRef m_disasm_context; + + DISALLOW_COPY_AND_ASSIGN (AssemblyParse_x86); +}; + +AssemblyParse_x86::AssemblyParse_x86 (const ExecutionContext &exe_ctx, int cpu, ArchSpec &arch, AddressRange func) : + m_exe_ctx (exe_ctx), + m_func_bounds(func), + m_cur_insn (), + m_machine_ip_regnum (LLDB_INVALID_REGNUM), + m_machine_sp_regnum (LLDB_INVALID_REGNUM), + m_machine_fp_regnum (LLDB_INVALID_REGNUM), + m_lldb_ip_regnum (LLDB_INVALID_REGNUM), + m_lldb_sp_regnum (LLDB_INVALID_REGNUM), + m_lldb_fp_regnum (LLDB_INVALID_REGNUM), + m_wordsize (-1), + m_cpu(cpu), + m_arch(arch) +{ + int *initialized_flag = NULL; + if (cpu == k_i386) + { + m_machine_ip_regnum = k_machine_eip; + m_machine_sp_regnum = k_machine_esp; + m_machine_fp_regnum = k_machine_ebp; + m_wordsize = 4; + initialized_flag = &i386_register_map_initialized; + } + else + { + m_machine_ip_regnum = k_machine_rip; + m_machine_sp_regnum = k_machine_rsp; + m_machine_fp_regnum = k_machine_rbp; + m_wordsize = 8; + initialized_flag = &x86_64_register_map_initialized; + } + + // we only look at prologue - it will be complete earlier than 512 bytes into func + if (m_func_bounds.GetByteSize() == 0) + m_func_bounds.SetByteSize(512); + + Thread *thread = m_exe_ctx.GetThreadPtr(); + if (thread && *initialized_flag == 0) + { + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + if (reg_ctx) + { + struct regmap_ent *ent; + int count, i; + if (cpu == k_i386) + { + ent = i386_register_map; + count = size_of_i386_register_map; + } + else + { + ent = x86_64_register_map; + count = size_of_x86_64_register_map; + } + for (i = 0; i < count; i++, ent++) + { + const RegisterInfo *ri = reg_ctx->GetRegisterInfoByName (ent->name); + if (ri) + ent->lldb_regno = ri->kinds[eRegisterKindLLDB]; + } + *initialized_flag = 1; + } + } + + // on initial construction we may not have a Thread so these have to remain + // uninitialized until we can get a RegisterContext to set up the register map table + if (*initialized_flag == 1) + { + uint32_t lldb_regno; + if (machine_regno_to_lldb_regno (m_machine_sp_regnum, lldb_regno)) + m_lldb_sp_regnum = lldb_regno; + if (machine_regno_to_lldb_regno (m_machine_fp_regnum, lldb_regno)) + m_lldb_fp_regnum = lldb_regno; + if (machine_regno_to_lldb_regno (m_machine_ip_regnum, lldb_regno)) + m_lldb_ip_regnum = lldb_regno; + } + + m_disasm_context = ::LLVMCreateDisasm(m_arch.GetTriple().getTriple().c_str(), + (void*)this, + /*TagType=*/1, + NULL, + NULL); +} + +AssemblyParse_x86::~AssemblyParse_x86 () +{ + ::LLVMDisasmDispose(m_disasm_context); +} + +// This function expects an x86 native register number (i.e. the bits stripped out of the +// actual instruction), not an lldb register number. + +bool +AssemblyParse_x86::nonvolatile_reg_p (int machine_regno) +{ + if (m_cpu == k_i386) + { + switch (machine_regno) { + case k_machine_ebx: + case k_machine_ebp: // not actually a nonvolatile but often treated as such by convention + case k_machine_esi: + case k_machine_edi: + case k_machine_esp: + return true; + default: + return false; + } + } + if (m_cpu == k_x86_64) + { + switch (machine_regno) { + case k_machine_rbx: + case k_machine_rsp: + case k_machine_rbp: // not actually a nonvolatile but often treated as such by convention + case k_machine_r12: + case k_machine_r13: + case k_machine_r14: + case k_machine_r15: + return true; + default: + return false; + } + } + return false; +} + + +// Macro to detect if this is a REX mode prefix byte. +#define REX_W_PREFIX_P(opcode) (((opcode) & (~0x5)) == 0x48) + +// The high bit which should be added to the source register number (the "R" bit) +#define REX_W_SRCREG(opcode) (((opcode) & 0x4) >> 2) + +// The high bit which should be added to the destination register number (the "B" bit) +#define REX_W_DSTREG(opcode) ((opcode) & 0x1) + +// pushq %rbp [0x55] +bool AssemblyParse_x86::push_rbp_pattern_p () { + uint8_t *p = m_cur_insn_bytes; + if (*p == 0x55) + return true; + return false; +} + +// pushq $0 ; the first instruction in start() [0x6a 0x00] +bool AssemblyParse_x86::push_0_pattern_p () +{ + uint8_t *p = m_cur_insn_bytes; + if (*p == 0x6a && *(p + 1) == 0x0) + return true; + return false; +} + +// movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5] +// movl %esp, %ebp [0x8b 0xec] or [0x89 0xe5] +bool AssemblyParse_x86::mov_rsp_rbp_pattern_p () { + uint8_t *p = m_cur_insn_bytes; + if (m_wordsize == 8 && *p == 0x48) + p++; + if (*(p) == 0x8b && *(p + 1) == 0xec) + return true; + if (*(p) == 0x89 && *(p + 1) == 0xe5) + return true; + return false; +} + +// subq $0x20, %rsp +bool AssemblyParse_x86::sub_rsp_pattern_p (int& amount) { + uint8_t *p = m_cur_insn_bytes; + if (m_wordsize == 8 && *p == 0x48) + p++; + // 8-bit immediate operand + if (*p == 0x83 && *(p + 1) == 0xec) { + amount = (int8_t) *(p + 2); + return true; + } + // 32-bit immediate operand + if (*p == 0x81 && *(p + 1) == 0xec) { + amount = (int32_t) extract_4 (p + 2); + return true; + } + // Not handled: [0x83 0xc4] for imm8 with neg values + // [0x81 0xc4] for imm32 with neg values + return false; +} + +// pushq %rbx +// pushl $ebx +bool AssemblyParse_x86::push_reg_p (int& regno) { + uint8_t *p = m_cur_insn_bytes; + int regno_prefix_bit = 0; + // If we have a rex prefix byte, check to see if a B bit is set + if (m_wordsize == 8 && *p == 0x41) { + regno_prefix_bit = 1 << 3; + p++; + } + if (*p >= 0x50 && *p <= 0x57) { + regno = (*p - 0x50) | regno_prefix_bit; + return true; + } + return false; +} + +// Look for an instruction sequence storing a nonvolatile register +// on to the stack frame. + +// movq %rax, -0x10(%rbp) [0x48 0x89 0x45 0xf0] +// movl %eax, -0xc(%ebp) [0x89 0x45 0xf4] +bool AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int& regno, int& rbp_offset) { + uint8_t *p = m_cur_insn_bytes; + int src_reg_prefix_bit = 0; + int target_reg_prefix_bit = 0; + + if (m_wordsize == 8 && REX_W_PREFIX_P (*p)) { + src_reg_prefix_bit = REX_W_SRCREG (*p) << 3; + target_reg_prefix_bit = REX_W_DSTREG (*p) << 3; + if (target_reg_prefix_bit == 1) { + // rbp/ebp don't need a prefix bit - we know this isn't the + // reg we care about. + return false; + } + p++; + } + + if (*p == 0x89) { + /* Mask off the 3-5 bits which indicate the destination register + if this is a ModR/M byte. */ + int opcode_destreg_masked_out = *(p + 1) & (~0x38); + + /* Is this a ModR/M byte with Mod bits 01 and R/M bits 101 + and three bits between them, e.g. 01nnn101 + We're looking for a destination of ebp-disp8 or ebp-disp32. */ + int immsize; + if (opcode_destreg_masked_out == 0x45) + immsize = 2; + else if (opcode_destreg_masked_out == 0x85) + immsize = 4; + else + return false; + + int offset = 0; + if (immsize == 2) + offset = (int8_t) *(p + 2); + if (immsize == 4) + offset = (uint32_t) extract_4 (p + 2); + if (offset > 0) + return false; + + regno = ((*(p + 1) >> 3) & 0x7) | src_reg_prefix_bit; + rbp_offset = offset > 0 ? offset : -offset; + return true; + } + return false; +} + +// ret [0xc9] or [0xc2 imm8] or [0xca imm8] +bool +AssemblyParse_x86::ret_pattern_p () +{ + uint8_t *p = m_cur_insn_bytes; + if (*p == 0xc9 || *p == 0xc2 || *p == 0xca || *p == 0xc3) + return true; + return false; +} + +uint32_t +AssemblyParse_x86::extract_4 (uint8_t *b) +{ + uint32_t v = 0; + for (int i = 3; i >= 0; i--) + v = (v << 8) | b[i]; + return v; +} + +bool +AssemblyParse_x86::machine_regno_to_lldb_regno (int machine_regno, uint32_t &lldb_regno) +{ + struct regmap_ent *ent; + int count, i; + if (m_cpu == k_i386) + { + ent = i386_register_map; + count = size_of_i386_register_map; + } + else + { + ent = x86_64_register_map; + count = size_of_x86_64_register_map; + } + for (i = 0; i < count; i++, ent++) + { + if (ent->machine_regno == machine_regno) + if (ent->lldb_regno != -1) + { + lldb_regno = ent->lldb_regno; + return true; + } + } + return false; +} + +bool +AssemblyParse_x86::instruction_length (Address addr, int &length) +{ + const uint32_t max_op_byte_size = m_arch.GetMaximumOpcodeByteSize(); + llvm::SmallVector <uint8_t, 32> opcode_data; + opcode_data.resize (max_op_byte_size); + + if (!addr.IsValid()) + return false; + + const bool prefer_file_cache = true; + Error error; + Target *target = m_exe_ctx.GetTargetPtr(); + if (target->ReadMemory (addr, prefer_file_cache, opcode_data.data(), max_op_byte_size, error) == -1) + { + return false; + } + + char out_string[512]; + const addr_t pc = addr.GetFileAddress(); + const size_t inst_size = ::LLVMDisasmInstruction (m_disasm_context, + opcode_data.data(), + max_op_byte_size, + pc, // PC value + out_string, + sizeof(out_string)); + + length = inst_size; + return true; +} + + +bool +AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan) +{ + UnwindPlan::RowSP row(new UnwindPlan::Row); + int non_prologue_insn_count = 0; + m_cur_insn = m_func_bounds.GetBaseAddress (); + int current_func_text_offset = 0; + int current_sp_bytes_offset_from_cfa = 0; + UnwindPlan::Row::RegisterLocation initial_regloc; + Error error; + + if (!m_cur_insn.IsValid()) + { + return false; + } + + unwind_plan.SetPlanValidAddressRange (m_func_bounds); + unwind_plan.SetRegisterKind (eRegisterKindLLDB); + + // At the start of the function, find the CFA by adding wordsize to the SP register + row->SetOffset (current_func_text_offset); + row->SetCFARegister (m_lldb_sp_regnum); + row->SetCFAOffset (m_wordsize); + + // caller's stack pointer value before the call insn is the CFA address + initial_regloc.SetIsCFAPlusOffset (0); + row->SetRegisterInfo (m_lldb_sp_regnum, initial_regloc); + + // saved instruction pointer can be found at CFA - wordsize. + current_sp_bytes_offset_from_cfa = m_wordsize; + initial_regloc.SetAtCFAPlusOffset (-current_sp_bytes_offset_from_cfa); + row->SetRegisterInfo (m_lldb_ip_regnum, initial_regloc); + + unwind_plan.AppendRow (row); + + // Allocate a new Row, populate it with the existing Row contents. + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + + const bool prefer_file_cache = true; + + Target *target = m_exe_ctx.GetTargetPtr(); + while (m_func_bounds.ContainsFileAddress (m_cur_insn) && non_prologue_insn_count < 10) + { + int stack_offset, insn_len; + int machine_regno; // register numbers masked directly out of instructions + uint32_t lldb_regno; // register numbers in lldb's eRegisterKindLLDB numbering scheme + + if (!instruction_length (m_cur_insn, insn_len) || insn_len == 0 || insn_len > kMaxInstructionByteSize) + { + // An unrecognized/junk instruction + break; + } + if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes, insn_len, error) == -1) + { + // Error reading the instruction out of the file, stop scanning + break; + } + + if (push_rbp_pattern_p ()) + { + row->SetOffset (current_func_text_offset + insn_len); + current_sp_bytes_offset_from_cfa += m_wordsize; + row->SetCFAOffset (current_sp_bytes_offset_from_cfa); + UnwindPlan::Row::RegisterLocation regloc; + regloc.SetAtCFAPlusOffset (-row->GetCFAOffset()); + row->SetRegisterInfo (m_lldb_fp_regnum, regloc); + unwind_plan.AppendRow (row); + // Allocate a new Row, populate it with the existing Row contents. + newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + goto loopnext; + } + + if (mov_rsp_rbp_pattern_p ()) + { + row->SetOffset (current_func_text_offset + insn_len); + row->SetCFARegister (m_lldb_fp_regnum); + unwind_plan.AppendRow (row); + // Allocate a new Row, populate it with the existing Row contents. + newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + goto loopnext; + } + + // This is the start() function (or a pthread equivalent), it starts with a pushl $0x0 which puts the + // saved pc value of 0 on the stack. In this case we want to pretend we didn't see a stack movement at all -- + // normally the saved pc value is already on the stack by the time the function starts executing. + if (push_0_pattern_p ()) + { + goto loopnext; + } + + if (push_reg_p (machine_regno)) + { + current_sp_bytes_offset_from_cfa += m_wordsize; + if (nonvolatile_reg_p (machine_regno) && machine_regno_to_lldb_regno (machine_regno, lldb_regno)) + { + row->SetOffset (current_func_text_offset + insn_len); + if (row->GetCFARegister() == m_lldb_sp_regnum) + { + row->SetCFAOffset (current_sp_bytes_offset_from_cfa); + } + UnwindPlan::Row::RegisterLocation regloc; + regloc.SetAtCFAPlusOffset (-current_sp_bytes_offset_from_cfa); + row->SetRegisterInfo (lldb_regno, regloc); + unwind_plan.AppendRow (row); + // Allocate a new Row, populate it with the existing Row contents. + newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + } + goto loopnext; + } + + if (mov_reg_to_local_stack_frame_p (machine_regno, stack_offset) && nonvolatile_reg_p (machine_regno)) + { + if (machine_regno_to_lldb_regno (machine_regno, lldb_regno)) + { + row->SetOffset (current_func_text_offset + insn_len); + UnwindPlan::Row::RegisterLocation regloc; + regloc.SetAtCFAPlusOffset (-row->GetCFAOffset()); + row->SetRegisterInfo (lldb_regno, regloc); + unwind_plan.AppendRow (row); + // Allocate a new Row, populate it with the existing Row contents. + newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + goto loopnext; + } + } + + if (sub_rsp_pattern_p (stack_offset)) + { + current_sp_bytes_offset_from_cfa += stack_offset; + if (row->GetCFARegister() == m_lldb_sp_regnum) + { + row->SetOffset (current_func_text_offset + insn_len); + row->SetCFAOffset (current_sp_bytes_offset_from_cfa); + unwind_plan.AppendRow (row); + // Allocate a new Row, populate it with the existing Row contents. + newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + } + goto loopnext; + } + + if (ret_pattern_p ()) + { + // we know where the end of the function is; set the limit on the PlanValidAddressRange + // in case our initial "high pc" value was overly large + // int original_size = m_func_bounds.GetByteSize(); + // int calculated_size = m_cur_insn.GetOffset() - m_func_bounds.GetBaseAddress().GetOffset() + insn_len + 1; + // m_func_bounds.SetByteSize (calculated_size); + // unwind_plan.SetPlanValidAddressRange (m_func_bounds); + break; + } + + // FIXME recognize the i386 picbase setup instruction sequence, + // 0x1f16: call 0x1f1b ; main + 11 at /private/tmp/a.c:3 + // 0x1f1b: popl %eax + // and record the temporary stack movements if the CFA is not expressed in terms of ebp. + + non_prologue_insn_count++; +loopnext: + m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len); + current_func_text_offset += insn_len; + } + + // Now look at the byte at the end of the AddressRange for a limited attempt at describing the + // epilogue. We're looking for the sequence + + // [ 0x5d ] mov %rbp, %rsp + // [ 0xc3 ] ret + // [ 0xe8 xx xx xx xx ] call __stack_chk_fail (this is sometimes the final insn in the function) + + // We want to add a Row describing how to unwind when we're stopped on the 'ret' instruction where the + // CFA is no longer defined in terms of rbp, but is now defined in terms of rsp like on function entry. + + uint64_t ret_insn_offset = LLDB_INVALID_ADDRESS; + Address end_of_fun(m_func_bounds.GetBaseAddress()); + end_of_fun.SetOffset (end_of_fun.GetOffset() + m_func_bounds.GetByteSize()); + + if (m_func_bounds.GetByteSize() > 7) + { + uint8_t bytebuf[7]; + Address last_seven_bytes(end_of_fun); + last_seven_bytes.SetOffset (last_seven_bytes.GetOffset() - 7); + if (target->ReadMemory (last_seven_bytes, prefer_file_cache, bytebuf, 7, error) != -1) + { + if (bytebuf[5] == 0x5d && bytebuf[6] == 0xc3) // mov, ret + { + ret_insn_offset = m_func_bounds.GetByteSize() - 1; + } + else if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3 && bytebuf[2] == 0xe8) // mov, ret, call + { + ret_insn_offset = m_func_bounds.GetByteSize() - 6; + } + } + } else if (m_func_bounds.GetByteSize() > 2) + { + uint8_t bytebuf[2]; + Address last_two_bytes(end_of_fun); + last_two_bytes.SetOffset (last_two_bytes.GetOffset() - 2); + if (target->ReadMemory (last_two_bytes, prefer_file_cache, bytebuf, 2, error) != -1) + { + if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3) // mov, ret + { + ret_insn_offset = m_func_bounds.GetByteSize() - 1; + } + } + } + + if (ret_insn_offset != LLDB_INVALID_ADDRESS) + { + // Create a fresh, empty Row and RegisterLocation - don't mention any other registers + UnwindPlan::RowSP epi_row(new UnwindPlan::Row); + UnwindPlan::Row::RegisterLocation epi_regloc; + + // When the ret instruction is about to be executed, here's our state + epi_row->SetOffset (ret_insn_offset); + epi_row->SetCFARegister (m_lldb_sp_regnum); + epi_row->SetCFAOffset (m_wordsize); + + // caller's stack pointer value before the call insn is the CFA address + epi_regloc.SetIsCFAPlusOffset (0); + epi_row->SetRegisterInfo (m_lldb_sp_regnum, epi_regloc); + + // saved instruction pointer can be found at CFA - wordsize + epi_regloc.SetAtCFAPlusOffset (-m_wordsize); + epi_row->SetRegisterInfo (m_lldb_ip_regnum, epi_regloc); + + unwind_plan.AppendRow (epi_row); + } + + unwind_plan.SetSourceName ("assembly insn profiling"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); + + return true; +} + +/* The "fast unwind plan" is valid for functions that follow the usual convention of + using the frame pointer register (ebp, rbp), i.e. the function prologue looks like + push %rbp [0x55] + mov %rsp,%rbp [0x48 0x89 0xe5] (this is a 2-byte insn seq on i386) +*/ + +bool +AssemblyParse_x86::get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan) +{ + UnwindPlan::RowSP row(new UnwindPlan::Row); + UnwindPlan::Row::RegisterLocation pc_reginfo; + UnwindPlan::Row::RegisterLocation sp_reginfo; + UnwindPlan::Row::RegisterLocation fp_reginfo; + unwind_plan.SetRegisterKind (eRegisterKindLLDB); + + if (!func.GetBaseAddress().IsValid()) + return false; + + Target *target = m_exe_ctx.GetTargetPtr(); + + uint8_t bytebuf[4]; + Error error; + const bool prefer_file_cache = true; + if (target->ReadMemory (func.GetBaseAddress(), prefer_file_cache, bytebuf, sizeof (bytebuf), error) == -1) + return false; + + uint8_t i386_prologue[] = {0x55, 0x89, 0xe5}; + uint8_t x86_64_prologue[] = {0x55, 0x48, 0x89, 0xe5}; + int prologue_size; + + if (memcmp (bytebuf, i386_prologue, sizeof (i386_prologue)) == 0) + { + prologue_size = sizeof (i386_prologue); + } + else if (memcmp (bytebuf, x86_64_prologue, sizeof (x86_64_prologue)) == 0) + { + prologue_size = sizeof (x86_64_prologue); + } + else + { + return false; + } + + pc_reginfo.SetAtCFAPlusOffset (-m_wordsize); + row->SetRegisterInfo (m_lldb_ip_regnum, pc_reginfo); + + sp_reginfo.SetIsCFAPlusOffset (0); + row->SetRegisterInfo (m_lldb_sp_regnum, sp_reginfo); + + // Zero instructions into the function + row->SetCFARegister (m_lldb_sp_regnum); + row->SetCFAOffset (m_wordsize); + row->SetOffset (0); + unwind_plan.AppendRow (row); + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + + // push %rbp has executed - stack moved, rbp now saved + row->SetCFAOffset (2 * m_wordsize); + fp_reginfo.SetAtCFAPlusOffset (2 * -m_wordsize); + row->SetRegisterInfo (m_lldb_fp_regnum, fp_reginfo); + row->SetOffset (1); + unwind_plan.AppendRow (row); + + newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + + // mov %rsp, %rbp has executed + row->SetCFARegister (m_lldb_fp_regnum); + row->SetCFAOffset (2 * m_wordsize); + row->SetOffset (prologue_size); /// 3 or 4 bytes depending on arch + unwind_plan.AppendRow (row); + + newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); + + unwind_plan.SetPlanValidAddressRange (func); + unwind_plan.SetSourceName ("fast unwind assembly profiling"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + return true; +} + +bool +AssemblyParse_x86::find_first_non_prologue_insn (Address &address) +{ + m_cur_insn = m_func_bounds.GetBaseAddress (); + if (!m_cur_insn.IsValid()) + { + return false; + } + + const bool prefer_file_cache = true; + Target *target = m_exe_ctx.GetTargetPtr(); + while (m_func_bounds.ContainsFileAddress (m_cur_insn)) + { + Error error; + int insn_len, offset, regno; + if (!instruction_length (m_cur_insn, insn_len) || insn_len > kMaxInstructionByteSize || insn_len == 0) + { + // An error parsing the instruction, i.e. probably data/garbage - stop scanning + break; + } + if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes, insn_len, error) == -1) + { + // Error reading the instruction out of the file, stop scanning + break; + } + + if (push_rbp_pattern_p () || mov_rsp_rbp_pattern_p () || sub_rsp_pattern_p (offset) + || push_reg_p (regno) || mov_reg_to_local_stack_frame_p (regno, offset)) + { + m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len); + continue; + } + + // Unknown non-prologue instruction - stop scanning + break; + } + + address = m_cur_insn; + return true; +} + + + + + + +//----------------------------------------------------------------------------------------------- +// UnwindAssemblyParser_x86 method definitions +//----------------------------------------------------------------------------------------------- + +UnwindAssembly_x86::UnwindAssembly_x86 (const ArchSpec &arch, int cpu) : + lldb_private::UnwindAssembly(arch), + m_cpu(cpu), + m_arch(arch) +{ +} + + +UnwindAssembly_x86::~UnwindAssembly_x86 () +{ +} + +bool +UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& func, Thread& thread, UnwindPlan& unwind_plan) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func); + return asm_parse.get_non_call_site_unwind_plan (unwind_plan); +} + +bool +UnwindAssembly_x86::GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func); + return asm_parse.get_fast_unwind_plan (func, unwind_plan); +} + +bool +UnwindAssembly_x86::FirstNonPrologueInsn (AddressRange& func, const ExecutionContext &exe_ctx, Address& first_non_prologue_insn) +{ + AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func); + return asm_parse.find_first_non_prologue_insn (first_non_prologue_insn); +} + +UnwindAssembly * +UnwindAssembly_x86::CreateInstance (const ArchSpec &arch) +{ + const llvm::Triple::ArchType cpu = arch.GetMachine (); + if (cpu == llvm::Triple::x86) + return new UnwindAssembly_x86 (arch, k_i386); + else if (cpu == llvm::Triple::x86_64) + return new UnwindAssembly_x86 (arch, k_x86_64); + return NULL; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol in UnwindAssemblyParser_x86 +//------------------------------------------------------------------ + +ConstString +UnwindAssembly_x86::GetPluginName() +{ + return GetPluginNameStatic(); +} + + +uint32_t +UnwindAssembly_x86::GetPluginVersion() +{ + return 1; +} + +void +UnwindAssembly_x86::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +UnwindAssembly_x86::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +lldb_private::ConstString +UnwindAssembly_x86::GetPluginNameStatic() +{ + static ConstString g_name("x86"); + return g_name; +} + +const char * +UnwindAssembly_x86::GetPluginDescriptionStatic() +{ + return "i386 and x86_64 assembly language profiler plugin."; +} diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h new file mode 100644 index 000000000000..eebaa7b6c803 --- /dev/null +++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h @@ -0,0 +1,73 @@ +//===-- UnwindAssembly-x86.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_UnwindAssembly_x86_h_ +#define liblldb_UnwindAssembly_x86_h_ + +#include "llvm-c/Disassembler.h" + +#include "lldb/lldb-private.h" +#include "lldb/Target/UnwindAssembly.h" + +class UnwindAssembly_x86 : public lldb_private::UnwindAssembly +{ +public: + + ~UnwindAssembly_x86 (); + + virtual bool + GetNonCallSiteUnwindPlanFromAssembly (lldb_private::AddressRange& func, + lldb_private::Thread& thread, + lldb_private::UnwindPlan& unwind_plan); + + virtual bool + GetFastUnwindPlan (lldb_private::AddressRange& func, + lldb_private::Thread& thread, + lldb_private::UnwindPlan &unwind_plan); + + // thread may be NULL in which case we only use the Target (e.g. if this is called pre-process-launch). + virtual bool + FirstNonPrologueInsn (lldb_private::AddressRange& func, + const lldb_private::ExecutionContext &exe_ctx, + lldb_private::Address& first_non_prologue_insn); + + static lldb_private::UnwindAssembly * + CreateInstance (const lldb_private::ArchSpec &arch); + + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +private: + UnwindAssembly_x86 (const lldb_private::ArchSpec &arch, int cpu); + + int m_cpu; + lldb_private::ArchSpec m_arch; +}; + + +#endif // liblldb_UnwindAssembly_x86_h_ |