diff options
author | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2016-04-14 14:28:34 +0000 |
---|---|---|
committer | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2016-04-14 14:28:34 +0000 |
commit | bb00d0b6b29a2ab1c59d3040a21f14156d8779df (patch) | |
tree | cbe5c64120aa89abbfd4cfc9e6d54b958f0e85bd /lldb/source/Plugins/ABI | |
parent | 7311bb34f63df58edb4819e5b483b49932b371bd (diff) | |
download | bcm5719-llvm-bb00d0b6b29a2ab1c59d3040a21f14156d8779df.tar.gz bcm5719-llvm-bb00d0b6b29a2ab1c59d3040a21f14156d8779df.zip |
Support Linux on SystemZ as platform
This patch adds support for Linux on SystemZ:
- A new ArchSpec value of eCore_s390x_generic
- A new directory Plugins/ABI/SysV-s390x providing an ABI implementation
- Register context support
- Native Linux support including watchpoint support
- ELF core file support
- Misc. support throughout the code base (e.g. breakpoint opcodes)
- Test case updates to support the platform
This should provide complete support for debugging the SystemZ platform.
Not yet supported are optional features like transaction support (zEC12)
or SIMD vector support (z13).
There is no instruction emulation, since our ABI requires that all code
provide correct DWARF CFI at all PC locations in .eh_frame to support
unwinding (i.e. -fasynchronous-unwind-tables is on by default).
The implementation follows existing platforms in a mostly straightforward
manner. A couple of things that are different:
- We do not use PTRACE_PEEKUSER / PTRACE_POKEUSER to access single registers,
since some registers (access register) reside at offsets in the user area
that are multiples of 4, but the PTRACE_PEEKUSER interface only allows
accessing aligned 8-byte blocks in the user area. Instead, we use a s390
specific ptrace interface PTRACE_PEEKUSR_AREA / PTRACE_POKEUSR_AREA that
allows accessing a whole block of the user area in one go, so in effect
allowing to treat parts of the user area as register sets.
- SystemZ hardware does not provide any means to implement read watchpoints,
only write watchpoints. In fact, we can only support a *single* write
watchpoint (but this can span a range of arbitrary size). In LLDB this
means we support only a single watchpoint. I've set all test cases that
require read watchpoints (or multiple watchpoints) to expected failure
on the platform. [ Note that there were two test cases that install
a read/write watchpoint even though they nowhere rely on the "read"
property. I've changed those to simply use plain write watchpoints. ]
Differential Revision: http://reviews.llvm.org/D18978
llvm-svn: 266308
Diffstat (limited to 'lldb/source/Plugins/ABI')
-rw-r--r-- | lldb/source/Plugins/ABI/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp | 808 | ||||
-rw-r--r-- | lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h | 120 | ||||
-rw-r--r-- | lldb/source/Plugins/ABI/SysV-s390x/CMakeLists.txt | 3 |
4 files changed, 932 insertions, 0 deletions
diff --git a/lldb/source/Plugins/ABI/CMakeLists.txt b/lldb/source/Plugins/ABI/CMakeLists.txt index 08452c5dad6..9d7a79308d7 100644 --- a/lldb/source/Plugins/ABI/CMakeLists.txt +++ b/lldb/source/Plugins/ABI/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(SysV-ppc) add_subdirectory(SysV-ppc64) add_subdirectory(SysV-mips) add_subdirectory(SysV-mips64) +add_subdirectory(SysV-s390x) add_subdirectory(SysV-i386) add_subdirectory(SysV-x86_64) add_subdirectory(MacOSX-i386) diff --git a/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp b/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp new file mode 100644 index 00000000000..af4f45d547e --- /dev/null +++ b/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.cpp @@ -0,0 +1,808 @@ +//===-- ABISysV_s390x.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABISysV_s390x.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" + +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.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" + +using namespace lldb; +using namespace lldb_private; + +enum dwarf_regnums +{ + // General Purpose Registers + dwarf_r0_s390x = 0, + dwarf_r1_s390x, + dwarf_r2_s390x, + dwarf_r3_s390x, + dwarf_r4_s390x, + dwarf_r5_s390x, + dwarf_r6_s390x, + dwarf_r7_s390x, + dwarf_r8_s390x, + dwarf_r9_s390x, + dwarf_r10_s390x, + dwarf_r11_s390x, + dwarf_r12_s390x, + dwarf_r13_s390x, + dwarf_r14_s390x, + dwarf_r15_s390x, + // Floating Point Registers / Vector Registers 0-15 + dwarf_f0_s390x = 16, + dwarf_f2_s390x, + dwarf_f4_s390x, + dwarf_f6_s390x, + dwarf_f1_s390x, + dwarf_f3_s390x, + dwarf_f5_s390x, + dwarf_f7_s390x, + dwarf_f8_s390x, + dwarf_f10_s390x, + dwarf_f12_s390x, + dwarf_f14_s390x, + dwarf_f9_s390x, + dwarf_f11_s390x, + dwarf_f13_s390x, + dwarf_f15_s390x, + // Access Registers + dwarf_acr0_s390x = 48, + dwarf_acr1_s390x, + dwarf_acr2_s390x, + dwarf_acr3_s390x, + dwarf_acr4_s390x, + dwarf_acr5_s390x, + dwarf_acr6_s390x, + dwarf_acr7_s390x, + dwarf_acr8_s390x, + dwarf_acr9_s390x, + dwarf_acr10_s390x, + dwarf_acr11_s390x, + dwarf_acr12_s390x, + dwarf_acr13_s390x, + dwarf_acr14_s390x, + dwarf_acr15_s390x, + // Program Status Word + dwarf_pswm_s390x = 64, + dwarf_pswa_s390x, + // Vector Registers 16-31 + dwarf_v16_s390x = 68, + dwarf_v18_s390x, + dwarf_v20_s390x, + dwarf_v22_s390x, + dwarf_v17_s390x, + dwarf_v19_s390x, + dwarf_v21_s390x, + dwarf_v23_s390x, + dwarf_v24_s390x, + dwarf_v26_s390x, + dwarf_v28_s390x, + dwarf_v30_s390x, + dwarf_v25_s390x, + dwarf_v27_s390x, + dwarf_v29_s390x, + dwarf_v31_s390x, +}; + +// RegisterKind: EHFrame, DWARF, Generic, Process Plugin, LLDB + +#define DEFINE_REG(name, size, alt, generic) \ + { \ + #name, alt, size, 0, eEncodingUint, eFormatHex, \ + { dwarf_##name##_s390x, dwarf_##name##_s390x, generic, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, \ + NULL, NULL, \ + } + +static RegisterInfo g_register_infos[] = +{ + DEFINE_REG(r0, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r1, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r2, 8, "arg1", LLDB_REGNUM_GENERIC_ARG1), + DEFINE_REG(r3, 8, "arg2", LLDB_REGNUM_GENERIC_ARG2), + DEFINE_REG(r4, 8, "arg3", LLDB_REGNUM_GENERIC_ARG3), + DEFINE_REG(r5, 8, "arg4", LLDB_REGNUM_GENERIC_ARG4), + DEFINE_REG(r6, 8, "arg5", LLDB_REGNUM_GENERIC_ARG5), + DEFINE_REG(r7, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r8, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r9, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r10, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r11, 8, "fp", LLDB_REGNUM_GENERIC_FP), + DEFINE_REG(r12, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r13, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r14, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(r15, 8, "sp", LLDB_REGNUM_GENERIC_SP), + DEFINE_REG(acr0, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr1, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr2, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr3, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr4, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr5, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr6, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr7, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr8, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr9, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr10, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr11, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr12, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr13, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr14, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(acr15, 4, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(pswm, 8, "flags", LLDB_REGNUM_GENERIC_FLAGS), + DEFINE_REG(pswa, 8, "pc", LLDB_REGNUM_GENERIC_PC), + DEFINE_REG(f0, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f1, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f2, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f3, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f4, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f5, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f6, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f7, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f8, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f9, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f10, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f11, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f12, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f13, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f14, 8, nullptr, LLDB_INVALID_REGNUM), + DEFINE_REG(f15, 8, nullptr, LLDB_INVALID_REGNUM), +}; + +static const uint32_t k_num_register_infos = llvm::array_lengthof(g_register_infos); +static bool g_register_info_names_constified = false; + +const lldb_private::RegisterInfo * +ABISysV_s390x::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 +ABISysV_s390x::GetRedZoneSize() const +{ + return 0; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ + +ABISP +ABISysV_s390x::CreateInstance(const ArchSpec &arch) +{ + static ABISP g_abi_sp; + if (arch.GetTriple().getArch() == llvm::Triple::systemz) + { + if (!g_abi_sp) + g_abi_sp.reset(new ABISysV_s390x); + return g_abi_sp; + } + return ABISP(); +} + +bool +ABISysV_s390x::PrepareTrivialCall(Thread &thread, addr_t sp, addr_t func_addr, addr_t return_addr, + llvm::ArrayRef<addr_t> args) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + StreamString s; + s.Printf("ABISysV_s390x::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->PutCString(s.GetString().c_str()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + 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); + const RegisterInfo *ra_reg_info = reg_ctx->GetRegisterInfoByName("r14", 0); + ProcessSP process_sp(thread.GetProcess()); + + // Allocate a new stack frame and space for stack arguments if necessary + + addr_t arg_pos = 0; + if (args.size() > 5) + { + sp -= 8 * (args.size() - 5); + arg_pos = sp; + } + + sp -= 160; + + // Process arguments + + for (size_t i = 0; i < args.size(); ++i) + { + if (i < 5) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i); + if (log) + log->Printf("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; + } + else + { + Error error; + if (log) + log->Printf("About to write arg%" PRIu64 " (0x%" PRIx64 ") onto stack", static_cast<uint64_t>(i + 1), + args[i]); + if (!process_sp->WritePointerToMemory(arg_pos, args[i], error)) + return false; + arg_pos += 8; + } + } + + // %r14 is set to the return address + + if (log) + log->Printf("Writing RA: 0x%" PRIx64, (uint64_t)return_addr); + + if (!reg_ctx->WriteRegisterFromUnsigned(ra_reg_info, return_addr)) + return false; + + // %r15 is set to the actual stack value. + + if (log) + log->Printf("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. + + if (log) + log->Printf("Writing PC: 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 < 5) + { + 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; + Error error; + if (thread.GetProcess()->ReadScalarIntegerFromMemory(current_stack_argument + 8 - byte_size, byte_size, + is_signed, scalar, error)) + { + current_stack_argument += 8; + return true; + } + return false; + } + return true; +} + +bool +ABISysV_s390x::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; + + addr_t current_stack_argument = sp; + + uint32_t argument_register_ids[5]; + + argument_register_ids[0] = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)->kinds[eRegisterKindLLDB]; + argument_register_ids[1] = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)->kinds[eRegisterKindLLDB]; + argument_register_ids[2] = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)->kinds[eRegisterKindLLDB]; + argument_register_ids[3] = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)->kinds[eRegisterKindLLDB]; + argument_register_ids[4] = + reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)->kinds[eRegisterKindLLDB]; + + unsigned int current_argument_register = 0; + + return false; + 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(); + if (!compiler_type) + return false; + bool is_signed; + + if (compiler_type.IsIntegerType(is_signed)) + { + ReadIntegerArgument(value->GetScalar(), compiler_type.GetBitSize(&thread), is_signed, thread, + argument_register_ids, current_argument_register, current_stack_argument); + } + else if (compiler_type.IsPointerType()) + { + ReadIntegerArgument(value->GetScalar(), compiler_type.GetBitSize(&thread), false, thread, + argument_register_ids, current_argument_register, current_stack_argument); + } + } + + return true; +} + +Error +ABISysV_s390x::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; + } + + 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.IsIntegerType(is_signed) || compiler_type.IsPointerType()) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r2", 0); + + DataExtractor data; + Error 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 + { + size_t bit_width = compiler_type.GetBitSize(frame_sp.get()); + if (bit_width <= 64) + { + const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0); + RegisterValue f0_value; + DataExtractor data; + Error 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[8]; + ByteOrder byte_order = data.GetByteOrder(); + + data.CopyByteOrderedData(0, num_bytes, buffer, 8, byte_order); + f0_value.SetBytes(buffer, 8, byte_order); + reg_ctx->WriteRegister(f0_info, f0_value); + set_it_simple = true; + } + else + { + // FIXME - don't know how to do 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; +} + +ValueObjectSP +ABISysV_s390x::GetReturnValueObjectSimple(Thread &thread, CompilerType &return_compiler_type) const +{ + ValueObjectSP return_valobj_sp; + Value value; + + if (!return_compiler_type) + return return_valobj_sp; + + // value.SetContext (Value::eContextTypeClangType, return_value_type); + 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(); + if (type_flags & eTypeIsScalar) + { + value.SetValueType(Value::eValueTypeScalar); + + bool success = false; + if (type_flags & eTypeIsInteger) + { + // Extract the register context so we can read arguments from registers + + const size_t byte_size = return_compiler_type.GetByteSize(nullptr); + uint64_t raw_value = + thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("r2", 0), 0); + const bool is_signed = (type_flags & eTypeIsSigned) != 0; + switch (byte_size) + { + default: + break; + + case sizeof(uint64_t): + if (is_signed) + value.GetScalar() = (int64_t)(raw_value); + else + value.GetScalar() = (uint64_t)(raw_value); + success = true; + break; + + case sizeof(uint32_t): + if (is_signed) + value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); + else + value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); + success = true; + break; + + case sizeof(uint16_t): + if (is_signed) + value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); + else + value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); + success = true; + break; + + case sizeof(uint8_t): + if (is_signed) + value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); + else + value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); + success = true; + break; + } + } + else if (type_flags & eTypeIsFloat) + { + if (type_flags & eTypeIsComplex) + { + // Don't handle complex yet. + } + else + { + const size_t byte_size = return_compiler_type.GetByteSize(nullptr); + if (byte_size <= sizeof(long double)) + { + const RegisterInfo *f0_info = reg_ctx->GetRegisterInfoByName("f0", 0); + RegisterValue f0_value; + if (reg_ctx->ReadRegister(f0_info, f0_value)) + { + DataExtractor data; + if (f0_value.GetData(data)) + { + lldb::offset_t offset = 0; + if (byte_size == sizeof(float)) + { + value.GetScalar() = (float)data.GetFloat(&offset); + success = true; + } + else if (byte_size == sizeof(double)) + { + value.GetScalar() = (double)data.GetDouble(&offset); + success = true; + } + else if (byte_size == sizeof(long double)) + { + // Don't handle long double yet. + } + } + } + } + } + } + + if (success) + return_valobj_sp = + ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); + } + else if (type_flags & eTypeIsPointer) + { + unsigned r2_id = reg_ctx->GetRegisterInfoByName("r2", 0)->kinds[eRegisterKindLLDB]; + value.GetScalar() = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_id, 0); + value.SetValueType(Value::eValueTypeScalar); + return_valobj_sp = ValueObjectConstResult::Create(thread.GetStackFrameAtIndex(0).get(), value, ConstString("")); + } + + return return_valobj_sp; +} + +ValueObjectSP +ABISysV_s390x::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()) + { + // FIXME: This is just taking a guess, r2 may very well no longer hold the return storage location. + // If we are going to do this right, when we make a new frame we should check to see if it uses a memory + // return, and if we are at the first instruction and if so stash away the return location. Then we would + // only return the memory return value if we know it is valid. + + unsigned r2_id = reg_ctx_sp->GetRegisterInfoByName("r2", 0)->kinds[eRegisterKindLLDB]; + lldb::addr_t storage_addr = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r2_id, 0); + return_valobj_sp = ValueObjectMemory::Create(&thread, "", Address(storage_addr, nullptr), return_compiler_type); + } + + return return_valobj_sp; +} + +bool +ABISysV_s390x::CreateFunctionEntryUnwindPlan(UnwindPlan &unwind_plan) +{ + unwind_plan.Clear(); + unwind_plan.SetRegisterKind(eRegisterKindDWARF); + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our Call Frame Address is the stack pointer value + 160 + row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_r15_s390x, 160); + + // The previous PC is in r14 + row->SetRegisterLocationToRegister(dwarf_pswa_s390x, dwarf_r14_s390x, true); + + // All other registers are the same. + unwind_plan.AppendRow(row); + unwind_plan.SetSourceName("s390x at-func-entry default"); + unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); + return true; +} + +bool +ABISysV_s390x::CreateDefaultUnwindPlan(UnwindPlan &unwind_plan) +{ + // There's really no default way to unwind on s390x. + // Trust the .eh_frame CFI, which should always be good. + return false; +} + +bool +ABISysV_s390x::GetFallbackRegisterLocation (const RegisterInfo *reg_info, + UnwindPlan::Row::RegisterLocation &unwind_regloc) +{ + // If a volatile register is being requested, we don't want to forward the next frame's register contents + // up the stack -- the register is not retrievable at this frame. + if (RegisterIsVolatile(reg_info)) + { + unwind_regloc.SetUndefined(); + return true; + } + + return false; +} + +bool +ABISysV_s390x::RegisterIsVolatile(const RegisterInfo *reg_info) +{ + return !RegisterIsCalleeSaved(reg_info); +} + +bool +ABISysV_s390x::RegisterIsCalleeSaved(const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Preserved registers are : + // r6-r13, r15 + // f8-f15 + + const char *name = reg_info->name; + if (name[0] == 'r') + { + switch (name[1]) + { + case '6': // r6 + case '7': // r7 + case '8': // r8 + case '9': // r9 + return name[2] == '\0'; + + case '1': // r10, r11, r12, r13, r15 + if ((name[2] >= '0' && name[2] <= '3') || name[2] == '5') + return name[3] == '\0'; + break; + + default: + break; + } + } + if (name[0] == 'f') + { + switch (name[1]) + { + case '8': // r8 + case '9': // r9 + return name[2] == '\0'; + + case '1': // r10, r11, r12, r13, r14, r15 + if (name[2] >= '0' && name[2] <= '5') + return name[3] == '\0'; + break; + + default: + break; + } + } + + // Accept shorter-variant versions + 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_s390x::Initialize() +{ + PluginManager::RegisterPlugin(GetPluginNameStatic(), "System V ABI for s390x targets", CreateInstance); +} + +void +ABISysV_s390x::Terminate() +{ + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb_private::ConstString +ABISysV_s390x::GetPluginNameStatic() +{ + static ConstString g_name("sysv-s390x"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ + +lldb_private::ConstString +ABISysV_s390x::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABISysV_s390x::GetPluginVersion() +{ + return 1; +} diff --git a/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h b/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h new file mode 100644 index 00000000000..3aba9c1917c --- /dev/null +++ b/lldb/source/Plugins/ABI/SysV-s390x/ABISysV_s390x.h @@ -0,0 +1,120 @@ +//===-- ABISysV_s390x.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABISysV_s390x_h_ +#define liblldb_ABISysV_s390x_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABISysV_s390x : public lldb_private::ABI +{ +public: + ~ABISysV_s390x() override = default; + + size_t + GetRedZoneSize() const override; + + bool + PrepareTrivialCall(lldb_private::Thread &thread, lldb::addr_t sp, lldb::addr_t functionAddress, + lldb::addr_t returnAddress, llvm::ArrayRef<lldb::addr_t> args) const override; + + bool + GetArgumentValues(lldb_private::Thread &thread, lldb_private::ValueList &values) const override; + + lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value) override; + + lldb::ValueObjectSP + GetReturnValueObjectImpl(lldb_private::Thread &thread, lldb_private::CompilerType &type) const override; + + bool + CreateFunctionEntryUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override; + + bool + CreateDefaultUnwindPlan(lldb_private::UnwindPlan &unwind_plan) override; + + bool + RegisterIsVolatile(const lldb_private::RegisterInfo *reg_info) override; + + bool + GetFallbackRegisterLocation (const lldb_private::RegisterInfo *reg_info, + lldb_private::UnwindPlan::Row::RegisterLocation &unwind_regloc) override; + + bool + CallFrameAddressIsValid(lldb::addr_t cfa) override + { + // Make sure the stack call frame addresses are 8 byte aligned + if (cfa & (8ull - 1ull)) + return false; // Not 8 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + bool + CodeAddressIsValid(lldb::addr_t pc) override + { + // Code addressed must be 2 byte aligned + if (pc & 1ull) + return false; + return true; + } + + const lldb_private::RegisterInfo * + GetRegisterInfoArray(uint32_t &count) override; + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance(const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + + lldb_private::ConstString + GetPluginName() override; + + uint32_t + GetPluginVersion() override; + +protected: + void + CreateRegisterMapIfNeeded(); + + lldb::ValueObjectSP + GetReturnValueObjectSimple(lldb_private::Thread &thread, lldb_private::CompilerType &ast_type) const; + + bool + RegisterIsCalleeSaved(const lldb_private::RegisterInfo *reg_info); + +private: + ABISysV_s390x() : lldb_private::ABI() + { + // Call CreateInstance instead. + } +}; + +#endif // liblldb_ABISysV_s390x_h_ diff --git a/lldb/source/Plugins/ABI/SysV-s390x/CMakeLists.txt b/lldb/source/Plugins/ABI/SysV-s390x/CMakeLists.txt new file mode 100644 index 00000000000..c3992db023e --- /dev/null +++ b/lldb/source/Plugins/ABI/SysV-s390x/CMakeLists.txt @@ -0,0 +1,3 @@ +add_lldb_library(lldbPluginABISysV_s390x + ABISysV_s390x.cpp + ) |