//===-- 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; }