| //===-- ABISysV_i386.cpp --------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| //===----------------------------------------------------------------------===// |
| |
| #include "ABISysV_i386.h" |
| |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/Triple.h" |
| |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/Value.h" |
| #include "lldb/Core/ValueObjectConstResult.h" |
| #include "lldb/Core/ValueObjectMemory.h" |
| #include "lldb/Core/ValueObjectRegister.h" |
| #include "lldb/Symbol/UnwindPlan.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/RegisterContext.h" |
| #include "lldb/Target/StackFrame.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| #include "lldb/Utility/ConstString.h" |
| #include "lldb/Utility/DataExtractor.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/RegisterValue.h" |
| #include "lldb/Utility/Status.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| LLDB_PLUGIN_DEFINE(ABISysV_i386) |
| |
| // This source file uses the following document as a reference: |
| //==================================================================== |
| // System V Application Binary Interface |
| // Intel386 Architecture Processor Supplement, Version 1.0 |
| // Edited by |
| // H.J. Lu, David L Kreitzer, Milind Girkar, Zia Ansari |
| // |
| // (Based on |
| // System V Application Binary Interface, |
| // AMD64 Architecture Processor Supplement, |
| // Edited by |
| // H.J. Lu, Michael Matz, Milind Girkar, Jan Hubicka, |
| // Andreas Jaeger, Mark Mitchell) |
| // |
| // February 3, 2015 |
| //==================================================================== |
| |
| // DWARF Register Number Mapping |
| // See Table 2.14 of the reference document (specified on top of this file) |
| // Comment: Table 2.14 is followed till 'mm' entries. After that, all entries |
| // are ignored here. |
| |
| enum dwarf_regnums { |
| dwarf_eax = 0, |
| dwarf_ecx, |
| dwarf_edx, |
| dwarf_ebx, |
| dwarf_esp, |
| dwarf_ebp, |
| dwarf_esi, |
| dwarf_edi, |
| dwarf_eip, |
| }; |
| |
| // Static Functions |
| |
| ABISP |
| ABISysV_i386::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) { |
| if (arch.GetTriple().getVendor() != llvm::Triple::Apple) { |
| if (arch.GetTriple().getArch() == llvm::Triple::x86) { |
| return ABISP( |
| new ABISysV_i386(std::move(process_sp), MakeMCRegisterInfo(arch))); |
| } |
| } |
| return ABISP(); |
| } |
| |
| bool ABISysV_i386::PrepareTrivialCall(Thread &thread, addr_t sp, |
| addr_t func_addr, addr_t return_addr, |
| llvm::ArrayRef<addr_t> 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); |
| |
| // While using register info to write a register value to memory, the |
| // register info 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. "eax" is used here for this purpose. |
| const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax"); |
| if (!reg_info_32) |
| return false; // TODO this should actually never happen |
| |
| Status error; |
| RegisterValue reg_value; |
| |
| // Make room for the argument(s) on the stack |
| sp -= 4 * args.size(); |
| |
| // SP Alignment |
| sp &= ~(16ull - 1ull); // 16-byte alignment |
| |
| // Write arguments onto the stack |
| 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 |
| 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; |
| |
| // Setting %esp to the actual stack value. |
| if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp)) |
| return false; |
| |
| // Setting %eip 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; |
| Status error; |
| |
| if (!process) |
| return false; |
| |
| if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, |
| is_signed, scalar, error)) { |
| current_stack_argument += byte_size; |
| return true; |
| } |
| return false; |
| } |
| |
| bool ABISysV_i386::GetArgumentValues(Thread &thread, ValueList &values) const { |
| unsigned int num_values = values.GetSize(); |
| unsigned int value_index; |
| |
| RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
| |
| if (!reg_ctx) |
| return false; |
| |
| // Get pointer to the first stack argument |
| 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; |
| |
| // Currently: Support for extracting values with Clang QualTypes only. |
| CompilerType compiler_type(value->GetCompilerType()); |
| llvm::Optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread); |
| if (bit_size) { |
| bool is_signed; |
| if (compiler_type.IsIntegerOrEnumerationType(is_signed)) { |
| ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, |
| thread.GetProcess().get(), current_stack_argument); |
| } else if (compiler_type.IsPointerType()) { |
| ReadIntegerArgument(value->GetScalar(), *bit_size, false, |
| thread.GetProcess().get(), current_stack_argument); |
| } |
| } |
| } |
| return true; |
| } |
| |
| Status ABISysV_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp, |
| lldb::ValueObjectSP &new_value_sp) { |
| Status 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; |
| } |
| |
| const uint32_t type_flags = compiler_type.GetTypeInfo(); |
| Thread *thread = frame_sp->GetThread().get(); |
| RegisterContext *reg_ctx = thread->GetRegisterContext().get(); |
| DataExtractor data; |
| Status data_error; |
| size_t num_bytes = new_value_sp->GetData(data, data_error); |
| bool register_write_successful = true; |
| |
| if (data_error.Fail()) { |
| error.SetErrorStringWithFormat( |
| "Couldn't convert return value to raw data: %s", |
| data_error.AsCString()); |
| return error; |
| } |
| |
| // Following "IF ELSE" block categorizes various 'Fundamental Data Types'. |
| // The terminology 'Fundamental Data Types' used here is adopted from Table |
| // 2.1 of the reference document (specified on top of this file) |
| |
| if (type_flags & eTypeIsPointer) // 'Pointer' |
| { |
| if (num_bytes != sizeof(uint32_t)) { |
| error.SetErrorString("Pointer to be returned is not 4 bytes wide"); |
| return error; |
| } |
| lldb::offset_t offset = 0; |
| const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0); |
| uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); |
| register_write_successful = |
| reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value); |
| } else if ((type_flags & eTypeIsScalar) || |
| (type_flags & eTypeIsEnumeration)) //'Integral' + 'Floating Point' |
| { |
| lldb::offset_t offset = 0; |
| const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0); |
| |
| if (type_flags & eTypeIsInteger) // 'Integral' except enum |
| { |
| switch (num_bytes) { |
| default: |
| break; |
| case 16: |
| // For clang::BuiltinType::UInt128 & Int128 ToDo: Need to decide how to |
| // handle it |
| break; |
| case 8: { |
| uint32_t raw_value_low = data.GetMaxU32(&offset, 4); |
| const RegisterInfo *edx_info = reg_ctx->GetRegisterInfoByName("edx", 0); |
| uint32_t raw_value_high = data.GetMaxU32(&offset, num_bytes - offset); |
| register_write_successful = |
| (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value_low) && |
| reg_ctx->WriteRegisterFromUnsigned(edx_info, raw_value_high)); |
| break; |
| } |
| case 4: |
| case 2: |
| case 1: { |
| uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); |
| register_write_successful = |
| reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value); |
| break; |
| } |
| } |
| } else if (type_flags & eTypeIsEnumeration) // handles enum |
| { |
| uint32_t raw_value = data.GetMaxU32(&offset, num_bytes); |
| register_write_successful = |
| reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value); |
| } else if (type_flags & eTypeIsFloat) // 'Floating Point' |
| { |
| RegisterValue st0_value, fstat_value, ftag_value; |
| const RegisterInfo *st0_info = reg_ctx->GetRegisterInfoByName("st0", 0); |
| const RegisterInfo *fstat_info = |
| reg_ctx->GetRegisterInfoByName("fstat", 0); |
| const RegisterInfo *ftag_info = reg_ctx->GetRegisterInfoByName("ftag", 0); |
| |
| /* According to Page 3-12 of document |
| System V Application Binary Interface, Intel386 Architecture Processor |
| Supplement, Fourth Edition |
| To return Floating Point values, all st% registers except st0 should be |
| empty after exiting from |
| a function. This requires setting fstat and ftag registers to specific |
| values. |
| fstat: The TOP field of fstat should be set to a value [0,7]. ABI doesn't |
| specify the specific |
| value of TOP in case of function return. Hence, we set the TOP field to 7 |
| by our choice. */ |
| uint32_t value_fstat_u32 = 0x00003800; |
| |
| /* ftag: Implication of setting TOP to 7 and indicating all st% registers |
| empty except st0 is to set |
| 7th bit of 4th byte of FXSAVE area to 1 and all other bits of this byte to |
| 0. This is in accordance |
| with the document Intel 64 and IA-32 Architectures Software Developer's |
| Manual, January 2015 */ |
| uint32_t value_ftag_u32 = 0x00000080; |
| |
| if (num_bytes <= 12) // handles float, double, long double, __float80 |
| { |
| long double value_long_dbl = 0.0; |
| if (num_bytes == 4) |
| value_long_dbl = data.GetFloat(&offset); |
| else if (num_bytes == 8) |
| value_long_dbl = data.GetDouble(&offset); |
| else if (num_bytes == 12) |
| value_long_dbl = data.GetLongDouble(&offset); |
| else { |
| error.SetErrorString("Invalid number of bytes for this return type"); |
| return error; |
| } |
| st0_value.SetLongDouble(value_long_dbl); |
| fstat_value.SetUInt32(value_fstat_u32); |
| ftag_value.SetUInt32(value_ftag_u32); |
| register_write_successful = |
| reg_ctx->WriteRegister(st0_info, st0_value) && |
| reg_ctx->WriteRegister(fstat_info, fstat_value) && |
| reg_ctx->WriteRegister(ftag_info, ftag_value); |
| } else if (num_bytes == 16) // handles __float128 |
| { |
| error.SetErrorString("Implementation is missing for this clang type."); |
| } |
| } else { |
| // Neither 'Integral' nor 'Floating Point'. If flow reaches here then |
| // check type_flags. This type_flags is not a valid type. |
| error.SetErrorString("Invalid clang type"); |
| } |
| } else { |
| /* 'Complex Floating Point', 'Packed', 'Decimal Floating Point' and |
| 'Aggregate' data types |
| are yet to be implemented */ |
| error.SetErrorString("Currently only Integral and Floating Point clang " |
| "types are supported."); |
| } |
| if (!register_write_successful) |
| error.SetErrorString("Register writing failed"); |
| return error; |
| } |
| |
| ValueObjectSP ABISysV_i386::GetReturnValueObjectSimple( |
| Thread &thread, CompilerType &return_compiler_type) const { |
| ValueObjectSP return_valobj_sp; |
| Value value; |
| |
| if (!return_compiler_type) |
| return return_valobj_sp; |
| |
| value.SetCompilerType(return_compiler_type); |
| |
| RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
| if (!reg_ctx) |
| return return_valobj_sp; |
| |
| const uint32_t type_flags = return_compiler_type.GetTypeInfo(); |
| |
| unsigned eax_id = |
| reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; |
| unsigned edx_id = |
| reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB]; |
| |
| // Following "IF ELSE" block categorizes various 'Fundamental Data Types'. |
| // The terminology 'Fundamental Data Types' used here is adopted from Table |
| // 2.1 of the reference document (specified on top of this file) |
| |
| if (type_flags & eTypeIsPointer) // 'Pointer' |
| { |
| uint32_t ptr = |
| thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
| 0xffffffff; |
| value.SetValueType(Value::eValueTypeScalar); |
| value.GetScalar() = ptr; |
| return_valobj_sp = ValueObjectConstResult::Create( |
| thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); |
| } else if ((type_flags & eTypeIsScalar) || |
| (type_flags & eTypeIsEnumeration)) //'Integral' + 'Floating Point' |
| { |
| value.SetValueType(Value::eValueTypeScalar); |
| llvm::Optional<uint64_t> byte_size = |
| return_compiler_type.GetByteSize(nullptr); |
| if (!byte_size) |
| return return_valobj_sp; |
| bool success = false; |
| |
| if (type_flags & eTypeIsInteger) // 'Integral' except enum |
| { |
| const bool is_signed = ((type_flags & eTypeIsSigned) != 0); |
| uint64_t raw_value = |
| thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
| 0xffffffff; |
| raw_value |= |
| (thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) & |
| 0xffffffff) |
| << 32; |
| |
| switch (*byte_size) { |
| default: |
| break; |
| |
| case 16: |
| // For clang::BuiltinType::UInt128 & Int128 ToDo: Need to decide how to |
| // handle it |
| break; |
| |
| case 8: |
| if (is_signed) |
| value.GetScalar() = (int64_t)(raw_value); |
| else |
| value.GetScalar() = (uint64_t)(raw_value); |
| success = true; |
| break; |
| |
| case 4: |
| if (is_signed) |
| value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); |
| else |
| value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); |
| success = true; |
| break; |
| |
| case 2: |
| if (is_signed) |
| value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); |
| else |
| value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); |
| success = true; |
| break; |
| |
| case 1: |
| if (is_signed) |
| value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); |
| else |
| value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); |
| success = true; |
| break; |
| } |
| |
| if (success) |
| return_valobj_sp = ValueObjectConstResult::Create( |
| thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); |
| } else if (type_flags & eTypeIsEnumeration) // handles enum |
| { |
| uint32_t enm = |
| thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
| 0xffffffff; |
| value.SetValueType(Value::eValueTypeScalar); |
| value.GetScalar() = enm; |
| return_valobj_sp = ValueObjectConstResult::Create( |
| thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); |
| } else if (type_flags & eTypeIsFloat) // 'Floating Point' |
| { |
| if (*byte_size <= 12) // handles float, double, long double, __float80 |
| { |
| const RegisterInfo *st0_info = reg_ctx->GetRegisterInfoByName("st0", 0); |
| RegisterValue st0_value; |
| |
| if (reg_ctx->ReadRegister(st0_info, st0_value)) { |
| DataExtractor data; |
| if (st0_value.GetData(data)) { |
| lldb::offset_t offset = 0; |
| long double value_long_double = data.GetLongDouble(&offset); |
| |
| // float is 4 bytes. |
| if (*byte_size == 4) { |
| float value_float = (float)value_long_double; |
| value.GetScalar() = value_float; |
| success = true; |
| } else if (*byte_size == 8) { |
| // double is 8 bytes |
| // On Android Platform: long double is also 8 bytes It will be |
| // handled here only. |
| double value_double = (double)value_long_double; |
| value.GetScalar() = value_double; |
| success = true; |
| } else if (*byte_size == 12) { |
| // long double and __float80 are 12 bytes on i386. |
| value.GetScalar() = value_long_double; |
| success = true; |
| } |
| } |
| } |
| |
| if (success) |
| return_valobj_sp = ValueObjectConstResult::Create( |
| thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); |
| } else if (*byte_size == 16) // handles __float128 |
| { |
| lldb::addr_t storage_addr = (uint32_t)( |
| thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
| 0xffffffff); |
| return_valobj_sp = ValueObjectMemory::Create( |
| &thread, "", Address(storage_addr, nullptr), return_compiler_type); |
| } |
| } else // Neither 'Integral' nor 'Floating Point' |
| { |
| // If flow reaches here then check type_flags This type_flags is |
| // unhandled |
| } |
| } else if (type_flags & eTypeIsComplex) // 'Complex Floating Point' |
| { |
| // ToDo: Yet to be implemented |
| } else if (type_flags & eTypeIsVector) // 'Packed' |
| { |
| llvm::Optional<uint64_t> byte_size = |
| return_compiler_type.GetByteSize(nullptr); |
| if (byte_size && *byte_size > 0) { |
| const RegisterInfo *vec_reg = reg_ctx->GetRegisterInfoByName("xmm0", 0); |
| if (vec_reg == nullptr) |
| vec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0); |
| |
| if (vec_reg) { |
| if (*byte_size <= vec_reg->byte_size) { |
| ProcessSP process_sp(thread.GetProcess()); |
| if (process_sp) { |
| std::unique_ptr<DataBufferHeap> heap_data_up( |
| new DataBufferHeap(*byte_size, 0)); |
| const ByteOrder byte_order = process_sp->GetByteOrder(); |
| RegisterValue reg_value; |
| if (reg_ctx->ReadRegister(vec_reg, reg_value)) { |
| Status error; |
| if (reg_value.GetAsMemoryData(vec_reg, heap_data_up->GetBytes(), |
| heap_data_up->GetByteSize(), |
| byte_order, error)) { |
| DataExtractor data(DataBufferSP(heap_data_up.release()), |
| byte_order, |
| process_sp->GetTarget() |
| .GetArchitecture() |
| .GetAddressByteSize()); |
| return_valobj_sp = ValueObjectConstResult::Create( |
| &thread, return_compiler_type, ConstString(""), data); |
| } |
| } |
| } |
| } else if (*byte_size <= vec_reg->byte_size * 2) { |
| const RegisterInfo *vec_reg2 = |
| reg_ctx->GetRegisterInfoByName("xmm1", 0); |
| if (vec_reg2) { |
| ProcessSP process_sp(thread.GetProcess()); |
| if (process_sp) { |
| std::unique_ptr<DataBufferHeap> heap_data_up( |
| new DataBufferHeap(*byte_size, 0)); |
| const ByteOrder byte_order = process_sp->GetByteOrder(); |
| RegisterValue reg_value; |
| RegisterValue reg_value2; |
| if (reg_ctx->ReadRegister(vec_reg, reg_value) && |
| reg_ctx->ReadRegister(vec_reg2, reg_value2)) { |
| |
| Status error; |
| if (reg_value.GetAsMemoryData(vec_reg, heap_data_up->GetBytes(), |
| vec_reg->byte_size, byte_order, |
| error) && |
| reg_value2.GetAsMemoryData( |
| vec_reg2, heap_data_up->GetBytes() + vec_reg->byte_size, |
| heap_data_up->GetByteSize() - vec_reg->byte_size, |
| byte_order, error)) { |
| DataExtractor data(DataBufferSP(heap_data_up.release()), |
| byte_order, |
| process_sp->GetTarget() |
| .GetArchitecture() |
| .GetAddressByteSize()); |
| return_valobj_sp = ValueObjectConstResult::Create( |
| &thread, return_compiler_type, ConstString(""), data); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } else // 'Decimal Floating Point' |
| { |
| // ToDo: Yet to be implemented |
| } |
| return return_valobj_sp; |
| } |
| |
| ValueObjectSP ABISysV_i386::GetReturnValueObjectImpl( |
| Thread &thread, CompilerType &return_compiler_type) const { |
| ValueObjectSP return_valobj_sp; |
| |
| if (!return_compiler_type) |
| return return_valobj_sp; |
| |
| ExecutionContext exe_ctx(thread.shared_from_this()); |
| return_valobj_sp = GetReturnValueObjectSimple(thread, return_compiler_type); |
| if (return_valobj_sp) |
| return return_valobj_sp; |
| |
| RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); |
| if (!reg_ctx_sp) |
| return return_valobj_sp; |
| |
| if (return_compiler_type.IsAggregateType()) { |
| unsigned eax_id = |
| reg_ctx_sp->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB]; |
| lldb::addr_t storage_addr = (uint32_t)( |
| thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & |
| 0xffffffff); |
| return_valobj_sp = ValueObjectMemory::Create( |
| &thread, "", Address(storage_addr, nullptr), return_compiler_type); |
| } |
| |
| return return_valobj_sp; |
| } |
| |
| // This defines CFA as esp+4 |
| // The saved pc is at CFA-4 (i.e. esp+0) |
| // The saved esp is CFA+0 |
| |
| bool ABISysV_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 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 ABISysV_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); |
| unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
| return true; |
| } |
| |
| // According to "Register Usage" in reference document (specified on top of |
| // this source file) ebx, ebp, esi, edi and esp registers are preserved i.e. |
| // non-volatile i.e. callee-saved on i386 |
| bool ABISysV_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { |
| if (!reg_info) |
| return false; |
| |
| // 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 ABISysV_i386::Initialize() { |
| PluginManager::RegisterPlugin( |
| GetPluginNameStatic(), "System V ABI for i386 targets", CreateInstance); |
| } |
| |
| void ABISysV_i386::Terminate() { |
| PluginManager::UnregisterPlugin(CreateInstance); |
| } |
| |
| // PluginInterface protocol |
| |
| lldb_private::ConstString ABISysV_i386::GetPluginNameStatic() { |
| static ConstString g_name("sysv-i386"); |
| return g_name; |
| } |
| |
| lldb_private::ConstString ABISysV_i386::GetPluginName() { |
| return GetPluginNameStatic(); |
| } |