//===-- 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" // C Includes // C++ Includes #include // Other libraries and framework includes #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" // Project includes #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/UnwindPlan.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; enum { ehframe_eax = 0, ehframe_ecx, ehframe_edx, ehframe_ebx, ehframe_ebp, // Different from DWARF the regnums - eh_frame esp/ebp had their // regnums switched on i386 darwin ehframe_esp, // Different from DWARF the regnums - eh_frame esp/ebp had their // regnums switched on i386 darwin ehframe_esi, ehframe_edi, ehframe_eip, ehframe_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 }; static RegisterInfo g_register_infos[] = { // NAME ALT SZ OFF ENCODING FORMAT // EH_FRAME DWARF GENERIC // PROCESS PLUGIN LLDB NATIVE // ====== ======= == === ============= ============ // ===================== ===================== ============================ // ==================== ====================== {"eax", nullptr, 4, 0, eEncodingUint, eFormatHex, {ehframe_eax, dwarf_eax, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ebx", nullptr, 4, 0, eEncodingUint, eFormatHex, {ehframe_ebx, dwarf_ebx, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ecx", nullptr, 4, 0, eEncodingUint, eFormatHex, {ehframe_ecx, dwarf_ecx, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"edx", nullptr, 4, 0, eEncodingUint, eFormatHex, {ehframe_edx, dwarf_edx, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"esi", nullptr, 4, 0, eEncodingUint, eFormatHex, {ehframe_esi, dwarf_esi, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"edi", nullptr, 4, 0, eEncodingUint, eFormatHex, {ehframe_edi, dwarf_edi, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ebp", "fp", 4, 0, eEncodingUint, eFormatHex, {ehframe_ebp, dwarf_ebp, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"esp", "sp", 4, 0, eEncodingUint, eFormatHex, {ehframe_esp, dwarf_esp, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"eip", "pc", 4, 0, eEncodingUint, eFormatHex, {ehframe_eip, dwarf_eip, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"eflags", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"cs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ss", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ds", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"es", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"gs", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"stmm0", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_stmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"stmm1", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_stmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"stmm2", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_stmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"stmm3", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_stmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"stmm4", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_stmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"stmm5", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_stmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"stmm6", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_stmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"stmm7", nullptr, 10, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_stmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fctrl", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fstat", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ftag", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fiseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fioff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"foseg", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fooff", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"fop", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"xmm0", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"xmm1", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"xmm2", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"xmm3", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"xmm4", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"xmm5", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"xmm6", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"xmm7", nullptr, 16, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_xmm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"mxcsr", nullptr, 4, 0, eEncodingUint, eFormatHex, {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ymm0", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm0, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ymm1", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm1, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ymm2", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm2, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ymm3", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm3, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ymm4", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm4, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ymm5", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm5, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ymm6", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm6, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}, {"ymm7", nullptr, 32, 0, eEncodingVector, eFormatVectorOfUInt8, {LLDB_INVALID_REGNUM, dwarf_ymm7, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, nullptr, nullptr, nullptr, 0}}; static const uint32_t k_num_register_infos = llvm::array_lengthof(g_register_infos); 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) && (arch.GetTriple().isMacOSX() || arch.GetTriple().isiOS() || arch.GetTriple().isWatchOS())) { 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, llvm::ArrayRef args) 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 sp -= 4 * args.size(); // Align the SP sp &= ~(16ull - 1ull); // 16-byte alignment addr_t arg_pos = sp; for (addr_t arg : args) { reg_value.SetUInt32(arg); error = reg_ctx->WriteRegisterValueToMemory( reg_info_32, arg_pos, reg_info_32->byte_size, reg_value); if (error.Fail()) return false; arg_pos += 4; } // 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; } 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? CompilerType compiler_type(value->GetCompilerType()); if (compiler_type) { bool is_signed; if (compiler_type.IsIntegerOrEnumerationType(is_signed)) { ReadIntegerArgument(value->GetScalar(), compiler_type.GetBitSize(&thread), is_signed, thread.GetProcess().get(), current_stack_argument); } else if (compiler_type.IsPointerType()) { ReadIntegerArgument(value->GetScalar(), compiler_type.GetBitSize(&thread), 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; } CompilerType compiler_type = new_value_sp->GetCompilerType(); if (!compiler_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 (compiler_type.IsIntegerOrEnumerationType(is_signed) || compiler_type.IsPointerType()) { DataExtractor data; Error data_error; size_t num_bytes = new_value_sp->GetData(data, data_error); if (data_error.Fail()) { error.SetErrorStringWithFormat( "Couldn't convert return value to raw data: %s", data_error.AsCString()); return error; } 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 (compiler_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, CompilerType &compiler_type) const { Value value; ValueObjectSP return_valobj_sp; if (!compiler_type) return return_valobj_sp; // value.SetContext (Value::eContextTypeClangType, // compiler_type.GetOpaqueQualType()); value.SetCompilerType(compiler_type); RegisterContext *reg_ctx = thread.GetRegisterContext().get(); if (!reg_ctx) return return_valobj_sp; bool is_signed; if (compiler_type.IsIntegerOrEnumerationType(is_signed)) { size_t bit_width = compiler_type.GetBitSize(&thread); 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 (compiler_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; } // This defines the CFA as esp+4 // the saved pc is at CFA-4 (i.e. esp+0) // The saved esp is CFA+0 bool ABIMacOSX_i386::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindDWARF); uint32_t sp_reg_num = dwarf_esp; uint32_t pc_reg_num = dwarf_eip; UnwindPlan::RowSP row(new UnwindPlan::Row); row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 4); row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false); row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); unwind_plan.AppendRow(row); unwind_plan.SetSourceName("i386 at-func-entry default"); unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); return true; } // This defines the CFA as ebp+8 // The saved pc is at CFA-4 (i.e. ebp+4) // The saved ebp is at CFA-8 (i.e. ebp+0) // The saved esp is CFA+0 bool ABIMacOSX_i386::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) { unwind_plan.Clear(); unwind_plan.SetRegisterKind(eRegisterKindDWARF); 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; row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 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->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 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 // // This document ("OS X ABI Function Call Guide", chapter "IA-32 Function // Calling Conventions") // says that the following registers on i386 are preserved aka non-volatile aka // callee-saved: // // ebx, ebp, esi, edi, esp 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; }