| //===-- 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/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 "Plugins/Process/Utility/ARMDefines.h" |
| |
| #include <vector> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| static const char *pluginName = "ABIMacOSX_arm"; |
| static const char *pluginDesc = "Mac OS X ABI for arm targets"; |
| static const char *pluginShort = "abi.macosx-arm"; |
| |
| 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; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Figure out if our return address is ARM or Thumb. We assume if we don't |
| // know about an address then it is ARM code. |
| |
| Target *target = &thread.GetProcess().GetTarget(); |
| Address so_addr; |
| bool ra_is_thumb = false; |
| if (return_addr & 3) |
| ra_is_thumb = true; |
| else if (so_addr.SetLoadAddress (return_addr, target)) |
| ra_is_thumb = so_addr.GetAddressClass() == eAddressClassCodeAlternateISA; |
| |
| // Set our clear bit zero for the return address if needed. We should never |
| // need to clear bit zero since the return address will either have bit zero |
| // or bit one (a thumb instruction on a two byte boundary) already set, or |
| // it won't and it will need it. |
| if (ra_is_thumb) |
| return_addr |= 1u; |
| |
| // 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; |
| |
| bool pc_is_thumb = false; |
| |
| // If bit zero or 1 is set, this must be a thumb function, no need to figure |
| // this out from the symbols. |
| if (function_addr & 3) |
| pc_is_thumb = true; |
| else if (so_addr.SetLoadAddress (function_addr, target)) |
| pc_is_thumb = so_addr.GetAddressClass() == eAddressClassCodeAlternateISA; |
| |
| |
| 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 (pc_is_thumb) |
| 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 &= ~1u; // 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; |
| } |
| |
| |
| static bool |
| ReadIntegerArgument (Scalar &scalar, |
| unsigned int bit_width, |
| bool is_signed, |
| Process &process, |
| addr_t ¤t_stack_argument) |
| { |
| // if (bit_width > 64) |
| // return false; // Scalar can't hold large integer arguments |
| // |
| // uint64_t arg_contents; |
| // uint32_t read_data; |
| // Error error; |
| // |
| // if (bit_width > 32) |
| // { |
| // if (process.ReadMemory(current_stack_argument, &read_data, sizeof(read_data), error) != sizeof(read_data)) |
| // return false; |
| // |
| // arg_contents = read_data; |
| // |
| // if (process.ReadMemory(current_stack_argument + 4, &read_data, sizeof(read_data), error) != sizeof(read_data)) |
| // return false; |
| // |
| // arg_contents |= ((uint64_t)read_data) << 32; |
| // |
| // current_stack_argument += 8; |
| // } |
| // else { |
| // if (process.ReadMemory(current_stack_argument, &read_data, sizeof(read_data), error) != sizeof(read_data)) |
| // return false; |
| // |
| // arg_contents = read_data; |
| // |
| // current_stack_argument += 4; |
| // } |
| // |
| // if (is_signed) |
| // { |
| // switch (bit_width) |
| // { |
| // default: |
| // return false; |
| // case 8: |
| // scalar = (int8_t)(arg_contents & 0xff); |
| // break; |
| // case 16: |
| // scalar = (int16_t)(arg_contents & 0xffff); |
| // break; |
| // case 32: |
| // scalar = (int32_t)(arg_contents & 0xffffffff); |
| // break; |
| // case 64: |
| // scalar = (int64_t)arg_contents; |
| // break; |
| // } |
| // } |
| // else |
| // { |
| // switch (bit_width) |
| // { |
| // default: |
| // return false; |
| // case 8: |
| // scalar = (uint8_t)(arg_contents & 0xff); |
| // break; |
| // case 16: |
| // scalar = (uint16_t)(arg_contents & 0xffff); |
| // break; |
| // case 32: |
| // scalar = (uint32_t)(arg_contents & 0xffffffff); |
| // break; |
| // case 64: |
| // scalar = (uint64_t)arg_contents; |
| // break; |
| // } |
| // } |
| // |
| // return true; |
| return false; |
| } |
| |
| bool |
| ABIMacOSX_arm::GetArgumentValues (Thread &thread, |
| ValueList &values) const |
| { |
| // unsigned int num_values = values.GetSize(); |
| // unsigned int value_index; |
| // |
| // // Extract the Clang AST context from the PC so that we can figure out type |
| // // sizes |
| // |
| // clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); |
| // |
| // // 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? |
| // switch (value->GetContextType()) |
| // { |
| // default: |
| // return false; |
| // case Value::eContextTypeClangType: |
| // { |
| // void *value_type = value->GetClangType(); |
| // bool is_signed; |
| // |
| // if (ClangASTContext::IsIntegerType (value_type, is_signed)) |
| // { |
| // size_t bit_width = ClangASTType::GetClangTypeBitWidth(ast_context, value_type); |
| // |
| // ReadIntegerArgument(value->GetScalar(), |
| // bit_width, |
| // is_signed, |
| // thread.GetProcess(), |
| // current_stack_argument); |
| // } |
| // else if (ClangASTContext::IsPointerType (value_type)) |
| // { |
| // ReadIntegerArgument(value->GetScalar(), |
| // 32, |
| // false, |
| // thread.GetProcess(), |
| // current_stack_argument); |
| // } |
| // } |
| // break; |
| // } |
| // } |
| // |
| // return true; |
| return false; |
| } |
| |
| bool |
| ABIMacOSX_arm::GetReturnValue (Thread &thread, |
| Value &value) const |
| { |
| switch (value.GetContextType()) |
| { |
| default: |
| return false; |
| case Value::eContextTypeClangType: |
| { |
| // Extract the Clang AST context from the PC so that we can figure out type |
| // sizes |
| |
| clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext(); |
| |
| // Get the pointer to the first stack argument so we have a place to start |
| // when reading data |
| |
| RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
| |
| void *value_type = value.GetClangType(); |
| bool is_signed; |
| |
| const RegisterInfo *r0_reg_info = reg_ctx->GetRegisterInfoByName("r0", 0); |
| if (ClangASTContext::IsIntegerType (value_type, is_signed)) |
| { |
| size_t bit_width = ClangASTType::GetClangTypeBitWidth(ast_context, value_type); |
| |
| switch (bit_width) |
| { |
| default: |
| return false; |
| 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 (ClangASTContext::IsPointerType (value_type)) |
| { |
| uint32_t ptr = thread.GetRegisterContext()->ReadRegisterAsUnsigned(r0_reg_info, 0) & UINT32_MAX; |
| value.GetScalar() = ptr; |
| } |
| else |
| { |
| // not handled yet |
| return false; |
| } |
| } |
| break; |
| } |
| |
| return true; |
| } |
| |
| 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; |
| |
| unwind_plan.Clear(); |
| unwind_plan.SetRegisterKind (eRegisterKindDWARF); |
| |
| UnwindPlan::Row row; |
| |
| // Our previous Call Frame Address is the stack pointer |
| row.SetCFARegister (sp_reg_num); |
| |
| // Our 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 (pluginName); |
| return true; |
| } |
| |
| bool |
| ABIMacOSX_arm::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 = dwarf_r7; // apple uses r7 for all frames. Normal arm uses r11 |
| sp_reg_num = dwarf_sp; |
| pc_reg_num = dwarf_pc; |
| 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::Row row; |
| const int32_t ptr_size = 8; |
| |
| unwind_plan.SetRegisterKind (eRegisterKindGeneric); |
| 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-darwin default unwind plan"); |
| return true; |
| } |
| |
| bool |
| ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info) |
| { |
| if (reg_info) |
| { |
| // Volatile registers include: ebx, ebp, esi, edi, esp, eip |
| 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-darwin only...) |
| |
| break; |
| } |
| } |
| else if (name[0] == 'd') |
| { |
| switch (name[1]) |
| { |
| case '0': |
| return name[2] == '\0'; // d0 |
| |
| case '1': |
| switch (name[2]) |
| { |
| case '\0': |
| return true; // d1; |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| return name[3] == '\0'; // d16 - d19 |
| default: |
| break; |
| } |
| break; |
| |
| case '2': |
| switch (name[2]) |
| { |
| case '\0': |
| return true; // d2; |
| 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 |
| default: |
| break; |
| } |
| break; |
| |
| case '3': |
| switch (name[2]) |
| { |
| case '\0': |
| return true; // d3; |
| case '0': |
| case '1': |
| return name[3] == '\0'; // d30 - d31 |
| default: |
| break; |
| } |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| return name[2] == '\0'; // d4 - d7 |
| |
| default: |
| break; |
| } |
| } |
| else if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') |
| return true; |
| } |
| return false; |
| } |
| |
| void |
| ABIMacOSX_arm::Initialize() |
| { |
| PluginManager::RegisterPlugin (pluginName, |
| pluginDesc, |
| CreateInstance); |
| } |
| |
| void |
| ABIMacOSX_arm::Terminate() |
| { |
| PluginManager::UnregisterPlugin (CreateInstance); |
| } |
| |
| //------------------------------------------------------------------ |
| // PluginInterface protocol |
| //------------------------------------------------------------------ |
| const char * |
| ABIMacOSX_arm::GetPluginName() |
| { |
| return pluginName; |
| } |
| |
| const char * |
| ABIMacOSX_arm::GetShortPluginName() |
| { |
| return pluginShort; |
| } |
| |
| uint32_t |
| ABIMacOSX_arm::GetPluginVersion() |
| { |
| return 1; |
| } |
| |