forked from OSchip/llvm-project
619 lines
20 KiB
C++
619 lines
20 KiB
C++
//===-- 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/Scalar.h"
|
|
#include "lldb/Symbol/ClangASTContext.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;
|
|
|
|
static const char *pluginName = "ABIMacOSX_i386";
|
|
static const char *pluginDesc = "Mac OS X ABI for i386 targets";
|
|
static const char *pluginShort = "abi.macosx-i386";
|
|
|
|
size_t
|
|
ABIMacOSX_i386::GetRedZoneSize () const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// Static Functions
|
|
//------------------------------------------------------------------
|
|
lldb_private::ABI *
|
|
ABIMacOSX_i386::CreateInstance (const ConstString &triple)
|
|
{
|
|
llvm::StringRef tripleStr(triple.GetCString());
|
|
llvm::Triple llvmTriple(tripleStr);
|
|
|
|
if (llvmTriple.getArch() != llvm::Triple::x86)
|
|
return NULL;
|
|
|
|
return new ABIMacOSX_i386;
|
|
}
|
|
|
|
bool
|
|
ABIMacOSX_i386::PrepareTrivialCall (Thread &thread,
|
|
lldb::addr_t sp,
|
|
lldb::addr_t functionAddress,
|
|
lldb::addr_t returnAddress,
|
|
lldb::addr_t arg,
|
|
lldb::addr_t *this_arg,
|
|
lldb::addr_t *cmd_arg) const
|
|
{
|
|
RegisterContext *reg_ctx = thread.GetRegisterContext();
|
|
if (!reg_ctx)
|
|
return false;
|
|
#define CHAIN_EBP
|
|
|
|
#ifndef CHAIN_EBP
|
|
uint32_t ebpID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP);
|
|
#endif
|
|
uint32_t eipID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
|
uint32_t espID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
|
|
|
|
// Make room for the argument(s) on the stack
|
|
|
|
if (this_arg && cmd_arg)
|
|
sp -= 12;
|
|
else if (this_arg)
|
|
sp -= 8;
|
|
else
|
|
sp -= 4;
|
|
|
|
// Align the SP
|
|
|
|
sp &= ~(0xfull); // 16-byte alignment
|
|
|
|
// Write the argument on the stack
|
|
|
|
Error error;
|
|
|
|
if (this_arg && cmd_arg)
|
|
{
|
|
uint32_t cmd_argU32 = *cmd_arg & 0xffffffffull;
|
|
uint32_t this_argU32 = *this_arg & 0xffffffffull;
|
|
uint32_t argU32 = arg & 0xffffffffull;
|
|
|
|
if (thread.GetProcess().WriteMemory(sp, &this_argU32, sizeof(this_argU32), error) != sizeof(this_argU32))
|
|
return false;
|
|
if (thread.GetProcess().WriteMemory(sp, &cmd_argU32, sizeof(cmd_argU32), error) != sizeof(cmd_argU32))
|
|
return false;
|
|
if (thread.GetProcess().WriteMemory(sp + 4, &argU32, sizeof(argU32), error) != sizeof(argU32))
|
|
return false;
|
|
}
|
|
if (this_arg)
|
|
{
|
|
uint32_t this_argU32 = *this_arg & 0xffffffffull;
|
|
uint32_t argU32 = arg & 0xffffffffull;
|
|
|
|
if (thread.GetProcess().WriteMemory(sp, &this_argU32, sizeof(this_argU32), error) != sizeof(this_argU32))
|
|
return false;
|
|
if (thread.GetProcess().WriteMemory(sp + 4, &argU32, sizeof(argU32), error) != sizeof(argU32))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
uint32_t argU32 = arg & 0xffffffffull;
|
|
|
|
if (thread.GetProcess().WriteMemory (sp, &argU32, sizeof(argU32), error) != sizeof(argU32))
|
|
return false;
|
|
}
|
|
|
|
// The return address is pushed onto the stack.
|
|
|
|
sp -= 4;
|
|
uint32_t returnAddressU32 = returnAddress;
|
|
if (thread.GetProcess().WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32))
|
|
return false;
|
|
|
|
// %esp is set to the actual stack value.
|
|
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(espID, sp))
|
|
return false;
|
|
|
|
#ifndef CHAIN_EBP
|
|
// %ebp is set to a fake value, in our case 0x0x00000000
|
|
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(ebpID, 0x00000000))
|
|
return false;
|
|
#endif
|
|
|
|
// %eip is set to the address of the called function.
|
|
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(eipID, functionAddress))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ABIMacOSX_i386::PrepareNormalCall (Thread &thread,
|
|
lldb::addr_t sp,
|
|
lldb::addr_t functionAddress,
|
|
lldb::addr_t returnAddress,
|
|
ValueList &args) const
|
|
{
|
|
RegisterContext *reg_ctx = thread.GetRegisterContext();
|
|
if (!reg_ctx)
|
|
return false;
|
|
Error error;
|
|
uint32_t ebpID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP);
|
|
uint32_t eipID = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
|
uint32_t espID = 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:
|
|
default:
|
|
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:
|
|
switch (val->GetContextType())
|
|
{
|
|
default:
|
|
return false;
|
|
case Value::eContextTypeClangType:
|
|
{
|
|
void *val_type = val->GetClangType();
|
|
uint32_t cstr_length;
|
|
|
|
if (ClangASTContext::IsCStringType (val_type, 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 (thread.GetProcess().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 &= ~(0xfull); // 16-byte alignment
|
|
|
|
// Write the arguments on the stack
|
|
|
|
size_t numChunks = argLayout.size();
|
|
|
|
for (index = 0; index < numChunks; ++index)
|
|
if (thread.GetProcess().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 = returnAddress;
|
|
if (thread.GetProcess().WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32))
|
|
return false;
|
|
|
|
// %esp is set to the actual stack value.
|
|
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(espID, sp))
|
|
return false;
|
|
|
|
// %ebp is set to a fake value, in our case 0x0x00000000
|
|
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(ebpID, 0x00000000))
|
|
return false;
|
|
|
|
// %eip is set to the address of the called function.
|
|
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(eipID, functionAddress))
|
|
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;
|
|
}
|
|
|
|
bool
|
|
ABIMacOSX_i386::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();
|
|
|
|
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;
|
|
}
|
|
|
|
bool
|
|
ABIMacOSX_i386::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();
|
|
|
|
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);
|
|
|
|
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 false;
|
|
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 (ClangASTContext::IsPointerType (value_type))
|
|
{
|
|
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 false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ABIMacOSX_i386::Initialize()
|
|
{
|
|
PluginManager::RegisterPlugin (pluginName,
|
|
pluginDesc,
|
|
CreateInstance);
|
|
}
|
|
|
|
void
|
|
ABIMacOSX_i386::Terminate()
|
|
{
|
|
PluginManager::UnregisterPlugin (CreateInstance);
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// PluginInterface protocol
|
|
//------------------------------------------------------------------
|
|
const char *
|
|
ABIMacOSX_i386::GetPluginName()
|
|
{
|
|
return pluginName;
|
|
}
|
|
|
|
const char *
|
|
ABIMacOSX_i386::GetShortPluginName()
|
|
{
|
|
return pluginShort;
|
|
}
|
|
|
|
uint32_t
|
|
ABIMacOSX_i386::GetPluginVersion()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
ABIMacOSX_i386::GetPluginCommandHelp (const char *command, Stream *strm)
|
|
{
|
|
}
|
|
|
|
Error
|
|
ABIMacOSX_i386::ExecutePluginCommand (Args &command, Stream *strm)
|
|
{
|
|
Error error;
|
|
error.SetErrorString("No plug-in command are currently supported.");
|
|
return error;
|
|
}
|
|
|
|
Log *
|
|
ABIMacOSX_i386::EnablePluginLogging (Stream *strm, Args &command)
|
|
{
|
|
return NULL;
|
|
}
|