Path: blob/main/contrib/llvm-project/lldb/source/Plugins/ABI/X86/ABIMacOSX_i386.cpp
39653 views
//===-- ABIMacOSX_i386.cpp ------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "ABIMacOSX_i386.h"910#include <optional>11#include <vector>1213#include "llvm/ADT/STLExtras.h"14#include "llvm/TargetParser/Triple.h"1516#include "lldb/Core/Module.h"17#include "lldb/Core/PluginManager.h"18#include "lldb/Core/ValueObjectConstResult.h"19#include "lldb/Symbol/UnwindPlan.h"20#include "lldb/Target/Process.h"21#include "lldb/Target/RegisterContext.h"22#include "lldb/Target/Target.h"23#include "lldb/Target/Thread.h"24#include "lldb/Utility/ConstString.h"25#include "lldb/Utility/RegisterValue.h"26#include "lldb/Utility/Scalar.h"27#include "lldb/Utility/Status.h"2829using namespace lldb;30using namespace lldb_private;3132LLDB_PLUGIN_DEFINE(ABIMacOSX_i386)3334enum {35dwarf_eax = 0,36dwarf_ecx,37dwarf_edx,38dwarf_ebx,39dwarf_esp,40dwarf_ebp,41dwarf_esi,42dwarf_edi,43dwarf_eip,44};4546size_t ABIMacOSX_i386::GetRedZoneSize() const { return 0; }4748// Static Functions4950ABISP51ABIMacOSX_i386::CreateInstance(lldb::ProcessSP process_sp, const ArchSpec &arch) {52if ((arch.GetTriple().getArch() == llvm::Triple::x86) &&53(arch.GetTriple().isMacOSX() || arch.GetTriple().isiOS() ||54arch.GetTriple().isWatchOS())) {55return ABISP(56new ABIMacOSX_i386(std::move(process_sp), MakeMCRegisterInfo(arch)));57}58return ABISP();59}6061bool ABIMacOSX_i386::PrepareTrivialCall(Thread &thread, addr_t sp,62addr_t func_addr, addr_t return_addr,63llvm::ArrayRef<addr_t> args) const {64RegisterContext *reg_ctx = thread.GetRegisterContext().get();65if (!reg_ctx)66return false;67uint32_t pc_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(68eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);69uint32_t sp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(70eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);7172// When writing a register value down to memory, the register info used to73// write memory just needs to have the correct size of a 32 bit register, the74// actual register it pertains to is not important, just the size needs to be75// correct. Here we use "eax"...76const RegisterInfo *reg_info_32 = reg_ctx->GetRegisterInfoByName("eax");77if (!reg_info_32)78return false; // TODO this should actually never happen7980// Make room for the argument(s) on the stack8182Status error;83RegisterValue reg_value;8485// Write any arguments onto the stack86sp -= 4 * args.size();8788// Align the SP89sp &= ~(16ull - 1ull); // 16-byte alignment9091addr_t arg_pos = sp;9293for (addr_t arg : args) {94reg_value.SetUInt32(arg);95error = reg_ctx->WriteRegisterValueToMemory(96reg_info_32, arg_pos, reg_info_32->byte_size, reg_value);97if (error.Fail())98return false;99arg_pos += 4;100}101102// The return address is pushed onto the stack (yes after we just set the103// alignment above!).104sp -= 4;105reg_value.SetUInt32(return_addr);106error = reg_ctx->WriteRegisterValueToMemory(107reg_info_32, sp, reg_info_32->byte_size, reg_value);108if (error.Fail())109return false;110111// %esp is set to the actual stack value.112113if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp))114return false;115116// %eip is set to the address of the called function.117118if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_num, func_addr))119return false;120121return true;122}123124static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,125bool is_signed, Process *process,126addr_t ¤t_stack_argument) {127128uint32_t byte_size = (bit_width + (8 - 1)) / 8;129Status error;130if (process->ReadScalarIntegerFromMemory(current_stack_argument, byte_size,131is_signed, scalar, error)) {132current_stack_argument += byte_size;133return true;134}135return false;136}137138bool ABIMacOSX_i386::GetArgumentValues(Thread &thread,139ValueList &values) const {140unsigned int num_values = values.GetSize();141unsigned int value_index;142143// Get the pointer to the first stack argument so we have a place to start144// when reading data145146RegisterContext *reg_ctx = thread.GetRegisterContext().get();147148if (!reg_ctx)149return false;150151addr_t sp = reg_ctx->GetSP(0);152153if (!sp)154return false;155156addr_t current_stack_argument = sp + 4; // jump over return address157158for (value_index = 0; value_index < num_values; ++value_index) {159Value *value = values.GetValueAtIndex(value_index);160161if (!value)162return false;163164// We currently only support extracting values with Clang QualTypes. Do we165// care about others?166CompilerType compiler_type(value->GetCompilerType());167std::optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);168if (bit_size) {169bool is_signed;170if (compiler_type.IsIntegerOrEnumerationType(is_signed))171ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed,172thread.GetProcess().get(), current_stack_argument);173else if (compiler_type.IsPointerType())174ReadIntegerArgument(value->GetScalar(), *bit_size, false,175thread.GetProcess().get(), current_stack_argument);176}177}178179return true;180}181182Status ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp,183lldb::ValueObjectSP &new_value_sp) {184Status error;185if (!new_value_sp) {186error.SetErrorString("Empty value object for return value.");187return error;188}189190CompilerType compiler_type = new_value_sp->GetCompilerType();191if (!compiler_type) {192error.SetErrorString("Null clang type for return value.");193return error;194}195196Thread *thread = frame_sp->GetThread().get();197198bool is_signed;199uint32_t count;200bool is_complex;201202RegisterContext *reg_ctx = thread->GetRegisterContext().get();203204bool set_it_simple = false;205if (compiler_type.IsIntegerOrEnumerationType(is_signed) ||206compiler_type.IsPointerType()) {207DataExtractor data;208Status data_error;209size_t num_bytes = new_value_sp->GetData(data, data_error);210if (data_error.Fail()) {211error.SetErrorStringWithFormat(212"Couldn't convert return value to raw data: %s",213data_error.AsCString());214return error;215}216lldb::offset_t offset = 0;217if (num_bytes <= 8) {218const RegisterInfo *eax_info = reg_ctx->GetRegisterInfoByName("eax", 0);219if (num_bytes <= 4) {220uint32_t raw_value = data.GetMaxU32(&offset, num_bytes);221222if (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value))223set_it_simple = true;224} else {225uint32_t raw_value = data.GetMaxU32(&offset, 4);226227if (reg_ctx->WriteRegisterFromUnsigned(eax_info, raw_value)) {228const RegisterInfo *edx_info =229reg_ctx->GetRegisterInfoByName("edx", 0);230uint32_t raw_value = data.GetMaxU32(&offset, num_bytes - offset);231232if (reg_ctx->WriteRegisterFromUnsigned(edx_info, raw_value))233set_it_simple = true;234}235}236} else {237error.SetErrorString("We don't support returning longer than 64 bit "238"integer values at present.");239}240} else if (compiler_type.IsFloatingPointType(count, is_complex)) {241if (is_complex)242error.SetErrorString(243"We don't support returning complex values at present");244else245error.SetErrorString(246"We don't support returning float values at present");247}248249if (!set_it_simple)250error.SetErrorString(251"We only support setting simple integer return types at present.");252253return error;254}255256ValueObjectSP257ABIMacOSX_i386::GetReturnValueObjectImpl(Thread &thread,258CompilerType &compiler_type) const {259Value value;260ValueObjectSP return_valobj_sp;261262if (!compiler_type)263return return_valobj_sp;264265// value.SetContext (Value::eContextTypeClangType,266// compiler_type.GetOpaqueQualType());267value.SetCompilerType(compiler_type);268269RegisterContext *reg_ctx = thread.GetRegisterContext().get();270if (!reg_ctx)271return return_valobj_sp;272273bool is_signed;274275if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {276std::optional<uint64_t> bit_width = compiler_type.GetBitSize(&thread);277if (!bit_width)278return return_valobj_sp;279unsigned eax_id =280reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];281unsigned edx_id =282reg_ctx->GetRegisterInfoByName("edx", 0)->kinds[eRegisterKindLLDB];283284switch (*bit_width) {285default:286case 128:287// Scalar can't hold 128-bit literals, so we don't handle this288return return_valobj_sp;289case 64:290uint64_t raw_value;291raw_value =292thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &2930xffffffff;294raw_value |=295(thread.GetRegisterContext()->ReadRegisterAsUnsigned(edx_id, 0) &2960xffffffff)297<< 32;298if (is_signed)299value.GetScalar() = (int64_t)raw_value;300else301value.GetScalar() = (uint64_t)raw_value;302break;303case 32:304if (is_signed)305value.GetScalar() = (int32_t)(306thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &3070xffffffff);308else309value.GetScalar() = (uint32_t)(310thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &3110xffffffff);312break;313case 16:314if (is_signed)315value.GetScalar() = (int16_t)(316thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &3170xffff);318else319value.GetScalar() = (uint16_t)(320thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &3210xffff);322break;323case 8:324if (is_signed)325value.GetScalar() = (int8_t)(326thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &3270xff);328else329value.GetScalar() = (uint8_t)(330thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &3310xff);332break;333}334} else if (compiler_type.IsPointerType()) {335unsigned eax_id =336reg_ctx->GetRegisterInfoByName("eax", 0)->kinds[eRegisterKindLLDB];337uint32_t ptr =338thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) &3390xffffffff;340value.GetScalar() = ptr;341} else {342// not handled yet343return return_valobj_sp;344}345346// If we get here, we have a valid Value, so make our ValueObject out of it:347348return_valobj_sp = ValueObjectConstResult::Create(349thread.GetStackFrameAtIndex(0).get(), value, ConstString(""));350return return_valobj_sp;351}352353// This defines the CFA as esp+4354// the saved pc is at CFA-4 (i.e. esp+0)355// The saved esp is CFA+0356357bool ABIMacOSX_i386::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {358unwind_plan.Clear();359unwind_plan.SetRegisterKind(eRegisterKindDWARF);360361uint32_t sp_reg_num = dwarf_esp;362uint32_t pc_reg_num = dwarf_eip;363364UnwindPlan::RowSP row(new UnwindPlan::Row);365row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 4);366row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false);367row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);368unwind_plan.AppendRow(row);369unwind_plan.SetSourceName("i386 at-func-entry default");370unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);371return true;372}373374// This defines the CFA as ebp+8375// The saved pc is at CFA-4 (i.e. ebp+4)376// The saved ebp is at CFA-8 (i.e. ebp+0)377// The saved esp is CFA+0378379bool ABIMacOSX_i386::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {380unwind_plan.Clear();381unwind_plan.SetRegisterKind(eRegisterKindDWARF);382383uint32_t fp_reg_num = dwarf_ebp;384uint32_t sp_reg_num = dwarf_esp;385uint32_t pc_reg_num = dwarf_eip;386387UnwindPlan::RowSP row(new UnwindPlan::Row);388const int32_t ptr_size = 4;389390row->GetCFAValue().SetIsRegisterPlusOffset(fp_reg_num, 2 * ptr_size);391row->SetOffset(0);392row->SetUnspecifiedRegistersAreUndefined(true);393394row->SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, ptr_size * -2, true);395row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * -1, true);396row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);397398unwind_plan.AppendRow(row);399unwind_plan.SetSourceName("i386 default unwind plan");400unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);401unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);402unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo);403return true;404}405406bool ABIMacOSX_i386::RegisterIsVolatile(const RegisterInfo *reg_info) {407return !RegisterIsCalleeSaved(reg_info);408}409410// v.411// http://developer.apple.com/library/mac/#documentation/developertools/Conceptual/LowLevelABI/130412// -IA-413// 32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4414//415// This document ("OS X ABI Function Call Guide", chapter "IA-32 Function416// Calling Conventions") says that the following registers on i386 are417// preserved aka non-volatile aka callee-saved:418//419// ebx, ebp, esi, edi, esp420421bool ABIMacOSX_i386::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {422if (reg_info) {423// Saved registers are ebx, ebp, esi, edi, esp, eip424const char *name = reg_info->name;425if (name[0] == 'e') {426switch (name[1]) {427case 'b':428if (name[2] == 'x' || name[2] == 'p')429return name[3] == '\0';430break;431case 'd':432if (name[2] == 'i')433return name[3] == '\0';434break;435case 'i':436if (name[2] == 'p')437return name[3] == '\0';438break;439case 's':440if (name[2] == 'i' || name[2] == 'p')441return name[3] == '\0';442break;443}444}445if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp446return true;447if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp448return true;449if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc450return true;451}452return false;453}454455void ABIMacOSX_i386::Initialize() {456PluginManager::RegisterPlugin(457GetPluginNameStatic(), "Mac OS X ABI for i386 targets", CreateInstance);458}459460void ABIMacOSX_i386::Terminate() {461PluginManager::UnregisterPlugin(CreateInstance);462}463464465