Change GetNumChildren()/CalculateNumChildren() methods return llvm::Expected This is an NFC change that does not yet add any error handling or change any code to return any errors. This is the second big change in the patch series started with https://github.com/llvm/llvm-project/pull/83501 A follow-up PR will wire up error handling.
1082 lines
33 KiB
C++
1082 lines
33 KiB
C++
//===-- ABISysV_ppc64.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_ppc64.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
|
|
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
|
#include "Utility/PPC64LE_DWARF_Registers.h"
|
|
#include "Utility/PPC64_DWARF_Registers.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/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/RegisterValue.h"
|
|
#include "lldb/Utility/Status.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/AST/Decl.h"
|
|
|
|
#define DECLARE_REGISTER_INFOS_PPC64_STRUCT
|
|
#include "Plugins/Process/Utility/RegisterInfos_ppc64.h"
|
|
#undef DECLARE_REGISTER_INFOS_PPC64_STRUCT
|
|
|
|
#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
|
|
#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h"
|
|
#undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
|
|
#include <optional>
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
LLDB_PLUGIN_DEFINE(ABISysV_ppc64)
|
|
|
|
const lldb_private::RegisterInfo *
|
|
ABISysV_ppc64::GetRegisterInfoArray(uint32_t &count) {
|
|
if (GetByteOrder() == lldb::eByteOrderLittle) {
|
|
count = std::size(g_register_infos_ppc64le);
|
|
return g_register_infos_ppc64le;
|
|
} else {
|
|
count = std::size(g_register_infos_ppc64);
|
|
return g_register_infos_ppc64;
|
|
}
|
|
}
|
|
|
|
size_t ABISysV_ppc64::GetRedZoneSize() const { return 224; }
|
|
|
|
lldb::ByteOrder ABISysV_ppc64::GetByteOrder() const {
|
|
return GetProcessSP()->GetByteOrder();
|
|
}
|
|
|
|
// Static Functions
|
|
|
|
ABISP
|
|
ABISysV_ppc64::CreateInstance(lldb::ProcessSP process_sp,
|
|
const ArchSpec &arch) {
|
|
if (arch.GetTriple().isPPC64())
|
|
return ABISP(
|
|
new ABISysV_ppc64(std::move(process_sp), MakeMCRegisterInfo(arch)));
|
|
return ABISP();
|
|
}
|
|
|
|
bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp,
|
|
addr_t func_addr, addr_t return_addr,
|
|
llvm::ArrayRef<addr_t> args) const {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
|
|
if (log) {
|
|
StreamString s;
|
|
s.Printf("ABISysV_ppc64::PrepareTrivialCall (tid = 0x%" PRIx64
|
|
", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64
|
|
", return_addr = 0x%" PRIx64,
|
|
thread.GetID(), (uint64_t)sp, (uint64_t)func_addr,
|
|
(uint64_t)return_addr);
|
|
|
|
for (size_t i = 0; i < args.size(); ++i)
|
|
s.Printf(", arg%" PRIu64 " = 0x%" PRIx64, static_cast<uint64_t>(i + 1),
|
|
args[i]);
|
|
s.PutCString(")");
|
|
log->PutString(s.GetString());
|
|
}
|
|
|
|
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
|
|
if (!reg_ctx)
|
|
return false;
|
|
|
|
const RegisterInfo *reg_info = nullptr;
|
|
|
|
if (args.size() > 8) // TODO handle more than 8 arguments
|
|
return false;
|
|
|
|
for (size_t i = 0; i < args.size(); ++i) {
|
|
reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric,
|
|
LLDB_REGNUM_GENERIC_ARG1 + i);
|
|
LLDB_LOGF(log, "About to write arg%" PRIu64 " (0x%" PRIx64 ") into %s",
|
|
static_cast<uint64_t>(i + 1), args[i], reg_info->name);
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
|
|
return false;
|
|
}
|
|
|
|
// First, align the SP
|
|
|
|
LLDB_LOGF(log, "16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64,
|
|
(uint64_t)sp, (uint64_t)(sp & ~0xfull));
|
|
|
|
sp &= ~(0xfull); // 16-byte alignment
|
|
|
|
sp -= 544; // allocate frame to save TOC, RA and SP.
|
|
|
|
Status error;
|
|
uint64_t reg_value;
|
|
const RegisterInfo *pc_reg_info =
|
|
reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
|
|
const RegisterInfo *sp_reg_info =
|
|
reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
|
|
ProcessSP process_sp(thread.GetProcess());
|
|
const RegisterInfo *lr_reg_info =
|
|
reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
|
|
const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoAtIndex(2);
|
|
const RegisterInfo *r12_reg_info = reg_ctx->GetRegisterInfoAtIndex(12);
|
|
|
|
// Save return address onto the stack.
|
|
LLDB_LOGF(log,
|
|
"Pushing the return address onto the stack: 0x%" PRIx64
|
|
"(+16): 0x%" PRIx64,
|
|
(uint64_t)sp, (uint64_t)return_addr);
|
|
if (!process_sp->WritePointerToMemory(sp + 16, return_addr, error))
|
|
return false;
|
|
|
|
// Write the return address to link register.
|
|
LLDB_LOGF(log, "Writing LR: 0x%" PRIx64, (uint64_t)return_addr);
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(lr_reg_info, return_addr))
|
|
return false;
|
|
|
|
// Write target address to %r12 register.
|
|
LLDB_LOGF(log, "Writing R12: 0x%" PRIx64, (uint64_t)func_addr);
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(r12_reg_info, func_addr))
|
|
return false;
|
|
|
|
// Read TOC pointer value.
|
|
reg_value = reg_ctx->ReadRegisterAsUnsigned(r2_reg_info, 0);
|
|
|
|
// Write TOC pointer onto the stack.
|
|
uint64_t stack_offset;
|
|
if (GetByteOrder() == lldb::eByteOrderLittle)
|
|
stack_offset = 24;
|
|
else
|
|
stack_offset = 40;
|
|
|
|
LLDB_LOGF(log, "Writing R2 (TOC) at SP(0x%" PRIx64 ")+%d: 0x%" PRIx64,
|
|
(uint64_t)(sp + stack_offset), (int)stack_offset,
|
|
(uint64_t)reg_value);
|
|
if (!process_sp->WritePointerToMemory(sp + stack_offset, reg_value, error))
|
|
return false;
|
|
|
|
// Read the current SP value.
|
|
reg_value = reg_ctx->ReadRegisterAsUnsigned(sp_reg_info, 0);
|
|
|
|
// Save current SP onto the stack.
|
|
LLDB_LOGF(log, "Writing SP at SP(0x%" PRIx64 ")+0: 0x%" PRIx64, (uint64_t)sp,
|
|
(uint64_t)reg_value);
|
|
if (!process_sp->WritePointerToMemory(sp, reg_value, error))
|
|
return false;
|
|
|
|
// %r1 is set to the actual stack value.
|
|
LLDB_LOGF(log, "Writing SP: 0x%" PRIx64, (uint64_t)sp);
|
|
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_info, sp))
|
|
return false;
|
|
|
|
// %pc is set to the address of the called function.
|
|
|
|
LLDB_LOGF(log, "Writing IP: 0x%" PRIx64, (uint64_t)func_addr);
|
|
|
|
if (!reg_ctx->WriteRegisterFromUnsigned(pc_reg_info, func_addr))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width,
|
|
bool is_signed, Thread &thread,
|
|
uint32_t *argument_register_ids,
|
|
unsigned int ¤t_argument_register,
|
|
addr_t ¤t_stack_argument) {
|
|
if (bit_width > 64)
|
|
return false; // Scalar can't hold large integer arguments
|
|
|
|
if (current_argument_register < 6) {
|
|
scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(
|
|
argument_register_ids[current_argument_register], 0);
|
|
current_argument_register++;
|
|
if (is_signed)
|
|
scalar.SignExtend(bit_width);
|
|
} else {
|
|
uint32_t byte_size = (bit_width + (8 - 1)) / 8;
|
|
Status error;
|
|
if (thread.GetProcess()->ReadScalarIntegerFromMemory(
|
|
current_stack_argument, byte_size, is_signed, scalar, error)) {
|
|
current_stack_argument += byte_size;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ABISysV_ppc64::GetArgumentValues(Thread &thread, ValueList &values) const {
|
|
unsigned int num_values = values.GetSize();
|
|
unsigned int value_index;
|
|
|
|
// Extract the register context so we can read arguments from registers
|
|
|
|
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
|
|
|
|
if (!reg_ctx)
|
|
return false;
|
|
|
|
// Get the pointer to the first stack argument so we have a place to start
|
|
// when reading data
|
|
|
|
addr_t sp = reg_ctx->GetSP(0);
|
|
|
|
if (!sp)
|
|
return false;
|
|
|
|
uint64_t stack_offset;
|
|
if (GetByteOrder() == lldb::eByteOrderLittle)
|
|
stack_offset = 32;
|
|
else
|
|
stack_offset = 48;
|
|
|
|
// jump over return address.
|
|
addr_t current_stack_argument = sp + stack_offset;
|
|
uint32_t argument_register_ids[8];
|
|
|
|
for (size_t i = 0; i < 8; ++i) {
|
|
argument_register_ids[i] =
|
|
reg_ctx
|
|
->GetRegisterInfo(eRegisterKindGeneric,
|
|
LLDB_REGNUM_GENERIC_ARG1 + i)
|
|
->kinds[eRegisterKindLLDB];
|
|
}
|
|
|
|
unsigned int current_argument_register = 0;
|
|
|
|
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();
|
|
std::optional<uint64_t> bit_size = compiler_type.GetBitSize(&thread);
|
|
if (!bit_size)
|
|
return false;
|
|
bool is_signed;
|
|
|
|
if (compiler_type.IsIntegerOrEnumerationType(is_signed)) {
|
|
ReadIntegerArgument(value->GetScalar(), *bit_size, is_signed, thread,
|
|
argument_register_ids, current_argument_register,
|
|
current_stack_argument);
|
|
} else if (compiler_type.IsPointerType()) {
|
|
ReadIntegerArgument(value->GetScalar(), *bit_size, false, thread,
|
|
argument_register_ids, current_argument_register,
|
|
current_stack_argument);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Status ABISysV_ppc64::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;
|
|
}
|
|
|
|
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()) {
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3", 0);
|
|
|
|
DataExtractor data;
|
|
Status 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) {
|
|
uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
|
|
|
|
if (reg_ctx->WriteRegisterFromUnsigned(reg_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 {
|
|
std::optional<uint64_t> bit_width =
|
|
compiler_type.GetBitSize(frame_sp.get());
|
|
if (!bit_width) {
|
|
error.SetErrorString("can't get size of type");
|
|
return error;
|
|
}
|
|
if (*bit_width <= 64) {
|
|
DataExtractor data;
|
|
Status 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;
|
|
}
|
|
|
|
unsigned char buffer[16];
|
|
ByteOrder byte_order = data.GetByteOrder();
|
|
|
|
data.CopyByteOrderedData(0, num_bytes, buffer, 16, byte_order);
|
|
set_it_simple = true;
|
|
} else {
|
|
// FIXME - don't know how to do 80 bit long doubles yet.
|
|
error.SetErrorString(
|
|
"We don't support returning float values > 64 bits at present");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!set_it_simple) {
|
|
// Okay we've got a structure or something that doesn't fit in a simple
|
|
// register. We should figure out where it really goes, but we don't
|
|
// support this yet.
|
|
error.SetErrorString("We only support setting simple integer and float "
|
|
"return types at present.");
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
//
|
|
// ReturnValueExtractor
|
|
//
|
|
|
|
namespace {
|
|
|
|
#define LOG_PREFIX "ReturnValueExtractor: "
|
|
|
|
class ReturnValueExtractor {
|
|
// This class represents a register, from which data may be extracted.
|
|
//
|
|
// It may be constructed by directly specifying its index (where 0 is the
|
|
// first register used to return values) or by specifying the offset of a
|
|
// given struct field, in which case the appropriated register index will be
|
|
// calculated.
|
|
class Register {
|
|
public:
|
|
enum Type {
|
|
GPR, // General Purpose Register
|
|
FPR // Floating Point Register
|
|
};
|
|
|
|
// main constructor
|
|
//
|
|
// offs - field offset in struct
|
|
Register(Type ty, uint32_t index, uint32_t offs, RegisterContext *reg_ctx,
|
|
ByteOrder byte_order)
|
|
: m_index(index), m_offs(offs % sizeof(uint64_t)),
|
|
m_avail(sizeof(uint64_t) - m_offs), m_type(ty), m_reg_ctx(reg_ctx),
|
|
m_byte_order(byte_order) {}
|
|
|
|
// explicit index, no offset
|
|
Register(Type ty, uint32_t index, RegisterContext *reg_ctx,
|
|
ByteOrder byte_order)
|
|
: Register(ty, index, 0, reg_ctx, byte_order) {}
|
|
|
|
// GPR, calculate index from offs
|
|
Register(uint32_t offs, RegisterContext *reg_ctx, ByteOrder byte_order)
|
|
: Register(GPR, offs / sizeof(uint64_t), offs, reg_ctx, byte_order) {}
|
|
|
|
uint32_t Index() const { return m_index; }
|
|
|
|
// register offset where data is located
|
|
uint32_t Offs() const { return m_offs; }
|
|
|
|
// available bytes in this register
|
|
uint32_t Avail() const { return m_avail; }
|
|
|
|
bool IsValid() const {
|
|
if (m_index > 7) {
|
|
LLDB_LOG(m_log, LOG_PREFIX
|
|
"No more than 8 registers should be used to return values");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string GetName() const {
|
|
if (m_type == GPR)
|
|
return ("r" + llvm::Twine(m_index + 3)).str();
|
|
else
|
|
return ("f" + llvm::Twine(m_index + 1)).str();
|
|
}
|
|
|
|
// get raw register data
|
|
bool GetRawData(uint64_t &raw_data) {
|
|
const RegisterInfo *reg_info =
|
|
m_reg_ctx->GetRegisterInfoByName(GetName());
|
|
if (!reg_info) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "Failed to get RegisterInfo");
|
|
return false;
|
|
}
|
|
|
|
RegisterValue reg_val;
|
|
if (!m_reg_ctx->ReadRegister(reg_info, reg_val)) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "ReadRegister() failed");
|
|
return false;
|
|
}
|
|
|
|
Status error;
|
|
uint32_t rc = reg_val.GetAsMemoryData(
|
|
*reg_info, &raw_data, sizeof(raw_data), m_byte_order, error);
|
|
if (rc != sizeof(raw_data)) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "GetAsMemoryData() failed");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
uint32_t m_index;
|
|
uint32_t m_offs;
|
|
uint32_t m_avail;
|
|
Type m_type;
|
|
RegisterContext *m_reg_ctx;
|
|
ByteOrder m_byte_order;
|
|
Log *m_log = GetLog(LLDBLog::Expressions);
|
|
};
|
|
|
|
Register GetGPR(uint32_t index) const {
|
|
return Register(Register::GPR, index, m_reg_ctx, m_byte_order);
|
|
}
|
|
|
|
Register GetFPR(uint32_t index) const {
|
|
return Register(Register::FPR, index, m_reg_ctx, m_byte_order);
|
|
}
|
|
|
|
Register GetGPRByOffs(uint32_t offs) const {
|
|
return Register(offs, m_reg_ctx, m_byte_order);
|
|
}
|
|
|
|
public:
|
|
// factory
|
|
static llvm::Expected<ReturnValueExtractor> Create(Thread &thread,
|
|
CompilerType &type) {
|
|
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
|
|
if (!reg_ctx)
|
|
return llvm::make_error<llvm::StringError>(
|
|
LOG_PREFIX "Failed to get RegisterContext",
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
ProcessSP process_sp = thread.GetProcess();
|
|
if (!process_sp)
|
|
return llvm::make_error<llvm::StringError>(
|
|
LOG_PREFIX "GetProcess() failed", llvm::inconvertibleErrorCode());
|
|
|
|
return ReturnValueExtractor(thread, type, reg_ctx, process_sp);
|
|
}
|
|
|
|
// main method: get value of the type specified at construction time
|
|
ValueObjectSP GetValue() {
|
|
const uint32_t type_flags = m_type.GetTypeInfo();
|
|
|
|
// call the appropriate type handler
|
|
ValueSP value_sp;
|
|
ValueObjectSP valobj_sp;
|
|
if (type_flags & eTypeIsScalar) {
|
|
if (type_flags & eTypeIsInteger) {
|
|
value_sp = GetIntegerValue(0);
|
|
} else if (type_flags & eTypeIsFloat) {
|
|
if (type_flags & eTypeIsComplex) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "Complex numbers are not supported yet");
|
|
return ValueObjectSP();
|
|
} else {
|
|
value_sp = GetFloatValue(m_type, 0);
|
|
}
|
|
}
|
|
} else if (type_flags & eTypeIsPointer) {
|
|
value_sp = GetPointerValue(0);
|
|
}
|
|
|
|
if (value_sp) {
|
|
valobj_sp = ValueObjectConstResult::Create(
|
|
m_thread.GetStackFrameAtIndex(0).get(), *value_sp, ConstString(""));
|
|
} else if (type_flags & eTypeIsVector) {
|
|
valobj_sp = GetVectorValueObject();
|
|
} else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass) {
|
|
valobj_sp = GetStructValueObject();
|
|
}
|
|
|
|
return valobj_sp;
|
|
}
|
|
|
|
private:
|
|
// data
|
|
Thread &m_thread;
|
|
CompilerType &m_type;
|
|
uint64_t m_byte_size;
|
|
std::unique_ptr<DataBufferHeap> m_data_up;
|
|
int32_t m_src_offs = 0;
|
|
int32_t m_dst_offs = 0;
|
|
bool m_packed = false;
|
|
Log *m_log = GetLog(LLDBLog::Expressions);
|
|
RegisterContext *m_reg_ctx;
|
|
ProcessSP m_process_sp;
|
|
ByteOrder m_byte_order;
|
|
uint32_t m_addr_size;
|
|
|
|
// methods
|
|
|
|
// constructor
|
|
ReturnValueExtractor(Thread &thread, CompilerType &type,
|
|
RegisterContext *reg_ctx, ProcessSP process_sp)
|
|
: m_thread(thread), m_type(type),
|
|
m_byte_size(m_type.GetByteSize(&thread).value_or(0)),
|
|
m_data_up(new DataBufferHeap(m_byte_size, 0)), m_reg_ctx(reg_ctx),
|
|
m_process_sp(process_sp), m_byte_order(process_sp->GetByteOrder()),
|
|
m_addr_size(
|
|
process_sp->GetTarget().GetArchitecture().GetAddressByteSize()) {}
|
|
|
|
// build a new scalar value
|
|
ValueSP NewScalarValue(CompilerType &type) {
|
|
ValueSP value_sp(new Value);
|
|
value_sp->SetCompilerType(type);
|
|
value_sp->SetValueType(Value::ValueType::Scalar);
|
|
return value_sp;
|
|
}
|
|
|
|
// get an integer value in the specified register
|
|
ValueSP GetIntegerValue(uint32_t reg_index) {
|
|
uint64_t raw_value;
|
|
auto reg = GetGPR(reg_index);
|
|
if (!reg.GetRawData(raw_value))
|
|
return ValueSP();
|
|
|
|
// build value from data
|
|
ValueSP value_sp(NewScalarValue(m_type));
|
|
|
|
uint32_t type_flags = m_type.GetTypeInfo();
|
|
bool is_signed = (type_flags & eTypeIsSigned) != 0;
|
|
|
|
switch (m_byte_size) {
|
|
case sizeof(uint64_t):
|
|
if (is_signed)
|
|
value_sp->GetScalar() = (int64_t)(raw_value);
|
|
else
|
|
value_sp->GetScalar() = (uint64_t)(raw_value);
|
|
break;
|
|
|
|
case sizeof(uint32_t):
|
|
if (is_signed)
|
|
value_sp->GetScalar() = (int32_t)(raw_value & UINT32_MAX);
|
|
else
|
|
value_sp->GetScalar() = (uint32_t)(raw_value & UINT32_MAX);
|
|
break;
|
|
|
|
case sizeof(uint16_t):
|
|
if (is_signed)
|
|
value_sp->GetScalar() = (int16_t)(raw_value & UINT16_MAX);
|
|
else
|
|
value_sp->GetScalar() = (uint16_t)(raw_value & UINT16_MAX);
|
|
break;
|
|
|
|
case sizeof(uint8_t):
|
|
if (is_signed)
|
|
value_sp->GetScalar() = (int8_t)(raw_value & UINT8_MAX);
|
|
else
|
|
value_sp->GetScalar() = (uint8_t)(raw_value & UINT8_MAX);
|
|
break;
|
|
|
|
default:
|
|
llvm_unreachable("Invalid integer size");
|
|
}
|
|
|
|
return value_sp;
|
|
}
|
|
|
|
// get a floating point value on the specified register
|
|
ValueSP GetFloatValue(CompilerType &type, uint32_t reg_index) {
|
|
uint64_t raw_data;
|
|
auto reg = GetFPR(reg_index);
|
|
if (!reg.GetRawData(raw_data))
|
|
return {};
|
|
|
|
// build value from data
|
|
ValueSP value_sp(NewScalarValue(type));
|
|
|
|
DataExtractor de(&raw_data, sizeof(raw_data), m_byte_order, m_addr_size);
|
|
|
|
offset_t offset = 0;
|
|
std::optional<uint64_t> byte_size = type.GetByteSize(m_process_sp.get());
|
|
if (!byte_size)
|
|
return {};
|
|
switch (*byte_size) {
|
|
case sizeof(float):
|
|
value_sp->GetScalar() = (float)de.GetDouble(&offset);
|
|
break;
|
|
|
|
case sizeof(double):
|
|
value_sp->GetScalar() = de.GetDouble(&offset);
|
|
break;
|
|
|
|
default:
|
|
llvm_unreachable("Invalid floating point size");
|
|
}
|
|
|
|
return value_sp;
|
|
}
|
|
|
|
// get pointer value from register
|
|
ValueSP GetPointerValue(uint32_t reg_index) {
|
|
uint64_t raw_data;
|
|
auto reg = GetGPR(reg_index);
|
|
if (!reg.GetRawData(raw_data))
|
|
return ValueSP();
|
|
|
|
// build value from raw data
|
|
ValueSP value_sp(NewScalarValue(m_type));
|
|
value_sp->GetScalar() = raw_data;
|
|
return value_sp;
|
|
}
|
|
|
|
// build the ValueObject from our data buffer
|
|
ValueObjectSP BuildValueObject() {
|
|
DataExtractor de(DataBufferSP(m_data_up.release()), m_byte_order,
|
|
m_addr_size);
|
|
return ValueObjectConstResult::Create(&m_thread, m_type, ConstString(""),
|
|
de);
|
|
}
|
|
|
|
// get a vector return value
|
|
ValueObjectSP GetVectorValueObject() {
|
|
const uint32_t MAX_VRS = 2;
|
|
|
|
// get first V register used to return values
|
|
const RegisterInfo *vr[MAX_VRS];
|
|
vr[0] = m_reg_ctx->GetRegisterInfoByName("vr2");
|
|
if (!vr[0]) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "Failed to get vr2 RegisterInfo");
|
|
return ValueObjectSP();
|
|
}
|
|
|
|
const uint32_t vr_size = vr[0]->byte_size;
|
|
size_t vrs = 1;
|
|
if (m_byte_size > 2 * vr_size) {
|
|
LLDB_LOG(
|
|
m_log, LOG_PREFIX
|
|
"Returning vectors that don't fit in 2 VR regs is not supported");
|
|
return ValueObjectSP();
|
|
}
|
|
|
|
// load vr3, if needed
|
|
if (m_byte_size > vr_size) {
|
|
vrs++;
|
|
vr[1] = m_reg_ctx->GetRegisterInfoByName("vr3");
|
|
if (!vr[1]) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "Failed to get vr3 RegisterInfo");
|
|
return ValueObjectSP();
|
|
}
|
|
}
|
|
|
|
// Get the whole contents of vector registers and let the logic here
|
|
// arrange the data properly.
|
|
|
|
RegisterValue vr_val[MAX_VRS];
|
|
Status error;
|
|
std::unique_ptr<DataBufferHeap> vr_data(
|
|
new DataBufferHeap(vrs * vr_size, 0));
|
|
|
|
for (uint32_t i = 0; i < vrs; i++) {
|
|
if (!m_reg_ctx->ReadRegister(vr[i], vr_val[i])) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "Failed to read vector register contents");
|
|
return ValueObjectSP();
|
|
}
|
|
if (!vr_val[i].GetAsMemoryData(*vr[i], vr_data->GetBytes() + i * vr_size,
|
|
vr_size, m_byte_order, error)) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "Failed to extract vector register bytes");
|
|
return ValueObjectSP();
|
|
}
|
|
}
|
|
|
|
// The compiler generated code seems to always put the vector elements at
|
|
// the end of the vector register, in case they don't occupy all of it.
|
|
// This offset variable handles this.
|
|
uint32_t offs = 0;
|
|
if (m_byte_size < vr_size)
|
|
offs = vr_size - m_byte_size;
|
|
|
|
// copy extracted data to our buffer
|
|
memcpy(m_data_up->GetBytes(), vr_data->GetBytes() + offs, m_byte_size);
|
|
return BuildValueObject();
|
|
}
|
|
|
|
// get a struct return value
|
|
ValueObjectSP GetStructValueObject() {
|
|
// case 1: get from stack
|
|
if (m_byte_size > 2 * sizeof(uint64_t)) {
|
|
uint64_t addr;
|
|
auto reg = GetGPR(0);
|
|
if (!reg.GetRawData(addr))
|
|
return {};
|
|
|
|
Status error;
|
|
size_t rc = m_process_sp->ReadMemory(addr, m_data_up->GetBytes(),
|
|
m_byte_size, error);
|
|
if (rc != m_byte_size) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "Failed to read memory pointed by r3");
|
|
return ValueObjectSP();
|
|
}
|
|
return BuildValueObject();
|
|
}
|
|
|
|
// get number of children
|
|
const bool omit_empty_base_classes = true;
|
|
auto n_or_err = m_type.GetNumChildren(omit_empty_base_classes, nullptr);
|
|
if (!n_or_err) {
|
|
LLDB_LOG_ERROR(m_log, n_or_err.takeError(), LOG_PREFIX "{0}");
|
|
return {};
|
|
}
|
|
uint32_t n = *n_or_err;
|
|
if (!n) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "No children found in struct");
|
|
return {};
|
|
}
|
|
|
|
// case 2: homogeneous double or float aggregate
|
|
CompilerType elem_type;
|
|
if (m_type.IsHomogeneousAggregate(&elem_type)) {
|
|
uint32_t type_flags = elem_type.GetTypeInfo();
|
|
std::optional<uint64_t> elem_size =
|
|
elem_type.GetByteSize(m_process_sp.get());
|
|
if (!elem_size)
|
|
return {};
|
|
if (type_flags & eTypeIsComplex || !(type_flags & eTypeIsFloat)) {
|
|
LLDB_LOG(m_log,
|
|
LOG_PREFIX "Unexpected type found in homogeneous aggregate");
|
|
return {};
|
|
}
|
|
|
|
for (uint32_t i = 0; i < n; i++) {
|
|
ValueSP val_sp = GetFloatValue(elem_type, i);
|
|
if (!val_sp)
|
|
return {};
|
|
|
|
// copy to buffer
|
|
Status error;
|
|
size_t rc = val_sp->GetScalar().GetAsMemoryData(
|
|
m_data_up->GetBytes() + m_dst_offs, *elem_size, m_byte_order,
|
|
error);
|
|
if (rc != *elem_size) {
|
|
LLDB_LOG(m_log, LOG_PREFIX "Failed to get float data");
|
|
return {};
|
|
}
|
|
m_dst_offs += *elem_size;
|
|
}
|
|
return BuildValueObject();
|
|
}
|
|
|
|
// case 3: get from GPRs
|
|
|
|
// first, check if this is a packed struct or not
|
|
auto ast = m_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>();
|
|
if (ast) {
|
|
clang::RecordDecl *record_decl = TypeSystemClang::GetAsRecordDecl(m_type);
|
|
|
|
if (record_decl) {
|
|
auto attrs = record_decl->attrs();
|
|
for (const auto &attr : attrs) {
|
|
if (attr->getKind() == clang::attr::Packed) {
|
|
m_packed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LLDB_LOG(m_log, LOG_PREFIX "{0} struct",
|
|
m_packed ? "packed" : "not packed");
|
|
|
|
for (uint32_t i = 0; i < n; i++) {
|
|
std::string name;
|
|
uint32_t size;
|
|
GetChildType(i, name, size);
|
|
// NOTE: the offset returned by GetChildCompilerTypeAtIndex()
|
|
// can't be used because it never considers alignment bytes
|
|
// between struct fields.
|
|
LLDB_LOG(m_log, LOG_PREFIX "field={0}, size={1}", name, size);
|
|
if (!ExtractField(size))
|
|
return ValueObjectSP();
|
|
}
|
|
|
|
return BuildValueObject();
|
|
}
|
|
|
|
// extract 'size' bytes at 'offs' from GPRs
|
|
bool ExtractFromRegs(int32_t offs, uint32_t size, void *buf) {
|
|
while (size) {
|
|
auto reg = GetGPRByOffs(offs);
|
|
if (!reg.IsValid())
|
|
return false;
|
|
|
|
uint32_t n = std::min(reg.Avail(), size);
|
|
uint64_t raw_data;
|
|
|
|
if (!reg.GetRawData(raw_data))
|
|
return false;
|
|
|
|
memcpy(buf, (char *)&raw_data + reg.Offs(), n);
|
|
offs += n;
|
|
size -= n;
|
|
buf = (char *)buf + n;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// extract one field from GPRs and put it in our buffer
|
|
bool ExtractField(uint32_t size) {
|
|
auto reg = GetGPRByOffs(m_src_offs);
|
|
if (!reg.IsValid())
|
|
return false;
|
|
|
|
// handle padding
|
|
if (!m_packed) {
|
|
uint32_t n = m_src_offs % size;
|
|
|
|
// not 'size' bytes aligned
|
|
if (n) {
|
|
LLDB_LOG(m_log,
|
|
LOG_PREFIX "Extracting {0} alignment bytes at offset {1}", n,
|
|
m_src_offs);
|
|
// get alignment bytes
|
|
if (!ExtractFromRegs(m_src_offs, n, m_data_up->GetBytes() + m_dst_offs))
|
|
return false;
|
|
m_src_offs += n;
|
|
m_dst_offs += n;
|
|
}
|
|
}
|
|
|
|
// get field
|
|
LLDB_LOG(m_log, LOG_PREFIX "Extracting {0} field bytes at offset {1}", size,
|
|
m_src_offs);
|
|
if (!ExtractFromRegs(m_src_offs, size, m_data_up->GetBytes() + m_dst_offs))
|
|
return false;
|
|
m_src_offs += size;
|
|
m_dst_offs += size;
|
|
return true;
|
|
}
|
|
|
|
// get child
|
|
CompilerType GetChildType(uint32_t i, std::string &name, uint32_t &size) {
|
|
// GetChild constant inputs
|
|
const bool transparent_pointers = false;
|
|
const bool omit_empty_base_classes = true;
|
|
const bool ignore_array_bounds = false;
|
|
// GetChild output params
|
|
int32_t child_offs;
|
|
uint32_t child_bitfield_bit_size;
|
|
uint32_t child_bitfield_bit_offset;
|
|
bool child_is_base_class;
|
|
bool child_is_deref_of_parent;
|
|
ValueObject *valobj = nullptr;
|
|
uint64_t language_flags;
|
|
ExecutionContext exe_ctx;
|
|
m_thread.CalculateExecutionContext(exe_ctx);
|
|
|
|
return m_type.GetChildCompilerTypeAtIndex(
|
|
&exe_ctx, i, transparent_pointers, omit_empty_base_classes,
|
|
ignore_array_bounds, name, size, child_offs, child_bitfield_bit_size,
|
|
child_bitfield_bit_offset, child_is_base_class,
|
|
child_is_deref_of_parent, valobj, language_flags);
|
|
}
|
|
};
|
|
|
|
#undef LOG_PREFIX
|
|
|
|
} // anonymous namespace
|
|
|
|
ValueObjectSP
|
|
ABISysV_ppc64::GetReturnValueObjectSimple(Thread &thread,
|
|
CompilerType &type) const {
|
|
if (!type)
|
|
return ValueObjectSP();
|
|
|
|
auto exp_extractor = ReturnValueExtractor::Create(thread, type);
|
|
if (!exp_extractor) {
|
|
Log *log = GetLog(LLDBLog::Expressions);
|
|
LLDB_LOG_ERROR(log, exp_extractor.takeError(),
|
|
"Extracting return value failed: {0}");
|
|
return ValueObjectSP();
|
|
}
|
|
|
|
return exp_extractor.get().GetValue();
|
|
}
|
|
|
|
ValueObjectSP ABISysV_ppc64::GetReturnValueObjectImpl(
|
|
Thread &thread, CompilerType &return_compiler_type) const {
|
|
return GetReturnValueObjectSimple(thread, return_compiler_type);
|
|
}
|
|
|
|
bool ABISysV_ppc64::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) {
|
|
unwind_plan.Clear();
|
|
unwind_plan.SetRegisterKind(eRegisterKindDWARF);
|
|
|
|
uint32_t lr_reg_num;
|
|
uint32_t sp_reg_num;
|
|
uint32_t pc_reg_num;
|
|
|
|
if (GetByteOrder() == lldb::eByteOrderLittle) {
|
|
lr_reg_num = ppc64le_dwarf::dwarf_lr_ppc64le;
|
|
sp_reg_num = ppc64le_dwarf::dwarf_r1_ppc64le;
|
|
pc_reg_num = ppc64le_dwarf::dwarf_pc_ppc64le;
|
|
} else {
|
|
lr_reg_num = ppc64_dwarf::dwarf_lr_ppc64;
|
|
sp_reg_num = ppc64_dwarf::dwarf_r1_ppc64;
|
|
pc_reg_num = ppc64_dwarf::dwarf_pc_ppc64;
|
|
}
|
|
|
|
UnwindPlan::RowSP row(new UnwindPlan::Row);
|
|
|
|
// Our Call Frame Address is the stack pointer value
|
|
row->GetCFAValue().SetIsRegisterPlusOffset(sp_reg_num, 0);
|
|
|
|
// The previous PC is in the LR
|
|
row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true);
|
|
unwind_plan.AppendRow(row);
|
|
|
|
// All other registers are the same.
|
|
|
|
unwind_plan.SetSourceName("ppc64 at-func-entry default");
|
|
unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ABISysV_ppc64::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) {
|
|
unwind_plan.Clear();
|
|
unwind_plan.SetRegisterKind(eRegisterKindDWARF);
|
|
|
|
uint32_t sp_reg_num;
|
|
uint32_t pc_reg_num;
|
|
uint32_t cr_reg_num;
|
|
|
|
if (GetByteOrder() == lldb::eByteOrderLittle) {
|
|
sp_reg_num = ppc64le_dwarf::dwarf_r1_ppc64le;
|
|
pc_reg_num = ppc64le_dwarf::dwarf_lr_ppc64le;
|
|
cr_reg_num = ppc64le_dwarf::dwarf_cr_ppc64le;
|
|
} else {
|
|
sp_reg_num = ppc64_dwarf::dwarf_r1_ppc64;
|
|
pc_reg_num = ppc64_dwarf::dwarf_lr_ppc64;
|
|
cr_reg_num = ppc64_dwarf::dwarf_cr_ppc64;
|
|
}
|
|
|
|
UnwindPlan::RowSP row(new UnwindPlan::Row);
|
|
const int32_t ptr_size = 8;
|
|
row->SetUnspecifiedRegistersAreUndefined(true);
|
|
row->GetCFAValue().SetIsRegisterDereferenced(sp_reg_num);
|
|
|
|
row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * 2, true);
|
|
row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true);
|
|
row->SetRegisterLocationToAtCFAPlusOffset(cr_reg_num, ptr_size, true);
|
|
|
|
unwind_plan.AppendRow(row);
|
|
unwind_plan.SetSourceName("ppc64 default unwind plan");
|
|
unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
|
|
unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
|
|
unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo);
|
|
unwind_plan.SetReturnAddressRegister(pc_reg_num);
|
|
return true;
|
|
}
|
|
|
|
bool ABISysV_ppc64::RegisterIsVolatile(const RegisterInfo *reg_info) {
|
|
return !RegisterIsCalleeSaved(reg_info);
|
|
}
|
|
|
|
// See "Register Usage" in the
|
|
// "System V Application Binary Interface"
|
|
// "64-bit PowerPC ELF Application Binary Interface Supplement" current version
|
|
// is 2 released 2015 at
|
|
// https://members.openpowerfoundation.org/document/dl/576
|
|
bool ABISysV_ppc64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
|
|
if (reg_info) {
|
|
// Preserved registers are :
|
|
// r1,r2,r13-r31
|
|
// cr2-cr4 (partially preserved)
|
|
// f14-f31 (not yet)
|
|
// v20-v31 (not yet)
|
|
// vrsave (not yet)
|
|
|
|
const char *name = reg_info->name;
|
|
if (name[0] == 'r') {
|
|
if ((name[1] == '1' || name[1] == '2') && name[2] == '\0')
|
|
return true;
|
|
if (name[1] == '1' && name[2] > '2')
|
|
return true;
|
|
if ((name[1] == '2' || name[1] == '3') && name[2] != '\0')
|
|
return true;
|
|
}
|
|
|
|
if (name[0] == 'f' && name[1] >= '0' && name[2] <= '9') {
|
|
if (name[2] == '\0')
|
|
return false;
|
|
if (name[1] == '1' && name[2] >= '4')
|
|
return true;
|
|
if ((name[1] == '2' || name[1] == '3') && name[2] != '\0')
|
|
return true;
|
|
}
|
|
|
|
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 false;
|
|
if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ABISysV_ppc64::Initialize() {
|
|
PluginManager::RegisterPlugin(
|
|
GetPluginNameStatic(), "System V ABI for ppc64 targets", CreateInstance);
|
|
}
|
|
|
|
void ABISysV_ppc64::Terminate() {
|
|
PluginManager::UnregisterPlugin(CreateInstance);
|
|
}
|